Skip to content

Commit 39cdde8

Browse files
committed
update _scipy_fft.py
1 parent d4929a3 commit 39cdde8

13 files changed

+858
-292
lines changed

.github/workflows/build_pip.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ jobs:
5454
run: |
5555
pip install --no-cache-dir cython pytest hypothesis
5656
pip install --no-cache-dir numpy ${{ matrix.use_pre }}
57+
pip install --no-cache-dir scipy
5758
echo "CONDA_PREFFIX is '${CONDA_PREFIX}'"
5859
export MKLROOT=${CONDA_PREFIX}
5960
pip install -e . --no-build-isolation --verbose --no-deps

.github/workflows/conda-package-cf.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ jobs:
117117
- name: Install mkl_fft
118118
run: |
119119
CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}"
120-
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python_ver }} ${{ matrix.numpy }} $PACKAGE_NAME pytest $CHANNELS
120+
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python_ver }} ${{ matrix.numpy }} $PACKAGE_NAME pytest scipy $CHANNELS
121121
# Test installed packages
122122
conda list -n ${{ env.TEST_ENV_NAME }}
123123
- name: Run tests
@@ -269,7 +269,7 @@ jobs:
269269
SET PACKAGE_VERSION=%%F
270270
)
271271
SET "TEST_DEPENDENCIES=pytest pytest-cov"
272-
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 }}
272+
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 }}
273273
- name: Report content of test environment
274274
shell: cmd /C CALL {0}
275275
run: |

.github/workflows/conda-package.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ jobs:
116116
- name: Install mkl_fft
117117
run: |
118118
CHANNELS="-c $GITHUB_WORKSPACE/channel ${{ env.CHANNELS }}"
119-
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python }} $PACKAGE_NAME pytest $CHANNELS
119+
conda create -n ${{ env.TEST_ENV_NAME }} python=${{ matrix.python }} $PACKAGE_NAME scipy pytest $CHANNELS
120120
# Test installed packages
121121
conda list -n ${{ env.TEST_ENV_NAME }}
122122
- name: Run tests
@@ -267,7 +267,7 @@ jobs:
267267
SET PACKAGE_VERSION=%%F
268268
)
269269
SET "TEST_DEPENDENCIES=pytest pytest-cov"
270-
conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} -c ${{ env.workdir }}/channel ${{ env.CHANNELS }}
270+
conda install -n ${{ env.TEST_ENV_NAME }} ${{ env.PACKAGE_NAME }}=%PACKAGE_VERSION% %TEST_DEPENDENCIES% python=${{ matrix.python }} scipy -c ${{ env.workdir }}/channel ${{ env.CHANNELS }}
271271
- name: Report content of test environment
272272
shell: cmd /C CALL {0}
273273
run: |

conda-recipe-cf/meta.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ test:
3333
- pytest -v --pyargs mkl_fft
3434
requires:
3535
- pytest
36+
- scipy
3637
imports:
3738
- mkl_fft
3839
- mkl_fft.interfaces

conda-recipe/meta.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ test:
3333
- pytest -v --pyargs mkl_fft
3434
requires:
3535
- pytest
36+
- scipy
3637
imports:
3738
- mkl_fft
3839
- mkl_fft.interfaces

mkl_fft/_fft_utils.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) 2025, Intel Corporation
3+
#
4+
# Redistribution and use in source and binary forms, with or without
5+
# modification, are permitted provided that the following conditions are met:
6+
#
7+
# * Redistributions of source code must retain the above copyright notice,
8+
# this list of conditions and the following disclaimer.
9+
# * Redistributions in binary form must reproduce the above copyright
10+
# notice, this list of conditions and the following disclaimer in the
11+
# documentation and/or other materials provided with the distribution.
12+
# * Neither the name of Intel Corporation nor the names of its contributors
13+
# may be used to endorse or promote products derived from this software
14+
# without specific prior written permission.
15+
#
16+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+
import numpy as np
28+
29+
__all__ = ["_check_norm", "_compute_fwd_scale"]
30+
31+
32+
def _check_norm(norm):
33+
if norm not in (None, "ortho", "forward", "backward"):
34+
raise ValueError(
35+
f"Invalid norm value {norm} should be None, 'ortho', 'forward', "
36+
"or 'backward'."
37+
)
38+
39+
40+
def _compute_fwd_scale(norm, n, shape):
41+
_check_norm(norm)
42+
if norm in (None, "backward"):
43+
return 1.0
44+
45+
ss = n if n is not None else shape
46+
nn = np.prod(ss)
47+
fsc = 1 / nn if nn != 0 else 1
48+
if norm == "forward":
49+
return fsc
50+
else: # norm == "ortho"
51+
return np.sqrt(fsc)

mkl_fft/_float_utils.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,5 @@ def __supported_array_or_not_implemented(x):
9191
if hasattr(np, "complex256"):
9292
black_list.append(np.complex256)
9393
if __x.dtype in black_list:
94-
return NotImplemented
94+
raise NotImplementedError
9595
return __x

mkl_fft/_numpy_fft.py

+57-78
Original file line numberDiff line numberDiff line change
@@ -74,32 +74,54 @@
7474
import warnings
7575

7676
import numpy as np
77-
from numpy import array, conjugate, prod, sqrt, take
7877

79-
from . import _float_utils
8078
from . import _pydfti as mkl_fft # pylint: disable=no-name-in-module
79+
from ._fft_utils import _check_norm, _compute_fwd_scale
80+
from ._float_utils import __downcast_float128_array
8181

8282

83-
def _compute_fwd_scale(norm, n, shape):
84-
_check_norm(norm)
85-
if norm in (None, "backward"):
86-
return 1.0
87-
88-
ss = n if n is not None else shape
89-
nn = prod(ss)
90-
fsc = 1 / nn if nn != 0 else 1
91-
if norm == "forward":
92-
return fsc
93-
else: # norm == "ortho"
94-
return sqrt(fsc)
95-
96-
97-
def _check_norm(norm):
98-
if norm not in (None, "ortho", "forward", "backward"):
99-
raise ValueError(
100-
f"Invalid norm value {norm} should be None, 'ortho', 'forward', "
101-
"or 'backward'."
83+
# copied with modifications from:
84+
# https://github.com/numpy/numpy/blob/main/numpy/fft/_pocketfft.py
85+
def _cook_nd_args(a, s=None, axes=None, invreal=False):
86+
if s is None:
87+
shapeless = True
88+
if axes is None:
89+
s = list(a.shape)
90+
else:
91+
s = np.take(a.shape, axes)
92+
else:
93+
shapeless = False
94+
s = list(s)
95+
if axes is None:
96+
if not shapeless and np.__version__ >= "2.0":
97+
msg = (
98+
"`axes` should not be `None` if `s` is not `None` "
99+
"(Deprecated in NumPy 2.0). In a future version of NumPy, "
100+
"this will raise an error and `s[i]` will correspond to "
101+
"the size along the transformed axis specified by "
102+
"`axes[i]`. To retain current behaviour, pass a sequence "
103+
"[0, ..., k-1] to `axes` for an array of dimension k."
104+
)
105+
warnings.warn(msg, DeprecationWarning, stacklevel=3)
106+
axes = list(range(-len(s), 0))
107+
if len(s) != len(axes):
108+
raise ValueError("Shape and axes have different lengths.")
109+
if invreal and shapeless:
110+
s[-1] = (a.shape[axes[-1]] - 1) * 2
111+
if None in s and np.__version__ >= "2.0":
112+
msg = (
113+
"Passing an array containing `None` values to `s` is "
114+
"deprecated in NumPy 2.0 and will raise an error in "
115+
"a future version of NumPy. To use the default behaviour "
116+
"of the corresponding 1-D transform, pass the value matching "
117+
"the default for its `n` parameter. To use the default "
118+
"behaviour for every axis, the `s` argument can be omitted."
102119
)
120+
warnings.warn(msg, DeprecationWarning, stacklevel=3)
121+
# use the whole input array along axis `i` if `s[i] == -1 or None`
122+
s = [a.shape[_a] if _s in [-1, None] else _s for _s, _a in zip(s, axes)]
123+
124+
return s, axes
103125

104126

105127
def _swap_direction(norm):
@@ -218,7 +240,7 @@ def fft(a, n=None, axis=-1, norm=None):
218240
219241
"""
220242

221-
x = _float_utils.__downcast_float128_array(a)
243+
x = __downcast_float128_array(a)
222244
fsc = _compute_fwd_scale(norm, n, x.shape[axis])
223245

224246
return trycall(mkl_fft.fft, (x,), {"n": n, "axis": axis, "fwd_scale": fsc})
@@ -311,7 +333,7 @@ def ifft(a, n=None, axis=-1, norm=None):
311333
312334
"""
313335

314-
x = _float_utils.__downcast_float128_array(a)
336+
x = __downcast_float128_array(a)
315337
fsc = _compute_fwd_scale(norm, n, x.shape[axis])
316338

317339
return trycall(mkl_fft.ifft, (x,), {"n": n, "axis": axis, "fwd_scale": fsc})
@@ -402,7 +424,7 @@ def rfft(a, n=None, axis=-1, norm=None):
402424
403425
"""
404426

405-
x = _float_utils.__downcast_float128_array(a)
427+
x = __downcast_float128_array(a)
406428
fsc = _compute_fwd_scale(norm, n, x.shape[axis])
407429

408430
return trycall(mkl_fft.rfft, (x,), {"n": n, "axis": axis, "fwd_scale": fsc})
@@ -495,7 +517,7 @@ def irfft(a, n=None, axis=-1, norm=None):
495517
496518
"""
497519

498-
x = _float_utils.__downcast_float128_array(a)
520+
x = __downcast_float128_array(a)
499521
fsc = _compute_fwd_scale(norm, n, 2 * (x.shape[axis] - 1))
500522

501523
return trycall(
@@ -581,9 +603,9 @@ def hfft(a, n=None, axis=-1, norm=None):
581603
"""
582604

583605
norm = _swap_direction(norm)
584-
x = _float_utils.__downcast_float128_array(a)
585-
x = array(x, copy=True, dtype=complex)
586-
conjugate(x, out=x)
606+
x = __downcast_float128_array(a)
607+
x = np.array(x, copy=True, dtype=complex)
608+
np.conjugate(x, out=x)
587609
fsc = _compute_fwd_scale(norm, n, 2 * (x.shape[axis] - 1))
588610

589611
return trycall(
@@ -651,61 +673,18 @@ def ihfft(a, n=None, axis=-1, norm=None):
651673

652674
# The copy may be required for multithreading.
653675
norm = _swap_direction(norm)
654-
x = _float_utils.__downcast_float128_array(a)
655-
x = array(x, copy=True, dtype=float)
676+
x = __downcast_float128_array(a)
677+
x = np.array(x, copy=True, dtype=float)
656678
fsc = _compute_fwd_scale(norm, n, x.shape[axis])
657679

658680
output = trycall(
659681
mkl_fft.rfft, (x,), {"n": n, "axis": axis, "fwd_scale": fsc}
660682
)
661683

662-
conjugate(output, out=output)
684+
np.conjugate(output, out=output)
663685
return output
664686

665687

666-
# copied from: https://github.com/numpy/numpy/blob/main/numpy/fft/_pocketfft.py
667-
def _cook_nd_args(a, s=None, axes=None, invreal=False):
668-
if s is None:
669-
shapeless = True
670-
if axes is None:
671-
s = list(a.shape)
672-
else:
673-
s = take(a.shape, axes)
674-
else:
675-
shapeless = False
676-
s = list(s)
677-
if axes is None:
678-
if not shapeless and np.__version__ >= "2.0":
679-
msg = (
680-
"`axes` should not be `None` if `s` is not `None` "
681-
"(Deprecated in NumPy 2.0). In a future version of NumPy, "
682-
"this will raise an error and `s[i]` will correspond to "
683-
"the size along the transformed axis specified by "
684-
"`axes[i]`. To retain current behaviour, pass a sequence "
685-
"[0, ..., k-1] to `axes` for an array of dimension k."
686-
)
687-
warnings.warn(msg, DeprecationWarning, stacklevel=3)
688-
axes = list(range(-len(s), 0))
689-
if len(s) != len(axes):
690-
raise ValueError("Shape and axes have different lengths.")
691-
if invreal and shapeless:
692-
s[-1] = (a.shape[axes[-1]] - 1) * 2
693-
if None in s and np.__version__ >= "2.0":
694-
msg = (
695-
"Passing an array containing `None` values to `s` is "
696-
"deprecated in NumPy 2.0 and will raise an error in "
697-
"a future version of NumPy. To use the default behaviour "
698-
"of the corresponding 1-D transform, pass the value matching "
699-
"the default for its `n` parameter. To use the default "
700-
"behaviour for every axis, the `s` argument can be omitted."
701-
)
702-
warnings.warn(msg, DeprecationWarning, stacklevel=3)
703-
# use the whole input array along axis `i` if `s[i] == -1 or None`
704-
s = [a.shape[_a] if _s in [-1, None] else _s for _s, _a in zip(s, axes)]
705-
706-
return s, axes
707-
708-
709688
def fftn(a, s=None, axes=None, norm=None):
710689
"""
711690
Compute the N-dimensional discrete Fourier Transform.
@@ -806,7 +785,7 @@ def fftn(a, s=None, axes=None, norm=None):
806785
807786
"""
808787

809-
x = _float_utils.__downcast_float128_array(a)
788+
x = __downcast_float128_array(a)
810789
s, axes = _cook_nd_args(x, s, axes)
811790
fsc = _compute_fwd_scale(norm, s, x.shape)
812791

@@ -913,7 +892,7 @@ def ifftn(a, s=None, axes=None, norm=None):
913892
914893
"""
915894

916-
x = _float_utils.__downcast_float128_array(a)
895+
x = __downcast_float128_array(a)
917896
s, axes = _cook_nd_args(x, s, axes)
918897
fsc = _compute_fwd_scale(norm, s, x.shape)
919898

@@ -1201,7 +1180,7 @@ def rfftn(a, s=None, axes=None, norm=None):
12011180
12021181
"""
12031182

1204-
x = _float_utils.__downcast_float128_array(a)
1183+
x = __downcast_float128_array(a)
12051184
s, axes = _cook_nd_args(x, s, axes)
12061185
fsc = _compute_fwd_scale(norm, s, x.shape)
12071186

@@ -1345,7 +1324,7 @@ def irfftn(a, s=None, axes=None, norm=None):
13451324
13461325
"""
13471326

1348-
x = _float_utils.__downcast_float128_array(a)
1327+
x = __downcast_float128_array(a)
13491328
s, axes = _cook_nd_args(x, s, axes, invreal=True)
13501329
fsc = _compute_fwd_scale(norm, s, x.shape)
13511330

0 commit comments

Comments
 (0)