|
| 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 | +``` |
0 commit comments