Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-with-clang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,5 @@ jobs:
- name: Run mkl_fft tests
run: |
source ${{ env.ONEAPI_ROOT }}/setvars.sh
pip install scipy mkl-service pytest
pip install pytest mkl-service scipy dask
pytest -s -v --pyargs mkl_fft
4 changes: 2 additions & 2 deletions .github/workflows/conda-package-cf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ jobs:
- name: Install mkl_fft
run: |
CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}"
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python_ver }} ${{ matrix.numpy }} $PACKAGE_NAME pytest scipy $CHANNELS
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python_ver }} ${{ matrix.numpy }} $PACKAGE_NAME pytest scipy dask $CHANNELS
# Test installed packages
conda list -n ${{ env.TEST_ENV_NAME }}

Expand Down Expand Up @@ -318,7 +318,7 @@ jobs:
FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO (
SET PACKAGE_VERSION=%%F
)
SET "TEST_DEPENDENCIES=pytest scipy"
SET "TEST_DEPENDENCIES=pytest scipy dask"
conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} ${{ matrix.numpy }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }}

- name: Report content of test environment
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/conda-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ jobs:
- name: Install mkl_fft
run: |
CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}"
conda create -n ${{ env.TEST_ENV_NAME }} $PACKAGE_NAME=${{ env.PACKAGE_VERSION }} python=${{ matrix.python }} pytest $CHANNELS
conda create -n ${{ env.TEST_ENV_NAME }} $PACKAGE_NAME=${{ env.PACKAGE_VERSION }} python=${{ matrix.python }} pytest dask $CHANNELS
if [[ "${{ matrix.python }}" != 3.9* ]]; then
# Intel channel only has scipy=1.10 for Python 3.9, which needs mkl<2025
# while scipy needs to install numpy and mkl_random and mkl_random-1.2.11 requires mkl>=2025
Expand Down Expand Up @@ -313,7 +313,7 @@ jobs:
FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO (
SET PACKAGE_VERSION=%%F
)
SET "TEST_DEPENDENCIES=pytest"
SET "TEST_DEPENDENCIES=pytest dask"
conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }}
if ("${{ matrix.python }}" -ne "3.9") {
conda install -n ${{ env.TEST_ENV_NAME }} scipy -c ${{ env.workdir }}/channel ${{ env.CHANNELS }}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
* Enabled support of Python 3.13 [gh-164](https://github.com/IntelPython/mkl_fft/pull/164)
* Added a new interface for FFT module of Dask accessible through `mkl_fft.interfaces.dask_fft` [gh-214](https://github.com/IntelPython/mkl_fft/pull/214)

### Changed
* Replaced `fwd_scale` parameter with `norm` in `mkl_fft` [gh-189](https://github.com/IntelPython/mkl_fft/pull/189)
Expand Down
2 changes: 2 additions & 0 deletions conda-recipe-cf/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ test:
requires:
- pytest
- scipy >=1.10
- dask
imports:
- mkl_fft
- mkl_fft.interfaces
- mkl_fft.interfaces.numpy_fft
- mkl_fft.interfaces.scipy_fft
- mkl_fft.interfaces.dask_fft

about:
home: http://github.com/IntelPython/mkl_fft
Expand Down
2 changes: 2 additions & 0 deletions conda-recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ test:
requires:
- pytest
- scipy >=1.10
- dask
imports:
- mkl_fft
- mkl_fft.interfaces
- mkl_fft.interfaces.numpy_fft
- mkl_fft.interfaces.scipy_fft
- mkl_fft.interfaces.dask_fft

about:
home: http://github.com/IntelPython/mkl_fft
Expand Down
42 changes: 41 additions & 1 deletion mkl_fft/interfaces/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Interfaces
The `mkl_fft` package provides interfaces that serve as drop-in replacements for equivalent functions in NumPy and SciPy.
The `mkl_fft` package provides interfaces that serve as drop-in replacements for equivalent functions in NumPy, SciPy, and Dask.

---

Expand Down Expand Up @@ -124,3 +124,43 @@ with mkl_fft.set_workers(4):
y = scipy.signal.fftconvolve(a, a) # Note that Nthr:4
# MKL_VERBOSE FFT(dcbo256x128,input_strides:{0,128,1},output_strides:{0,128,1},bScale:3.05176e-05,tLim:4,unaligned_output,desc:0x563aefe86180) 187.37us CNR:OFF Dyn:1 FastMM:1 TID:0 NThr:4
```

---

## Dask interface - `mkl_fft.interfaces.dask_fft`

This interface is a drop-in replacement for the [`dask.fft`](https://dask.pydata.org/en/latest/array-api.html#fast-fourier-transforms) module and includes **all** the functions available there:

* complex-to-complex FFTs: `fft`, `ifft`, `fft2`, `ifft2`, `fftn`, `ifftn`.

* real-to-complex and complex-to-real FFTs: `rfft`, `irfft`, `rfft2`, `irfft2`, `rfftn`, `irfftn`.

* Hermitian FFTs: `hfft`, `ihfft`.

* Helper routines: `fft_wrap`, `fftfreq`, `rfftfreq`, `fftshift`, `ifftshift`. These routines serve as a fallback to the Dask implementation and are included for completeness.

The following example shows how to use this interface for calculating a 2D FFT.

```python
import numpy, dask
import mkl_fft.interfaces.dask_fft as dask_fft

a = numpy.random.randn(128, 64) + 1j*numpy.random.randn(128, 64)
x = dask.array.from_array(a, chunks=(64, 64))
lazy_res = dask_fft.fft(x)
mkl_res = lazy_res.compute()
np_res = numpy.fft.fft(a)
numpy.allclose(mkl_res, np_res)
# True

# There are two chunks in this example based on the size of input array (128, 64) and chunk size (64, 64)
# to confirm that MKL FFT is called twice, turn on verbosity
import mkl
mkl.verbose(1)
# True

mkl_res = lazy_res.compute() # MKL_VERBOSE FFT is shown twice below which means MKL FFT is called twice
# MKL_VERBOSE oneMKL 2024.0 Update 2 Patch 2 Product build 20240823 for Intel(R) 64 architecture Intel(R) Advanced Vector Extensions 512 (Intel(R) AVX-512) with support for INT8, BF16, FP16 (limited) instructions, and Intel(R) Advanced Matrix Extensions (Intel(R) AMX) with INT8 and BF16, Lnx 3.80GHz intel_thread
# MKL_VERBOSE FFT(dcfo64*64,input_strides:{0,1},output_strides:{0,1},input_distance:64,output_distance:64,bScale:0.015625,tLim:32,unaligned_input,desc:0x7fd000010e40) 432.84us CNR:OFF Dyn:1 FastMM:1 TID:0 NThr:112
# MKL_VERBOSE FFT(dcfo64*64,input_strides:{0,1},output_strides:{0,1},input_distance:64,output_distance:64,bScale:0.015625,tLim:32,unaligned_input,desc:0x7fd480011300) 499.00us CNR:OFF Dyn:1 FastMM:1 TID:0 NThr:112
```
11 changes: 10 additions & 1 deletion mkl_fft/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,17 @@
from . import numpy_fft

try:
import scipy.fft
# check to see if scipy is installed
import scipy
except ImportError:
pass
else:
from . import scipy_fft

try:
# check to see if dask is installed
import dask
except ImportError:
pass
else:
from . import dask_fft
67 changes: 67 additions & 0 deletions mkl_fft/interfaces/dask_fft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python
# Copyright (c) 2025, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from dask.array.fft import fft_wrap, fftfreq, fftshift, ifftshift, rfftfreq

from . import numpy_fft as _numpy_fft

__all__ = [
"fft",
"ifft",
"fft2",
"ifft2",
"fftn",
"ifftn",
"rfft",
"irfft",
"rfft2",
"irfft2",
"rfftn",
"irfftn",
"hfft",
"ihfft",
"fftshift",
"ifftshift",
"fftfreq",
"rfftfreq",
"fft_wrap",
]


fft = fft_wrap(_numpy_fft.fft)
ifft = fft_wrap(_numpy_fft.ifft)
fft2 = fft_wrap(_numpy_fft.fft2)
ifft2 = fft_wrap(_numpy_fft.ifft2)
fftn = fft_wrap(_numpy_fft.fftn)
ifftn = fft_wrap(_numpy_fft.ifftn)
rfft = fft_wrap(_numpy_fft.rfft)
irfft = fft_wrap(_numpy_fft.irfft)
rfft2 = fft_wrap(_numpy_fft.rfft2)
irfft2 = fft_wrap(_numpy_fft.irfft2)
rfftn = fft_wrap(_numpy_fft.rfftn)
irfftn = fft_wrap(_numpy_fft.irfftn)
hfft = fft_wrap(_numpy_fft.hfft)
ihfft = fft_wrap(_numpy_fft.ihfft)
12 changes: 12 additions & 0 deletions mkl_fft/tests/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@
except AttributeError:
scipy_fft = None

try:
dask_fft = mfi.dask_fft
except AttributeError:
dask_fft = None

interfaces = []
ids = []
if scipy_fft is not None:
interfaces.append(scipy_fft)
ids.append("scipy")
if dask_fft is not None:
interfaces.append(dask_fft)
ids.append("dask")
interfaces.append(mfi.numpy_fft)
ids.append("numpy")

Expand Down Expand Up @@ -189,3 +197,7 @@ def test_axes(interface):
)
def test_interface_helper_functions(interface, func):
assert hasattr(interface, func)


def test_dask_fftwrap():
assert hasattr(mfi.dask_fft, "fft_wrap")
Loading
Loading