From 5f71898cae1f578b02752974340d02a11403d803 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Mon, 5 May 2025 08:08:21 -0700 Subject: [PATCH 1/3] fix an issue for real inputs of irfft --- .github/workflows/conda-package-cf.yml | 4 +-- .github/workflows/conda-package.yml | 8 +++--- CHANGELOG.md | 5 ++++ mkl_fft/_pydfti.pyx | 25 +++++++++---------- mkl_fft/interfaces/_numpy_fft.py | 10 +++----- mkl_fft/tests/test_fft1d.py | 10 ++++++++ mkl_fft/tests/third_party/scipy/test_basic.py | 10 +++----- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/.github/workflows/conda-package-cf.yml b/.github/workflows/conda-package-cf.yml index 462d99b..02be2c5 100644 --- a/.github/workflows/conda-package-cf.yml +++ b/.github/workflows/conda-package-cf.yml @@ -295,8 +295,8 @@ jobs: FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( SET PACKAGE_VERSION=%%F ) - SET "TEST_DEPENDENCIES=pytest pytest-cov" - conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} ${{ matrix.numpy }} scipy -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + SET "TEST_DEPENDENCIES=pytest scipy" + 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 shell: cmd /C CALL {0} diff --git a/.github/workflows/conda-package.yml b/.github/workflows/conda-package.yml index 86afbe7..f3bc08d 100644 --- a/.github/workflows/conda-package.yml +++ b/.github/workflows/conda-package.yml @@ -131,7 +131,8 @@ jobs: - name: Install mkl_fft run: | CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}" - conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python }} $PACKAGE_NAME pytest scipy $CHANNELS + conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python }} "scipy>=1.10" $CHANNELS + conda install -n ${{ env.TEST_ENV_NAME }} $PACKAGE_NAME pytest $CHANNELS # Test installed packages conda list -n ${{ env.TEST_ENV_NAME }} @@ -295,8 +296,8 @@ jobs: FOR /F "tokens=* USEBACKQ" %%F IN (`python -c "%SCRIPT%"`) DO ( SET PACKAGE_VERSION=%%F ) - SET "TEST_DEPENDENCIES=pytest pytest-cov" - conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} scipy -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} + SET "TEST_DEPENDENCIES=pytest scipy" + conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }} - name: Report content of test environment shell: cmd /C CALL {0} @@ -304,6 +305,7 @@ jobs: echo "Value of CONDA environment variable was: " %CONDA% echo "Value of CONDA_PREFIX environment variable was: " %CONDA_PREFIX% conda info && conda list -n ${{ env.TEST_ENV_NAME }} + - name: Run tests shell: cmd /C CALL {0} run: >- diff --git a/CHANGELOG.md b/CHANGELOG.md index ebb5d30..1fbbb01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed * 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) +### Fixed +* Fixed an issue for calling `mkl_fft.interfaces.numpy.fftn` with an empty axes [gh-139](https://github.com/IntelPython/mkl_fft/pull/139) +* Fixed an issue for calling `mkl_fft.interfaces.numpy.fftn` with a zero-size array [gh-139](https://github.com/IntelPython/mkl_fft/pull/139) +* Fixed inconsistency of input and output arrays dtype for `irfft` function [gh-180](https://github.com/IntelPython/mkl_fft/pull/180) + ## [1.3.14] (04/10/2025) resolves gh-152 by adding an explicit `mkl-service` dependency to `mkl-fft` when building the wheel diff --git a/mkl_fft/_pydfti.pyx b/mkl_fft/_pydfti.pyx index 74f1d69..5cfd20f 100644 --- a/mkl_fft/_pydfti.pyx +++ b/mkl_fft/_pydfti.pyx @@ -89,7 +89,7 @@ def _tls_dfti_cache_capsule(): cdef extern from "Python.h": ctypedef int size_t - long PyInt_AsLong(object ob) + long PyLong_AsLong(object ob) int PyObject_HasAttrString(object, char*) @@ -262,7 +262,7 @@ cdef cnp.ndarray _process_arguments( xnd[0] = cnp.PyArray_NDIM(x_arr) # tensor-rank of the array err = 0 - axis_[0] = PyInt_AsLong(axis) + axis_[0] = PyLong_AsLong(axis) if (axis_[0] == -1 and PyErr_Occurred()): PyErr_Clear() err = 1 @@ -278,7 +278,7 @@ cdef cnp.ndarray _process_arguments( n_[0] = x_arr.shape[axis_[0]] else: try: - n_[0] = PyInt_AsLong(n) + n_[0] = PyLong_AsLong(n) except: err = 1 @@ -334,7 +334,7 @@ cdef int _is_integral(object num): if num is None: return 0 try: - n = PyInt_AsLong(num) + n = PyLong_AsLong(num) _integral = 1 if n > 0 else 0 except: _integral = 0 @@ -665,13 +665,12 @@ def _r2c_fft1d_impl( return f_arr -# this routine is functionally equivalent to numpy.fft.irfft def _c2r_fft1d_impl( x, n=None, axis=-1, overwrite_x=False, double fsc=1.0, out=None ): """ - Uses MKL to perform 1D FFT on the real input array x along the given axis, - producing complex output, but giving only half of the harmonics. + Uses MKL to perform 1D FFT on the real/complex input array x along the + given axis, producing real output. cf. numpy.fft.irfft """ @@ -704,13 +703,13 @@ def _c2r_fft1d_impl( else: # we must cast the input and allocate the output, # so we cast to complex double and operate in place - try: + if x_type is cnp.NPY_FLOAT: x_arr = cnp.PyArray_FROM_OTF( - x_arr, cnp.NPY_CDOUBLE, cnp.NPY_BEHAVED) - except: - raise ValueError( - "First argument should be a real or " - "a complex sequence of single or double precision" + x_arr, cnp.NPY_CFLOAT, cnp.NPY_BEHAVED + ) + else: + x_arr = cnp.PyArray_FROM_OTF( + x_arr, cnp.NPY_CDOUBLE, cnp.NPY_BEHAVED ) x_type = cnp.PyArray_TYPE(x_arr) in_place = 1 diff --git a/mkl_fft/interfaces/_numpy_fft.py b/mkl_fft/interfaces/_numpy_fft.py index 8557213..f2f72d5 100644 --- a/mkl_fft/interfaces/_numpy_fft.py +++ b/mkl_fft/interfaces/_numpy_fft.py @@ -295,13 +295,11 @@ def hfft(a, n=None, axis=-1, norm=None, out=None): """ norm = _swap_direction(norm) x = _downcast_float128_array(a) - x = np.array(x, copy=True) - np.conjugate(x, out=x) fsc = _compute_fwd_scale(norm, n, 2 * (x.shape[axis] - 1)) return _trycall( mkl_fft.irfft, - (x,), + (np.conjugate(x),), {"n": n, "axis": axis, "fwd_scale": fsc, "out": out}, ) @@ -317,9 +315,9 @@ def ihfft(a, n=None, axis=-1, norm=None, out=None): x = _downcast_float128_array(a) fsc = _compute_fwd_scale(norm, n, x.shape[axis]) - output = _trycall( + result = _trycall( mkl_fft.rfft, (x,), {"n": n, "axis": axis, "fwd_scale": fsc, "out": out} ) - np.conjugate(output, out=output) - return output + np.conjugate(result, out=result) + return result diff --git a/mkl_fft/tests/test_fft1d.py b/mkl_fft/tests/test_fft1d.py index 01631fe..dba71f3 100644 --- a/mkl_fft/tests/test_fft1d.py +++ b/mkl_fft/tests/test_fft1d.py @@ -457,3 +457,13 @@ def test_irfft_out_strided(axis): expected = np.fft.irfft(x, axis=axis, out=out) assert_allclose(result, expected) + + +@requires_numpy_2 +@pytest.mark.parametrize("dt", ["i4", "f4", "f8", "c8", "c16"]) +def test_irfft_dtype(dt): + x = np.array(rnd.random((20, 20)), dtype=dt) + result = mkl_fft.irfft(x) + expected = np.fft.irfft(x) + assert result.dtype == expected.dtype + assert_allclose(result, expected, rtol=1e-7, atol=1e-7) diff --git a/mkl_fft/tests/third_party/scipy/test_basic.py b/mkl_fft/tests/third_party/scipy/test_basic.py index 470bbfd..2a5c217 100644 --- a/mkl_fft/tests/third_party/scipy/test_basic.py +++ b/mkl_fft/tests/third_party/scipy/test_basic.py @@ -14,13 +14,11 @@ # pylint: disable=possibly-used-before-assignment if scipy.__version__ < "1.12": - # scipy from Intel channel is 1.10 - pytest.skip( - "This test file needs scipy>=1.12", - allow_module_level=True, - ) + # scipy from Intel channel is 1.10 with python 3.9 and 3.10 + pytest.skip("This test file needs scipy>=1.12", allow_module_level=True) elif scipy.__version__ < "1.14": - # For python<=3.9, scipy<1.14 is installed + # For pytho-3.11 and 3.12, scipy<1.14 is installed from Intel channel + # For python<=3.9, scipy<1.14 is installed from conda channel # pylint: disable=no-name-in-module from scipy._lib._array_api import size as xp_size else: From 7db1838f4fe8d7f9c36e9b2349410ffc3218cc8a Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad <120411540+vtavana@users.noreply.github.com> Date: Fri, 9 May 2025 09:28:13 -0500 Subject: [PATCH 2/3] Update mkl_fft/tests/third_party/scipy/test_basic.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- mkl_fft/tests/third_party/scipy/test_basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkl_fft/tests/third_party/scipy/test_basic.py b/mkl_fft/tests/third_party/scipy/test_basic.py index 2a5c217..1239a26 100644 --- a/mkl_fft/tests/third_party/scipy/test_basic.py +++ b/mkl_fft/tests/third_party/scipy/test_basic.py @@ -17,7 +17,7 @@ # scipy from Intel channel is 1.10 with python 3.9 and 3.10 pytest.skip("This test file needs scipy>=1.12", allow_module_level=True) elif scipy.__version__ < "1.14": - # For pytho-3.11 and 3.12, scipy<1.14 is installed from Intel channel + # For python-3.11 and 3.12, scipy<1.14 is installed from Intel channel # For python<=3.9, scipy<1.14 is installed from conda channel # pylint: disable=no-name-in-module from scipy._lib._array_api import size as xp_size From 64ccae5258612711c47c329a76cacf9ed43ac6ff Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad <120411540+vtavana@users.noreply.github.com> Date: Thu, 15 May 2025 16:24:02 -0500 Subject: [PATCH 3/3] Update mkl_fft/tests/test_fft1d.py Co-authored-by: Anton <100830759+antonwolfy@users.noreply.github.com> --- mkl_fft/tests/test_fft1d.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mkl_fft/tests/test_fft1d.py b/mkl_fft/tests/test_fft1d.py index dba71f3..fbff2bd 100644 --- a/mkl_fft/tests/test_fft1d.py +++ b/mkl_fft/tests/test_fft1d.py @@ -465,5 +465,4 @@ def test_irfft_dtype(dt): x = np.array(rnd.random((20, 20)), dtype=dt) result = mkl_fft.irfft(x) expected = np.fft.irfft(x) - assert result.dtype == expected.dtype - assert_allclose(result, expected, rtol=1e-7, atol=1e-7) + assert_allclose(result, expected, rtol=1e-7, atol=1e-7, strict=True)