From e8ead6f04da02f853ca89aa2f56e9d48b7842aa4 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Mon, 14 Apr 2025 22:40:42 -0700 Subject: [PATCH 1/2] include hfft --- CHANGES.rst | 8 +- mkl_fft/_fft_utils.py | 14 ++- mkl_fft/_numpy_fft.py | 18 +-- mkl_fft/_scipy_fft.py | 106 +++++++++++++++--- mkl_fft/tests/third_party/scipy/test_basic.py | 64 +++++------ .../third_party/scipy/test_multithreading.py | 7 +- 6 files changed, 150 insertions(+), 67 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9f28ac3..f7cd942 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,7 +2,13 @@ :code:`mkl_fft` changelog ========================= -1.3.14 (04/10/2025) +[dev] (MM/DD/YY) +================== + +scipy interface :code:`mkl_fft.interfaces.scipy_fft` now includes Hermitian FFT functions: +:code:`hfft`, :code:`ihfft`, :code:`hfftn`, :code:`ihfftn`, :code:`hfft2`, and :code:`ihfft2` + +1.3.14 (04/11/2025) =================== resolves gh-152 by adding an explicit :code:`mkl-service` dependency to :code:`mkl-fft` when building the wheel diff --git a/mkl_fft/_fft_utils.py b/mkl_fft/_fft_utils.py index 58c152b..e4e9189 100644 --- a/mkl_fft/_fft_utils.py +++ b/mkl_fft/_fft_utils.py @@ -26,7 +26,7 @@ import numpy as np -__all__ = ["_check_norm", "_compute_fwd_scale"] +__all__ = ["_check_norm", "_compute_fwd_scale", "_swap_direction"] def _check_norm(norm): @@ -49,3 +49,15 @@ def _compute_fwd_scale(norm, n, shape): return fsc else: # norm == "ortho" return np.sqrt(fsc) + + +def _swap_direction(norm): + _check_norm(norm) + _swap_direction_map = { + "backward": "forward", + None: "forward", + "ortho": "ortho", + "forward": "backward", + } + + return _swap_direction_map[norm] diff --git a/mkl_fft/_numpy_fft.py b/mkl_fft/_numpy_fft.py index 7836d99..de4d846 100644 --- a/mkl_fft/_numpy_fft.py +++ b/mkl_fft/_numpy_fft.py @@ -76,7 +76,7 @@ import numpy as np from . import _pydfti as mkl_fft # pylint: disable=no-name-in-module -from ._fft_utils import _check_norm, _compute_fwd_scale +from ._fft_utils import _compute_fwd_scale, _swap_direction from ._float_utils import _downcast_float128_array @@ -124,18 +124,6 @@ def _cook_nd_args(a, s=None, axes=None, invreal=False): return s, axes -def _swap_direction(norm): - _check_norm(norm) - _swap_direction_map = { - "backward": "forward", - None: "forward", - "ortho": "ortho", - "forward": "backward", - } - - return _swap_direction_map[norm] - - def trycall(func, args, kwrds): try: res = func(*args, **kwrds) @@ -604,7 +592,7 @@ def hfft(a, n=None, axis=-1, norm=None): norm = _swap_direction(norm) x = _downcast_float128_array(a) - x = np.array(x, copy=True, dtype=complex) + x = np.array(x, copy=True) np.conjugate(x, out=x) fsc = _compute_fwd_scale(norm, n, 2 * (x.shape[axis] - 1)) @@ -671,10 +659,8 @@ def ihfft(a, n=None, axis=-1, norm=None): """ - # The copy may be required for multithreading. norm = _swap_direction(norm) x = _downcast_float128_array(a) - x = np.array(x, copy=True, dtype=float) fsc = _compute_fwd_scale(norm, n, x.shape[axis]) output = trycall( diff --git a/mkl_fft/_scipy_fft.py b/mkl_fft/_scipy_fft.py index 18ab2d3..bfc2e2e 100644 --- a/mkl_fft/_scipy_fft.py +++ b/mkl_fft/_scipy_fft.py @@ -33,7 +33,7 @@ import numpy as np from . import _pydfti as mkl_fft # pylint: disable=no-name-in-module -from ._fft_utils import _compute_fwd_scale +from ._fft_utils import _compute_fwd_scale, _swap_direction from ._float_utils import _supported_array_or_not_implemented __doc__ = """ @@ -125,6 +125,12 @@ def set_workers(n_workers): "irfft2", "rfftn", "irfftn", + "hfft", + "ihfft", + "hfft2", + "ihfft2", + "hfftn", + "ihfftn", "get_workers", "set_workers", "DftiBackend", @@ -231,7 +237,7 @@ def _validate_input(a): def fft( - a, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, plan=None + a, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None ): _check_plan(plan) x = _validate_input(a) @@ -244,7 +250,7 @@ def fft( def ifft( - a, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, plan=None + a, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None ): _check_plan(plan) x = _validate_input(a) @@ -263,6 +269,7 @@ def fft2( norm=None, overwrite_x=False, workers=None, + *, plan=None, ): @@ -284,6 +291,7 @@ def ifft2( norm=None, overwrite_x=False, workers=None, + *, plan=None, ): @@ -299,7 +307,14 @@ def ifft2( def fftn( - a, s=None, axes=None, norm=None, overwrite_x=False, workers=None, plan=None + a, + s=None, + axes=None, + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, ): _check_plan(plan) x = _validate_input(a) @@ -312,7 +327,14 @@ def fftn( def ifftn( - a, s=None, axes=None, norm=None, overwrite_x=False, workers=None, plan=None + a, + s=None, + axes=None, + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, ): _check_plan(plan) x = _validate_input(a) @@ -324,7 +346,7 @@ def ifftn( ) -def rfft(a, n=None, axis=-1, norm=None, workers=None, plan=None): +def rfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): _check_plan(plan) x = _validate_input(a) fsc = _compute_fwd_scale(norm, n, x.shape[axis]) @@ -333,7 +355,7 @@ def rfft(a, n=None, axis=-1, norm=None, workers=None, plan=None): return mkl_fft.rfft(x, n=n, axis=axis, fwd_scale=fsc) -def irfft(a, n=None, axis=-1, norm=None, workers=None, plan=None): +def irfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): _check_plan(plan) x = _validate_input(a) fsc = _compute_fwd_scale(norm, n, 2 * (x.shape[axis] - 1)) @@ -342,17 +364,15 @@ def irfft(a, n=None, axis=-1, norm=None, workers=None, plan=None): return mkl_fft.irfft(x, n=n, axis=axis, fwd_scale=fsc) -def rfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, plan=None): - +def rfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): return rfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) -def irfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, plan=None): - +def irfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): return irfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) -def rfftn(a, s=None, axes=None, norm=None, workers=None, plan=None): +def rfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): _check_plan(plan) x = _validate_input(a) s, axes = _cook_nd_args(x, s, axes) @@ -362,11 +382,71 @@ def rfftn(a, s=None, axes=None, norm=None, workers=None, plan=None): return mkl_fft.rfftn(x, s, axes, fwd_scale=fsc) -def irfftn(a, s=None, axes=None, norm=None, workers=None, plan=None): +def irfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): + _check_plan(plan) + x = _validate_input(a) + s, axes = _cook_nd_args(x, s, axes, invreal=True) + fsc = _compute_fwd_scale(norm, s, x.shape) + + with Workers(workers): + return mkl_fft.irfftn(x, s, axes, fwd_scale=fsc) + + +def hfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): + _check_plan(plan) + x = _validate_input(a) + norm = _swap_direction(norm) + x = np.array(x, copy=True) + np.conjugate(x, out=x) + fsc = _compute_fwd_scale(norm, n, 2 * (x.shape[axis] - 1)) + + with Workers(workers): + return mkl_fft.irfft(x, n=n, axis=axis, fwd_scale=fsc) + + +def ihfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): + _check_plan(plan) + x = _validate_input(a) + norm = _swap_direction(norm) + fsc = _compute_fwd_scale(norm, n, x.shape[axis]) + + with Workers(workers): + result = mkl_fft.rfft(x, n=n, axis=axis, fwd_scale=fsc) + + np.conjugate(result, out=result) + return result + + +def hfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): + return hfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) + + +def ihfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): + return ihfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) + + +def hfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): _check_plan(plan) x = _validate_input(a) + norm = _swap_direction(norm) + x = np.array(x, copy=True) + np.conjugate(x, out=x) s, axes = _cook_nd_args(x, s, axes, invreal=True) fsc = _compute_fwd_scale(norm, s, x.shape) with Workers(workers): return mkl_fft.irfftn(x, s, axes, fwd_scale=fsc) + + +def ihfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): + _check_plan(plan) + x = _validate_input(a) + norm = _swap_direction(norm) + s, axes = _cook_nd_args(x, s, axes) + fsc = _compute_fwd_scale(norm, s, x.shape) + + with Workers(workers): + result = mkl_fft.rfftn(x, s, axes, fwd_scale=fsc) + + np.conjugate(result, out=result) + return result diff --git a/mkl_fft/tests/third_party/scipy/test_basic.py b/mkl_fft/tests/third_party/scipy/test_basic.py index 9e72097..470bbfd 100644 --- a/mkl_fft/tests/third_party/scipy/test_basic.py +++ b/mkl_fft/tests/third_party/scipy/test_basic.py @@ -1,9 +1,6 @@ # This file includes tests from scipy.fft module: # https://github.com/scipy/scipy/blob/main/scipy/fft/tests/test_basic.py -# TODO: remove when hfft* functions are added -# pylint: disable=no-member - import multiprocessing import queue import threading @@ -57,9 +54,9 @@ def get_expected_input_dtype(func, xp): fft.ifft, fft.ifftn, fft.ifft2, - # TODO: fft.hfft, - # TODO: fft.hfftn, - # TODO: fft.hfft2, + fft.hfft, + fft.hfftn, + fft.hfft2, fft.irfft, fft.irfftn, fft.irfft2, @@ -69,9 +66,9 @@ def get_expected_input_dtype(func, xp): fft.rfft, fft.rfftn, fft.rfft2, - # TODO: fft.ihfft, - # TODO: fft.ihfftn, - # TODO: fft.ihfft2, + fft.ihfft, + fft.ihfftn, + fft.ihfft2, ]: dtype = xp.float64 else: @@ -321,10 +318,9 @@ def _check_axes(self, op, xp): def test_axes_standard(self, op, xp): self._check_axes(op, xp) - # TODO: add this test - # @pytest.mark.parametrize("op", [fft.hfftn, fft.ihfftn]) - # def test_axes_non_standard(self, op, xp): - # self._check_axes(op, xp) + @pytest.mark.parametrize("op", [fft.hfftn, fft.ihfftn]) + def test_axes_non_standard(self, op, xp): + self._check_axes(op, xp) @pytest.mark.parametrize("op", [fft.fftn, fft.ifftn, fft.rfftn, fft.irfftn]) def test_axes_subset_with_shape_standard(self, op, xp): @@ -352,10 +348,10 @@ def test_axes_subset_with_shape_standard(self, op, xp): fft.ifft2, fft.rfft2, fft.irfft2, - # TODO: fft.hfft2, - # TODO: fft.ihfft2, - # TODO: fft.hfftn, - # TODO: fft.ihfftn, + fft.hfft2, + fft.ihfft2, + fft.hfftn, + fft.ihfftn, ], ) def test_axes_subset_with_shape_non_standard(self, op, xp): @@ -386,7 +382,7 @@ def test_all_1d_norm_preserving(self, xp): (fft.rfft, fft.irfft), # hfft: order so the first function takes x.size samples # (necessary for comparison to x_norm above) - # TODO: (fft.ihfft, fft.hfft), + (fft.ihfft, fft.hfft), # functions that expect complex dtypes at the end (fft.fft, fft.ifft), ] @@ -410,24 +406,24 @@ def test_dtypes_nonstandard(self, dtype, xp): res_fft = fft.ifft(fft.fft(x)) res_rfft = fft.irfft(fft.rfft(x)) - # TODO: res_hfft = fft.hfft(fft.ihfft(x), x.shape[0]) + res_hfft = fft.hfft(fft.ihfft(x), x.shape[0]) # Check both numerical results and exact dtype matches assert_array_almost_equal(res_fft, x_complex) assert_array_almost_equal(res_rfft, x) - # TODO: assert_array_almost_equal(res_hfft, x) + assert_array_almost_equal(res_hfft, x) assert res_fft.dtype == x_complex.dtype assert res_rfft.dtype == np.result_type(np.float32, x.dtype) - # TODO: assert res_hfft.dtype == np.result_type(np.float32, x.dtype) + assert res_hfft.dtype == np.result_type(np.float32, x.dtype) @pytest.mark.parametrize("dtype", ["float32", "float64"]) def test_dtypes_real(self, dtype, xp): x = xp.asarray(random(30), dtype=getattr(xp, dtype)) res_rfft = fft.irfft(fft.rfft(x)) - # TODO: res_hfft = fft.hfft(fft.ihfft(x), x.shape[0]) + res_hfft = fft.hfft(fft.ihfft(x), x.shape[0]) # Check both numerical results and exact dtype matches xp_assert_close(res_rfft, x, rtol=1e-06, atol=1e-06) - # TODO: xp_assert_close(res_hfft, x) + xp_assert_close(res_hfft, x, rtol=1e-06, atol=1e-06) @pytest.mark.parametrize("dtype", ["complex64", "complex128"]) def test_dtypes_complex(self, dtype, xp): @@ -456,12 +452,12 @@ def test_dtypes_complex(self, dtype, xp): fft.irfft2, fft.rfftn, fft.irfftn, - # TODO: fft.hfft, - # TODO: fft.ihfft, - # TODO: fft.hfft2, - # TODO: fft.ihfft2, - # TODO: fft.hfftn, - # TODO: fft.ihfftn, + fft.hfft, + fft.ihfft, + fft.hfft2, + fft.ihfft2, + fft.hfftn, + fft.ihfftn, ], ) def test_array_like(self, xp, op): @@ -563,12 +559,10 @@ def test_irfft(self, xp): a = xp.full(self.input_shape, 1 + 0j) self._test_mtsame(fft.irfft, a, xp=xp) - @pytest.mark.skip("hfft is not supported") def test_hfft(self, xp): a = xp.ones(self.input_shape, dtype=xp.complex64) self._test_mtsame(fft.hfft, a, xp=xp) - @pytest.mark.skip("ihfft is not supported") def test_ihfft(self, xp): a = xp.ones(self.input_shape) self._test_mtsame(fft.ihfft, a, xp=xp) @@ -611,12 +605,12 @@ def test_not_last_axis_success(self, xp): fft.ifftn, fft.rfftn, fft.irfftn, - # TODO: fft.hfft, - # TODO: fft.ihfft, + fft.hfft, + fft.ihfft, ], ) def test_non_standard_params(func, xp): - if func in [fft.rfft, fft.rfftn]: # TODO: fft.ihfft + if func in [fft.rfft, fft.rfftn, fft.ihfft]: dtype = xp.float64 else: dtype = xp.complex128 @@ -644,7 +638,7 @@ def test_non_standard_params(func, xp): fft.fftn, fft.ifftn, fft.irfftn, - # TODO: fft.hfft, + fft.hfft, ], ) def test_real_input(func, dtype, xp): diff --git a/mkl_fft/tests/third_party/scipy/test_multithreading.py b/mkl_fft/tests/third_party/scipy/test_multithreading.py index 80626db..945a5e4 100644 --- a/mkl_fft/tests/third_party/scipy/test_multithreading.py +++ b/mkl_fft/tests/third_party/scipy/test_multithreading.py @@ -31,7 +31,12 @@ def x(): fft.irfft2, fft.rfftn, fft.irfftn, - # TODO: fft.hfft, fft.ihfft, fft.hfft2, fft.ihfft2, fft.hfftn, fft.ihfftn, + fft.hfft, + fft.ihfft, + fft.hfft2, + fft.ihfft2, + fft.hfftn, + fft.ihfftn, # TODO: fft.dct, fft.idct, fft.dctn, fft.idctn, # TODO: fft.dst, fft.idst, fft.dstn, fft.idstn, ], From 841fead845d6f440d559040086490f12be6963dd Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Fri, 18 Apr 2025 09:45:10 -0700 Subject: [PATCH 2/2] add docsting --- mkl_fft/_scipy_fft.py | 372 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 330 insertions(+), 42 deletions(-) diff --git a/mkl_fft/_scipy_fft.py b/mkl_fft/_scipy_fft.py index bfc2e2e..789f540 100644 --- a/mkl_fft/_scipy_fft.py +++ b/mkl_fft/_scipy_fft.py @@ -24,6 +24,12 @@ # 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. +""" +An interface for FFT module of Scipy (`scipy.fft`) that uses OneMKL FFT +in the backend. + +""" + import contextlib import contextvars import operator @@ -208,13 +214,20 @@ def _check_plan(plan): ) -def _cook_nd_args(a, s=None, axes=None, invreal=False): +def _check_overwrite_x(overwrite_x): + if overwrite_x: + raise NotImplementedError( + "Overwriting the content of `x` is currently not supported" + ) + + +def _cook_nd_args(x, s=None, axes=None, invreal=False): if s is None: shapeless = True if axes is None: - s = list(a.shape) + s = list(x.shape) else: - s = np.take(a.shape, axes) + s = np.take(x.shape, axes) else: shapeless = False s = list(s) @@ -223,13 +236,13 @@ def _cook_nd_args(a, s=None, axes=None, invreal=False): if len(s) != len(axes): raise ValueError("Shape and axes have different lengths.") if invreal and shapeless: - s[-1] = (a.shape[axes[-1]] - 1) * 2 + s[-1] = (x.shape[axes[-1]] - 1) * 2 return s, axes -def _validate_input(a): +def _validate_input(x): try: - x = _supported_array_or_not_implemented(a) + x = _supported_array_or_not_implemented(x) except ValueError: raise NotImplementedError @@ -237,10 +250,16 @@ def _validate_input(a): def fft( - a, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None + x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None ): + """ + Compute the 1-D discrete Fourier Transform. + + For full documentation refer to `scipy.fft.fft`. + + """ _check_plan(plan) - x = _validate_input(a) + x = _validate_input(x) fsc = _compute_fwd_scale(norm, n, x.shape[axis]) with Workers(workers): @@ -250,10 +269,16 @@ def fft( def ifft( - a, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None + x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None ): + """ + Compute the 1-D inverse discrete Fourier Transform. + + For full documentation refer to `scipy.fft.ifft`. + + """ _check_plan(plan) - x = _validate_input(a) + x = _validate_input(x) fsc = _compute_fwd_scale(norm, n, x.shape[axis]) with Workers(workers): @@ -263,7 +288,7 @@ def ifft( def fft2( - a, + x, s=None, axes=(-2, -1), norm=None, @@ -272,9 +297,14 @@ def fft2( *, plan=None, ): + """ + Compute the 2-D discrete Fourier Transform. + + For full documentation refer to `scipy.fft.fft2`. + """ return fftn( - a, + x, s=s, axes=axes, norm=norm, @@ -285,7 +315,7 @@ def fft2( def ifft2( - a, + x, s=None, axes=(-2, -1), norm=None, @@ -294,9 +324,14 @@ def ifft2( *, plan=None, ): + """ + Compute the 2-D inverse discrete Fourier Transform. + + For full documentation refer to `scipy.fft.ifft2`. + """ return ifftn( - a, + x, s=s, axes=axes, norm=norm, @@ -307,7 +342,7 @@ def ifft2( def fftn( - a, + x, s=None, axes=None, norm=None, @@ -316,8 +351,14 @@ def fftn( *, plan=None, ): + """ + Compute the N-D discrete Fourier Transform. + + For full documentation refer to `scipy.fft.fftn`. + + """ _check_plan(plan) - x = _validate_input(a) + x = _validate_input(x) fsc = _compute_fwd_scale(norm, s, x.shape) with Workers(workers): @@ -327,7 +368,7 @@ def fftn( def ifftn( - a, + x, s=None, axes=None, norm=None, @@ -336,8 +377,14 @@ def ifftn( *, plan=None, ): + """ + Compute the N-D inverse discrete Fourier Transform. + + For full documentation refer to `scipy.fft.ifftn`. + + """ _check_plan(plan) - x = _validate_input(a) + x = _validate_input(x) fsc = _compute_fwd_scale(norm, s, x.shape) with Workers(workers): @@ -346,35 +393,135 @@ def ifftn( ) -def rfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): +def rfft( + x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None +): + """ + Compute the 1-D discrete Fourier Transform for real input.. + + For full documentation refer to `scipy.fft.rfft`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) fsc = _compute_fwd_scale(norm, n, x.shape[axis]) with Workers(workers): return mkl_fft.rfft(x, n=n, axis=axis, fwd_scale=fsc) -def irfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): +def irfft( + x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None +): + """ + Compute the inverse of `rfft`. + + For full documentation refer to `scipy.fft.irfft`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) fsc = _compute_fwd_scale(norm, n, 2 * (x.shape[axis] - 1)) with Workers(workers): return mkl_fft.irfft(x, n=n, axis=axis, fwd_scale=fsc) -def rfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): - return rfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) +def rfft2( + x, + s=None, + axes=(-2, -1), + overwrite_x=False, + norm=None, + workers=None, + *, + plan=None, +): + """ + Compute the 2-D discrete Fourier Transform for real input. + + For full documentation refer to `scipy.fft.rfft2`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ + return rfftn( + x, + s=s, + axes=axes, + norm=norm, + overwrite_x=overwrite_x, + workers=workers, + plan=plan, + ) + + +def irfft2( + x, + s=None, + axes=(-2, -1), + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, +): + """ + Compute the inverse of `rfft2`. + For full documentation refer to `scipy.fft.irfft2`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ + return irfftn( + x, + s=s, + axes=axes, + norm=norm, + overwrite_x=overwrite_x, + workers=workers, + plan=plan, + ) + + +def rfftn( + x, + s=None, + axes=None, + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, +): + """ + Compute the N-D discrete Fourier Transform for real input. -def irfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): - return irfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) + For full documentation refer to `scipy.fft.rfftn`. + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. -def rfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) s, axes = _cook_nd_args(x, s, axes) fsc = _compute_fwd_scale(norm, s, x.shape) @@ -382,9 +529,29 @@ def rfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): return mkl_fft.rfftn(x, s, axes, fwd_scale=fsc) -def irfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): +def irfftn( + x, + s=None, + axes=None, + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, +): + """ + Compute the inverse of `rfftn`. + + For full documentation refer to `scipy.fft.irfftn`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) s, axes = _cook_nd_args(x, s, axes, invreal=True) fsc = _compute_fwd_scale(norm, s, x.shape) @@ -392,9 +559,23 @@ def irfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): return mkl_fft.irfftn(x, s, axes, fwd_scale=fsc) -def hfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): +def hfft( + x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None +): + """ + Compute the FFT of a signal that has Hermitian symmetry, + i.e., a real spectrum. + + For full documentation refer to `scipy.fft.hfft`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) norm = _swap_direction(norm) x = np.array(x, copy=True) np.conjugate(x, out=x) @@ -404,9 +585,22 @@ def hfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): return mkl_fft.irfft(x, n=n, axis=axis, fwd_scale=fsc) -def ihfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): +def ihfft( + x, n=None, axis=-1, norm=None, overwrite_x=False, workers=None, *, plan=None +): + """ + Compute the inverse FFT of a signal that has Hermitian symmetry. + + For full documentation refer to `scipy.fft.ihfft`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) norm = _swap_direction(norm) fsc = _compute_fwd_scale(norm, n, x.shape[axis]) @@ -417,17 +611,91 @@ def ihfft(a, n=None, axis=-1, norm=None, workers=None, *, plan=None): return result -def hfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): - return hfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) +def hfft2( + x, + s=None, + axes=(-2, -1), + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, +): + """ + Compute the 2-D FFT of a Hermitian complex array. + For full documentation refer to `scipy.fft.hfft2`. -def ihfft2(a, s=None, axes=(-2, -1), norm=None, workers=None, *, plan=None): - return ihfftn(a, s=s, axes=axes, norm=norm, workers=workers, plan=plan) + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ + return hfftn( + x, + s=s, + axes=axes, + norm=norm, + overwrite_x=overwrite_x, + workers=workers, + plan=plan, + ) -def hfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): +def ihfft2( + x, + s=None, + axes=(-2, -1), + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, +): + """ + Compute the 2-D inverse FFT of a real spectrum. + + For full documentation refer to `scipy.fft.ihfft2`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ + return ihfftn( + x, + s=s, + axes=axes, + norm=norm, + overwrite_x=overwrite_x, + workers=workers, + plan=plan, + ) + + +def hfftn( + x, + s=None, + axes=None, + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, +): + """ + Compute the N-D FFT of Hermitian symmetric complex input, i.e., a signal with a real spectrum. + + For full documentation refer to `scipy.fft.hfftn`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) norm = _swap_direction(norm) x = np.array(x, copy=True) np.conjugate(x, out=x) @@ -438,9 +706,29 @@ def hfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): return mkl_fft.irfftn(x, s, axes, fwd_scale=fsc) -def ihfftn(a, s=None, axes=None, norm=None, workers=None, *, plan=None): +def ihfftn( + x, + s=None, + axes=None, + norm=None, + overwrite_x=False, + workers=None, + *, + plan=None, +): + """ + Compute the N-D inverse discrete Fourier Transform for a real spectrum. + + For full documentation refer to `scipy.fft.ihfftn`. + + Limitation + ----------- + The kwarg `overwrite_x` is only supported with its default value. + + """ _check_plan(plan) - x = _validate_input(a) + _check_overwrite_x(overwrite_x) + x = _validate_input(x) norm = _swap_direction(norm) s, axes = _cook_nd_args(x, s, axes) fsc = _compute_fwd_scale(norm, s, x.shape)