Skip to content

Commit 9af7ecc

Browse files
committed
add README.md to interfaces
1 parent 2eb3cfc commit 9af7ecc

File tree

9 files changed

+254
-79
lines changed

9 files changed

+254
-79
lines changed

.github/workflows/conda-package.yml

+5-3
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ jobs:
131131
- name: Install mkl_fft
132132
run: |
133133
CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}"
134-
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python }} $PACKAGE_NAME pytest scipy $CHANNELS
134+
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python }} "scipy>=1.10" $CHANNELS
135+
conda install -n ${{ env.TEST_ENV_NAME }} $PACKAGE_NAME pytest $CHANNELS
135136
# Test installed packages
136137
conda list -n ${{ env.TEST_ENV_NAME }}
137138
@@ -295,15 +296,16 @@ jobs:
295296
FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO (
296297
SET PACKAGE_VERSION=%%F
297298
)
298-
SET "TEST_DEPENDENCIES=pytest pytest-cov"
299-
conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} scipy -c ${{ env.workdir }}/channel ${{ env.CHANNELS }}
299+
SET "TEST_DEPENDENCIES=pytest scipy"
300+
conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }}
300301
301302
- name: Report content of test environment
302303
shell: cmd /C CALL {0}
303304
run: |
304305
echo "Value of CONDA environment variable was: " %CONDA%
305306
echo "Value of CONDA_PREFIX environment variable was: " %CONDA_PREFIX%
306307
conda info && conda list -n ${{ env.TEST_ENV_NAME }}
308+
307309
- name: Run tests
308310
shell: cmd /C CALL {0}
309311
run: >-

CHANGELOG.md

+3-19
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010
* Added Hermitian FFT functions to SciPy interface `mkl_fft.interfaces.scipy_fft`: `hfft`, `ihfft`, `hfftn`, `ihfftn`, `hfft2`, and `ihfft2` [gh-161](https://github.com/IntelPython/mkl_fft/pull/161)
1111
* Added support for `out` kwarg to all FFT functions in `mkl_fft` and `mkl_fft.interfaces.numpy_fft` [gh-157](https://github.com/IntelPython/mkl_fft/pull/157)
12+
* Added `fftfreq`, `fftshift`, `ifftshift`, and `rfftfreq` to both NumPy and SciPy interfaces [gh-179](https://github.com/IntelPython/mkl_fft/pull/179)
1213

1314
### Changed
14-
* NumPy interface `mkl_fft.interfaces.numpy_fft` is aligned with numpy-2.* [gh-139](https://github.com/IntelPython/mkl_fft/pull/139), [gh-157](https://github.com/IntelPython/mkl_fft/pull/157)
15+
* NumPy interface `mkl_fft.interfaces.numpy_fft` is aligned with numpy-2.x.x [gh-139](https://github.com/IntelPython/mkl_fft/pull/139), [gh-157](https://github.com/IntelPython/mkl_fft/pull/157)
16+
* To set `mkl_fft` as the backend for SciPy is only possible through `mkl_fft.interfaces.scipy_fft` [gh-179](https://github.com/IntelPython/mkl_fft/pull/179)
1517

1618
## [1.3.14] (04/10/2025)
1719

@@ -86,24 +88,6 @@ transform improves multi-core utilization which may offset the performance loss
8688

8789
Added `scipy.fft` backend, see #42. Fixed #46.
8890

89-
90-
```python
91-
>>> import numpy as np, mkl_fft, mkl_fft._scipy_fft as mkl_be, scipy, scipy.fft, mkl
92-
93-
>>> mkl.verbose(1)
94-
# True
95-
96-
>>> x = np.random.randn(8*7).reshape((7, 8))
97-
>>> with scipy.fft.set_backend(mkl_be, only=True):
98-
>>> ff = scipy.fft.fft2(x, workers=4)
99-
>>> ff2 = scipy.fft.fft2(x)
100-
# MKL_VERBOSE Intel(R) MKL 2020.0 Product build 20191102 for Intel(R) 64 architecture Intel(R) Advanced Vector Extensions 2 (Intel(R) AVX2) enabled processors, Lnx 2.40GHz intel_thread
101-
# MKL_VERBOSE FFT(drfo7:8:8x8:1:1,bScale:0.0178571,tLim:1,desc:0x5629ad31b800) 24.85ms CNR:OFF Dyn:1 FastMM:1 TID:0 NThr:16,FFT:4
102-
103-
>>> np.allclose(ff, ff2)
104-
# True
105-
```
106-
10791
## [1.0.15]
10892

10993
Changed tests to not compare against numpy fft, as this broke due to renaming of `np.fft.pocketfft` to

README.md

+21-10
Original file line numberDiff line numberDiff line change
@@ -43,34 +43,45 @@ This eliminates the need to copy input array contiguously into an intermediate b
4343

4444
`mkl_fft` directly supports N-dimensional Fourier transforms.
4545

46-
More details can be found in SciPy 2017 conference proceedings:
47-
https://github.com/scipy-conference/scipy_proceedings/tree/2017/papers/oleksandr_pavlyk
46+
More details can be found in [SciPy 2017 conference proceedings](https://github.com/scipy-conference/scipy_proceedings/tree/2017/papers/oleksandr_pavlyk).
4847

4948
---
5049

51-
`mkl_fft` implements the following functions:
50+
The `mkl_fft` package offers interfaces that act as drop-in replacements for equivalent functions in NumPy and SciPy. Learn more about these interfaces [here](https://github.com/IntelPython/mkl_fft/blob/master/mkl_fft/interfaces/README.md).
51+
52+
While using these interfaces is the easiest way to leverage `mk_fft`, one can also use `mkl_fft` directly with the following FFT functions:
5253

5354
### complex-to-complex (c2c) transforms:
5455

55-
`fft(x, n=None, axis=-1, overwrite_x=False, fwd_scale=1.0, out=out)` - 1D FFT, similar to `scipy.fft.fft`
56+
`fft(x, n=None, axis=-1, overwrite_x=False, fwd_scale=1.0, out=None)` - 1D FFT, similar to `scipy.fft.fft`
5657

57-
`fft2(x, s=None, axes=(-2, -1), overwrite_x=False, fwd_scale=1.0, out=out)` - 2D FFT, similar to `scipy.fft.fft2`
58+
`fft2(x, s=None, axes=(-2, -1), overwrite_x=False, fwd_scale=1.0, out=None)` - 2D FFT, similar to `scipy.fft.fft2`
5859

59-
`fftn(x, s=None, axes=None, overwrite_x=False, fwd_scale=1.0, out=out)` - ND FFT, similar to `scipy.fft.fftn`
60+
`fftn(x, s=None, axes=None, overwrite_x=False, fwd_scale=1.0, out=None)` - ND FFT, similar to `scipy.fft.fftn`
6061

6162
and similar inverse FFT (`ifft*`) functions.
6263

6364
### real-to-complex (r2c) and complex-to-real (c2r) transforms:
6465

65-
`rfft(x, n=None, axis=-1, fwd_scale=1.0, out=out)` - r2c 1D FFT, similar to `numpy.fft.rfft`
66+
`rfft(x, n=None, axis=-1, fwd_scale=1.0, out=None)` - r2c 1D FFT, similar to `numpy.fft.rfft`
6667

67-
`rfft2(x, s=None, axes=(-2, -1), fwd_scale=1.0, out=out)` - r2c 2D FFT, similar to `numpy.fft.rfft2`
68+
`rfft2(x, s=None, axes=(-2, -1), fwd_scale=1.0, out=None)` - r2c 2D FFT, similar to `numpy.fft.rfft2`
6869

69-
`rfftn(x, s=None, axes=None, fwd_scale=1.0, out=out)` - r2c ND FFT, similar to `numpy.fft.rfftn`
70+
`rfftn(x, s=None, axes=None, fwd_scale=1.0, out=None)` - r2c ND FFT, similar to `numpy.fft.rfftn`
7071

7172
and similar inverse c2r FFT (`irfft*`) functions.
7273

73-
The package also provides `mkl_fft.interfaces.numpy_fft` and `mkl_fft.interfaces.scipy_fft` interfaces which provide drop-in replacements for equivalent functions in NumPy and SciPy, respectively.
74+
The following example shows how to use `mkl_fft` for calculating a 1D FFT.
75+
76+
```python
77+
import numpy, mkl_fft
78+
a = numpy.random.randn(10) + 1j*numpy.random.randn(10)
79+
80+
mkl_res = mkl_fft.fft(a)
81+
np_res = numpy.fft.fft(a)
82+
numpy.allclose(mkl_res, np_res)
83+
# True
84+
```
7485

7586
---
7687

mkl_fft/_pydfti.pyx

+4-4
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def _tls_dfti_cache_capsule():
8989
cdef extern from "Python.h":
9090
ctypedef int size_t
9191

92-
long PyInt_AsLong(object ob)
92+
long PyLong_AsLong(object ob)
9393
int PyObject_HasAttrString(object, char*)
9494

9595

@@ -262,7 +262,7 @@ cdef cnp.ndarray _process_arguments(
262262
xnd[0] = cnp.PyArray_NDIM(x_arr) # tensor-rank of the array
263263

264264
err = 0
265-
axis_[0] = PyInt_AsLong(axis)
265+
axis_[0] = PyLong_AsLong(axis)
266266
if (axis_[0] == -1 and PyErr_Occurred()):
267267
PyErr_Clear()
268268
err = 1
@@ -278,7 +278,7 @@ cdef cnp.ndarray _process_arguments(
278278
n_[0] = x_arr.shape[axis_[0]]
279279
else:
280280
try:
281-
n_[0] = PyInt_AsLong(n)
281+
n_[0] = PyLong_AsLong(n)
282282
except:
283283
err = 1
284284

@@ -334,7 +334,7 @@ cdef int _is_integral(object num):
334334
if num is None:
335335
return 0
336336
try:
337-
n = PyInt_AsLong(num)
337+
n = PyLong_AsLong(num)
338338
_integral = 1 if n > 0 else 0
339339
except:
340340
_integral = 0

mkl_fft/interfaces/README.md

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Interfaces
2+
The `mkl_fft` package provides interfaces that serve as drop-in replacements for equivalent functions in NumPy and SciPy.
3+
4+
---
5+
6+
## NumPy interface - `mkl_fft.interfaces.numpy_fft`
7+
8+
This interface is a drop-in replacement for the [`numpy.fft`](https://numpy.org/devdocs/reference/routines.fft.html) module and includes **all** the functions available there:
9+
10+
* complex-to-complex FFTs: `fft`, `ifft`, `fft2`, `ifft2`, `fftn`, `ifftn`.
11+
12+
* real-to-complex and complex-to-real FFTs: `rfft`, `irfft`, `rfft2`, `irfft2`, `rfftn`, `irfftn`.
13+
14+
* Hermitian FFTs: `hfft`, `ihfft`.
15+
16+
* Helper routines: `fftfreq`, `rfftfreq`, `fftshift`, `ifftshift`. These routines serve as a fallback to the NumPy implementation and are included for completeness.
17+
18+
The following example shows how to use this interface for calculating a 1D FFT.
19+
20+
```python
21+
import numpy
22+
import mkl_fft.interfaces.numpy_fft as numpy_fft
23+
24+
a = numpy.random.randn(10) + 1j*numpy.random.randn(10)
25+
26+
mkl_res = numpy_fft.fft(a)
27+
np_res = numpy.fft.fft(a)
28+
numpy.allclose(mkl_res, np_res)
29+
# True
30+
```
31+
32+
---
33+
34+
## SciPy interface - `mkl_fft.interfaces.scipy_fft`
35+
This interface is a drop-in replacement for the [`scipy.fft`](https://scipy.github.io/devdocs/reference/fft.html) module and includes **some** of the functions available there:
36+
37+
* complex-to-complex FFTs: `fft`, `ifft`, `fft2`, `ifft2`, `fftn`, `ifftn`.
38+
39+
* real-to-complex and complex-to-real FFTs: `rfft`, `irfft`, `rfft2`, `irfft2`, `rfftn`, `irfftn`.
40+
41+
* Hermitian FFTs: `hfft`, `ihfft`, `hfft2`, `ihfft2`, `hfftn`, `ihfftn`.
42+
43+
* Helper functions: `fftshift`, `ifftshift`, `fftfreq`, `rfftfreq`, `set_workers`, `get_workers`. All of these functions, except for `set_workers` and `get_workers`, serve as a fallback to the SciPy implementation and are included for completeness.
44+
45+
The following example shows how to use this interface for calculating a 1D FFT.
46+
47+
```python
48+
import numpy, scipy
49+
import mkl_fft.interfaces.scipy_fft as scipy_fft
50+
51+
a = numpy.random.randn(10) + 1j * numpy.random.randn(10)
52+
53+
mkl_res = scipy_fft.fft(a)
54+
sp_res = scipy.fft.fft(a)
55+
numpy.allclose(mkl_res, sp_res)
56+
# True
57+
```
58+
59+
---
60+
61+
### Installing `mkl_fft` as the FFT backend for SciPy
62+
63+
`mkl_fft.interfaces.scipy_fft` supports use as a backend. The following example shows how to set `mkl_fft` as the FFT backend for SciPy.
64+
65+
```python
66+
import numpy, scipy, mkl
67+
import mkl_fft.interfaces.scipy_fft as mkl_backend
68+
x = numpy.random.randn(56).reshape(7, 8)
69+
70+
# turning on verbosity to show `mkl_fft` is used as the SciPy backend
71+
mkl.verbose(1)
72+
# True
73+
74+
with scipy.fft.set_backend(mkl_backend, only=True):
75+
mkl_res = scipy.fft.fft2(x, workers=4) # Calls `mkl_fft` backend
76+
# 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 2.00GHz intel_thread
77+
# MKL_VERBOSE FFT(drfo7:8:8x8:1:1,input_strides:{0,8,1},output_strides:{0,8,1},bScale:0.0178571,tLim:1,unaligned_output,desc:0x557affb60d40) 36.11us CNR:OFF Dyn:1 FastMM:1 TID:0 NThr:4
78+
79+
sp_res = scipy.fft.fft2(x, workers=4) # Calls default SciPy backend
80+
numpy.allclose(mkl_res, sp_res)
81+
# True
82+
```
83+
84+
The previous example was only for illustration purposes. In practice, there is no added benefit to defining a backend and calculating FFT, since this functionality is already accessible through the scipy interface, as shown earlier.
85+
To demonstrate the advantages of using `mkl_fft` as a backend, the following example compares the timing of `scipy.signal.fftconvolve` using the default SciPy backend versus the `mkl_fft` backend on an Intel® Xeon® CPU.
86+
87+
```python
88+
import numpy, scipy
89+
import mkl_fft.interfaces.scipy_fft as mkl_backend
90+
import timeit
91+
shape = (4096, 2048)
92+
a = numpy.random.randn(*shape) + 1j*numpy.random.randn(*shape)
93+
b = numpy.random.randn(*shape) + 1j*numpy.random.randn(*shape)
94+
95+
t1 = timeit.timeit(lambda: scipy.signal.fftconvolve(a, b), number=10)
96+
print(f"Time with scipy.fft default backend: {t1:.1f} seconds")
97+
# Time with scipy.fft default backend: 51.9 seconds
98+
99+
with scipy.fft.set_backend(mkl_backend, only=True):
100+
t2 = timeit.timeit(lambda: scipy.signal.fftconvolve(a, b), number=10)
101+
102+
print(f"Time with OneMKL FFT backend installed: {t2:.1f} seconds")
103+
# Time with MKL FFT backend installed: 9.1 seconds
104+
```

mkl_fft/interfaces/_scipy_fft.py

-38
Original file line numberDiff line numberDiff line change
@@ -63,47 +63,9 @@
6363
"ihfftn",
6464
"get_workers",
6565
"set_workers",
66-
"DftiBackend",
6766
]
6867

6968

70-
__doc__ = """
71-
This module implements interfaces mimicking `scipy.fft` module.
72-
73-
It also provides DftiBackend class which can be used to set mkl_fft to be used
74-
via `scipy.fft` namespace.
75-
76-
:Example:
77-
import scipy.fft
78-
import mkl_fft.interfaces._scipy_fft as mkl_be
79-
# Set mkl_fft to be used as backend of SciPy's FFT functions.
80-
scipy.fft.set_global_backend(mkl_be)
81-
"""
82-
83-
84-
__ua_domain__ = "numpy.scipy.fft"
85-
86-
87-
def __ua_function__(method, args, kwargs):
88-
"""Fetch registered UA function."""
89-
fn = globals().get(method.__name__, None)
90-
if fn is None:
91-
return NotImplemented
92-
return fn(*args, **kwargs)
93-
94-
95-
class DftiBackend:
96-
__ua_domain__ = "numpy.scipy.fft"
97-
98-
@staticmethod
99-
def __ua_function__(method, args, kwargs):
100-
"""Fetch registered UA function."""
101-
fn = globals().get(method.__name__, None)
102-
if fn is None:
103-
return NotImplemented
104-
return fn(*args, **kwargs)
105-
106-
10769
class _cpu_max_threads_count:
10870
def __init__(self):
10971
self.cpu_count = None

mkl_fft/interfaces/numpy_fft.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,44 @@
2424
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2525
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2626

27-
from ._numpy_fft import *
27+
# Added for completing the namespaces
28+
from numpy.fft import fftfreq, fftshift, ifftshift, rfftfreq
29+
30+
# pylint: disable=no-name-in-module
31+
from ._numpy_fft import (
32+
fft,
33+
fft2,
34+
fftn,
35+
hfft,
36+
ifft,
37+
ifft2,
38+
ifftn,
39+
ihfft,
40+
irfft,
41+
irfft2,
42+
irfftn,
43+
rfft,
44+
rfft2,
45+
rfftn,
46+
)
47+
48+
__all__ = [
49+
"fft",
50+
"ifft",
51+
"fft2",
52+
"ifft2",
53+
"fftn",
54+
"ifftn",
55+
"rfft",
56+
"irfft",
57+
"rfft2",
58+
"irfft2",
59+
"rfftn",
60+
"irfftn",
61+
"hfft",
62+
"ihfft",
63+
"fftshift",
64+
"ifftshift",
65+
"fftfreq",
66+
"rfftfreq",
67+
]

0 commit comments

Comments
 (0)