diff --git a/CHANGELOG.md b/CHANGELOG.md index 577e1cc2..ac22bf04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `irfft` with `out=` now writes directly into `out` when the cast or zero-padded copy of a non-contiguous input has the same memory order as `out`, avoiding a temporary array and an extra copy [gh-396](https://github.com/IntelPython/mkl_fft/pull/396) ### Fixed +* Zero-padding (`n` larger than the input length along the axis) of input that is both C- and F-contiguous, such as shape `(m, 1)` padded along axis 1, now gives a C-contiguous result as NumPy does instead of a Fortran-contiguous one, so a C-ordered `out` is also written directly without a temporary copy [gh-397](https://github.com/IntelPython/mkl_fft/pull/397) ## [2.4.0] - 2026-10-16 diff --git a/mkl_fft/_pydfti.pyx b/mkl_fft/_pydfti.pyx index 77936f31..41f7bdec 100644 --- a/mkl_fft/_pydfti.pyx +++ b/mkl_fft/_pydfti.pyx @@ -229,9 +229,9 @@ cdef cnp.ndarray _pad_array( b_shape[axis] = n # allocating temporary buffer - x_arr_is_fortran = cnp.PyArray_CHKFLAGS( - x_arr, cnp.NPY_ARRAY_F_CONTIGUOUS - ) + # F order only if x_arr is not also C-contiguous (e.g. shape (m, 1)), + # as NumPy does, otherwise padding along a unit axis gives an F array + x_arr_is_fortran = cnp.PyArray_ISFORTRAN(x_arr) b_arr = cnp.PyArray_EMPTY( b_ndim, b_shape, b_type, x_arr_is_fortran ) # 0 for C-contiguous diff --git a/mkl_fft/tests/test_fft1d.py b/mkl_fft/tests/test_fft1d.py index e63421e3..0c09df37 100644 --- a/mkl_fft/tests/test_fft1d.py +++ b/mkl_fft/tests/test_fft1d.py @@ -522,6 +522,28 @@ def test_irfft_dtype(dt): assert_allclose(result, expected, rtol=1e-7, atol=1e-7, strict=True) +@pytest.mark.parametrize( + "shape, axis", [((5, 1), 1), ((1, 5), 0), ((4, 1, 1), 2), ((1, 1, 4), 0)] +) +@pytest.mark.parametrize("func", ["fft", "ifft", "rfft", "irfft"]) +def test_pad_both_contiguous_input(func, shape, axis): + # x is both C- and F-contiguous and is zero-padded along a unit axis, + # the padded copy and so the result are C-contiguous as in NumPy + x = rnd.random(shape) + if func == "irfft": + x = x + 1j * rnd.random(shape) + expected = getattr(np.fft, func)(x, n=8, axis=axis) + + result = getattr(mkl_fft, func)(x, n=8, axis=axis) + assert result.flags.c_contiguous + assert_allclose(result, expected, atol=1e-12) + + out = np.full_like(expected, -1, order="C") + result = getattr(mkl_fft, func)(x, n=8, axis=axis, out=out) + assert result is out + assert_allclose(result, expected, atol=1e-12) + + @pytest.mark.parametrize("step", [2, -1, -3]) @pytest.mark.parametrize("func", ["fft", "ifft"]) def test_fft_real_input_out_strided_1d(func, step):