flopscope 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- benchmarks/__init__.py +1 -0
- benchmarks/__main__.py +6 -0
- benchmarks/_baseline.py +171 -0
- benchmarks/_bitwise.py +231 -0
- benchmarks/_complex.py +176 -0
- benchmarks/_contractions.py +291 -0
- benchmarks/_fft.py +198 -0
- benchmarks/_impl_urls.py +139 -0
- benchmarks/_linalg.py +197 -0
- benchmarks/_linalg_delegates.py +407 -0
- benchmarks/_metadata.py +141 -0
- benchmarks/_misc.py +653 -0
- benchmarks/_perf.py +321 -0
- benchmarks/_perm_group_calibration.py +175 -0
- benchmarks/_pointwise.py +372 -0
- benchmarks/_polynomial.py +193 -0
- benchmarks/_random.py +209 -0
- benchmarks/_reductions.py +136 -0
- benchmarks/_sorting.py +289 -0
- benchmarks/_stats.py +137 -0
- benchmarks/_window.py +92 -0
- benchmarks/accumulation/__init__.py +0 -0
- benchmarks/accumulation/bench_cost_compute.py +138 -0
- benchmarks/dashboard.py +312 -0
- benchmarks/runner.py +636 -0
- flopscope/__init__.py +273 -0
- flopscope/_accumulation/__init__.py +13 -0
- flopscope/_accumulation/_bipartite.py +121 -0
- flopscope/_accumulation/_burnside.py +51 -0
- flopscope/_accumulation/_cache.py +146 -0
- flopscope/_accumulation/_components.py +153 -0
- flopscope/_accumulation/_cost.py +1414 -0
- flopscope/_accumulation/_cost_descriptions.py +63 -0
- flopscope/_accumulation/_detection.py +318 -0
- flopscope/_accumulation/_ladder.py +191 -0
- flopscope/_accumulation/_output_orbit.py +104 -0
- flopscope/_accumulation/_partition.py +290 -0
- flopscope/_accumulation/_path_info.py +211 -0
- flopscope/_accumulation/_public.py +169 -0
- flopscope/_accumulation/_reduction.py +310 -0
- flopscope/_accumulation/_regimes.py +303 -0
- flopscope/_accumulation/_shape.py +33 -0
- flopscope/_accumulation/_wreath.py +209 -0
- flopscope/_budget.py +1027 -0
- flopscope/_config.py +118 -0
- flopscope/_counting_ops.py +451 -0
- flopscope/_display.py +478 -0
- flopscope/_docstrings.py +59 -0
- flopscope/_dtypes.py +20 -0
- flopscope/_einsum.py +717 -0
- flopscope/_errstate.py +25 -0
- flopscope/_flops.py +282 -0
- flopscope/_free_ops.py +2654 -0
- flopscope/_ndarray.py +1126 -0
- flopscope/_opt_einsum/LICENSE +21 -0
- flopscope/_opt_einsum/NOTICE +59 -0
- flopscope/_opt_einsum/__init__.py +209 -0
- flopscope/_opt_einsum/_contract.py +1478 -0
- flopscope/_opt_einsum/_helpers.py +164 -0
- flopscope/_opt_einsum/_hsluv.py +273 -0
- flopscope/_opt_einsum/_path_random.py +462 -0
- flopscope/_opt_einsum/_paths.py +1653 -0
- flopscope/_opt_einsum/_subgraph_symmetry.py +544 -0
- flopscope/_opt_einsum/_symmetry.py +140 -0
- flopscope/_opt_einsum/_typing.py +37 -0
- flopscope/_perm_group.py +717 -0
- flopscope/_pointwise.py +2522 -0
- flopscope/_polynomial.py +278 -0
- flopscope/_registry.py +3216 -0
- flopscope/_sorting_ops.py +571 -0
- flopscope/_symmetric.py +812 -0
- flopscope/_symmetry_transport.py +510 -0
- flopscope/_symmetry_utils.py +669 -0
- flopscope/_type_info.py +12 -0
- flopscope/_unwrap.py +70 -0
- flopscope/_validation.py +83 -0
- flopscope/_version_check.py +46 -0
- flopscope/_weights.py +195 -0
- flopscope/_window.py +177 -0
- flopscope/accounting.py +565 -0
- flopscope/data/default_weights.json +462 -0
- flopscope/data/weights.csv +509 -0
- flopscope/errors.py +197 -0
- flopscope/numpy/__init__.py +878 -0
- flopscope/numpy/fft/__init__.py +55 -0
- flopscope/numpy/fft/_free.py +51 -0
- flopscope/numpy/fft/_transforms.py +695 -0
- flopscope/numpy/linalg/__init__.py +105 -0
- flopscope/numpy/linalg/_aliases.py +126 -0
- flopscope/numpy/linalg/_compound.py +161 -0
- flopscope/numpy/linalg/_decompositions.py +353 -0
- flopscope/numpy/linalg/_properties.py +533 -0
- flopscope/numpy/linalg/_solvers.py +444 -0
- flopscope/numpy/linalg/_svd.py +122 -0
- flopscope/numpy/random/__init__.py +684 -0
- flopscope/numpy/random/_cost_formulas.py +115 -0
- flopscope/numpy/random/_counted_classes.py +241 -0
- flopscope/numpy/testing/__init__.py +13 -0
- flopscope/numpy/typing/__init__.py +30 -0
- flopscope/py.typed +0 -0
- flopscope/stats/__init__.py +84 -0
- flopscope/stats/_base.py +77 -0
- flopscope/stats/_cauchy.py +146 -0
- flopscope/stats/_erf.py +190 -0
- flopscope/stats/_expon.py +146 -0
- flopscope/stats/_laplace.py +150 -0
- flopscope/stats/_logistic.py +148 -0
- flopscope/stats/_lognorm.py +160 -0
- flopscope/stats/_ndtri.py +133 -0
- flopscope/stats/_norm.py +149 -0
- flopscope/stats/_truncnorm.py +186 -0
- flopscope/stats/_uniform.py +141 -0
- flopscope-0.2.0.dist-info/METADATA +23 -0
- flopscope-0.2.0.dist-info/RECORD +115 -0
- flopscope-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
# src/flopscope/fft/_transforms.py
|
|
2
|
+
"""FFT transform wrappers with FLOP counting.
|
|
3
|
+
|
|
4
|
+
Cost model: 5 * N * log2(N) for complex DFT of length N (Cooley-Tukey radix-2).
|
|
5
|
+
Source: Cooley & Tukey (1965); Van Loan, "Computational Frameworks for the FFT" (1992), §1.4.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
|
|
13
|
+
import numpy as _np
|
|
14
|
+
from numpy.typing import ArrayLike
|
|
15
|
+
|
|
16
|
+
from flopscope._budget import _call_numpy, _counted_wrapper
|
|
17
|
+
from flopscope._docstrings import attach_docstring
|
|
18
|
+
from flopscope._ndarray import FlopscopeArray, _to_base_ndarray
|
|
19
|
+
from flopscope._validation import require_budget
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def fft_cost(n: int) -> int:
|
|
23
|
+
"""FLOP cost of a 1-D complex FFT.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
n : int
|
|
28
|
+
Transform length.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
int
|
|
33
|
+
Estimated FLOP count: 5 * n * ceil(log2(n)).
|
|
34
|
+
|
|
35
|
+
Notes
|
|
36
|
+
-----
|
|
37
|
+
Source: Cooley & Tukey (1965); Van Loan, *Computational Frameworks
|
|
38
|
+
for the FFT* (1992), §1.4. Assumes radix-2 Cooley-Tukey algorithm.
|
|
39
|
+
"""
|
|
40
|
+
if n <= 1:
|
|
41
|
+
return 0
|
|
42
|
+
return 5 * n * math.ceil(math.log2(n))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def rfft_cost(n: int) -> int:
|
|
46
|
+
"""FLOP cost of a 1-D real FFT.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
n : int
|
|
51
|
+
Input length (real-valued).
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
int
|
|
56
|
+
Estimated FLOP count: 5 * (n // 2) * ceil(log2(n)).
|
|
57
|
+
|
|
58
|
+
Notes
|
|
59
|
+
-----
|
|
60
|
+
The real FFT exploits conjugate symmetry, roughly halving the work
|
|
61
|
+
compared to a full complex FFT.
|
|
62
|
+
"""
|
|
63
|
+
if n <= 1:
|
|
64
|
+
return 0
|
|
65
|
+
return 5 * (n // 2) * math.ceil(math.log2(n))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def fftn_cost(shape: tuple[int, ...]) -> int:
|
|
69
|
+
"""FLOP cost of an N-D complex FFT.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
shape : tuple of int
|
|
74
|
+
Transform shape along each axis.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
int
|
|
79
|
+
Estimated FLOP count: 5 * N * sum(ceil(log2(d_i))), where N = prod(shape).
|
|
80
|
+
|
|
81
|
+
Notes
|
|
82
|
+
-----
|
|
83
|
+
The multi-dimensional FFT is computed as successive 1-D FFTs along each
|
|
84
|
+
axis. The cost along axis *i* is 5 * (N / d_i) * d_i * ceil(log2(d_i))
|
|
85
|
+
= 5 * N * ceil(log2(d_i)). Summing over axes gives
|
|
86
|
+
5 * N * sum(ceil(log2(d_i))).
|
|
87
|
+
"""
|
|
88
|
+
N = 1
|
|
89
|
+
for d in shape:
|
|
90
|
+
N *= d
|
|
91
|
+
if N <= 1:
|
|
92
|
+
return 0
|
|
93
|
+
log_sum = sum(math.ceil(math.log2(d)) for d in shape if d > 1)
|
|
94
|
+
return 5 * N * log_sum
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def rfftn_cost(shape: tuple[int, ...]) -> int:
|
|
98
|
+
"""FLOP cost of an N-D real FFT.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
shape : tuple of int
|
|
103
|
+
Transform shape along each axis.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
int
|
|
108
|
+
Estimated FLOP count: 5 * (N // 2) * sum(ceil(log2(d_i))), where N = prod(shape).
|
|
109
|
+
|
|
110
|
+
Notes
|
|
111
|
+
-----
|
|
112
|
+
Exploits conjugate symmetry along the last axis, roughly halving
|
|
113
|
+
the work compared to a full complex N-D FFT.
|
|
114
|
+
"""
|
|
115
|
+
N = 1
|
|
116
|
+
for d in shape:
|
|
117
|
+
N *= d
|
|
118
|
+
if N <= 1:
|
|
119
|
+
return 0
|
|
120
|
+
log_sum = sum(math.ceil(math.log2(d)) for d in shape if d > 1)
|
|
121
|
+
return 5 * (N // 2) * log_sum
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def hfft_cost(n_out: int) -> int:
|
|
125
|
+
"""FLOP cost of a Hermitian FFT.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
n_out : int
|
|
130
|
+
Output length.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
int
|
|
135
|
+
Estimated FLOP count: 5 * n_out * ceil(log2(n_out)).
|
|
136
|
+
|
|
137
|
+
Notes
|
|
138
|
+
-----
|
|
139
|
+
The Hermitian FFT computes the FFT of a signal with Hermitian symmetry,
|
|
140
|
+
producing real-valued output.
|
|
141
|
+
"""
|
|
142
|
+
if n_out <= 1:
|
|
143
|
+
return 0
|
|
144
|
+
return 5 * n_out * math.ceil(math.log2(n_out))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# Batch helpers
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _batch_count_1d(a: _np.ndarray, axis: int) -> int:
|
|
153
|
+
"""Number of independent 1-D transforms along *axis*."""
|
|
154
|
+
if a.ndim == 0 or a.shape[axis] == 0:
|
|
155
|
+
return 1
|
|
156
|
+
return a.size // a.shape[axis]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _batch_count_nd(a: _np.ndarray, axes: tuple[int, ...] | None) -> int:
|
|
160
|
+
"""Number of independent N-D transforms over *axes*."""
|
|
161
|
+
if axes is None:
|
|
162
|
+
return 1 # all axes are transform axes
|
|
163
|
+
batch = a.size
|
|
164
|
+
for ax in axes:
|
|
165
|
+
batch //= a.shape[ax]
|
|
166
|
+
return max(batch, 1)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# 1-D transforms
|
|
170
|
+
@_counted_wrapper
|
|
171
|
+
def fft(
|
|
172
|
+
a: ArrayLike,
|
|
173
|
+
n: int | None = None,
|
|
174
|
+
axis: int = -1,
|
|
175
|
+
norm: str | None = None,
|
|
176
|
+
out: ArrayLike | None = None,
|
|
177
|
+
) -> FlopscopeArray:
|
|
178
|
+
budget = require_budget()
|
|
179
|
+
if not isinstance(a, _np.ndarray):
|
|
180
|
+
a = _np.asarray(a)
|
|
181
|
+
if n is None:
|
|
182
|
+
n = a.shape[axis]
|
|
183
|
+
cost = _batch_count_1d(a, axis) * fft_cost(n)
|
|
184
|
+
with budget.deduct("fft.fft", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
185
|
+
result = _call_numpy(
|
|
186
|
+
_np.fft.fft,
|
|
187
|
+
_to_base_ndarray(a),
|
|
188
|
+
n=n,
|
|
189
|
+
axis=axis,
|
|
190
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
191
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
192
|
+
)
|
|
193
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
attach_docstring(
|
|
197
|
+
fft, _np.fft.fft, "fft", "5 \u00d7 n \u00d7 \u2308log\u2082(n)\u2309 FLOPs"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@_counted_wrapper
|
|
202
|
+
def ifft(
|
|
203
|
+
a: ArrayLike,
|
|
204
|
+
n: int | None = None,
|
|
205
|
+
axis: int = -1,
|
|
206
|
+
norm: str | None = None,
|
|
207
|
+
out: ArrayLike | None = None,
|
|
208
|
+
) -> FlopscopeArray:
|
|
209
|
+
budget = require_budget()
|
|
210
|
+
if not isinstance(a, _np.ndarray):
|
|
211
|
+
a = _np.asarray(a)
|
|
212
|
+
if n is None:
|
|
213
|
+
n = a.shape[axis]
|
|
214
|
+
cost = _batch_count_1d(a, axis) * fft_cost(n)
|
|
215
|
+
with budget.deduct("fft.ifft", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
216
|
+
result = _call_numpy(
|
|
217
|
+
_np.fft.ifft,
|
|
218
|
+
_to_base_ndarray(a),
|
|
219
|
+
n=n,
|
|
220
|
+
axis=axis,
|
|
221
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
222
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
223
|
+
)
|
|
224
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
attach_docstring(
|
|
228
|
+
ifft, _np.fft.ifft, "fft", "5 \u00d7 n \u00d7 \u2308log\u2082(n)\u2309 FLOPs"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@_counted_wrapper
|
|
233
|
+
def rfft(
|
|
234
|
+
a: ArrayLike,
|
|
235
|
+
n: int | None = None,
|
|
236
|
+
axis: int = -1,
|
|
237
|
+
norm: str | None = None,
|
|
238
|
+
out: ArrayLike | None = None,
|
|
239
|
+
) -> FlopscopeArray:
|
|
240
|
+
budget = require_budget()
|
|
241
|
+
if not isinstance(a, _np.ndarray):
|
|
242
|
+
a = _np.asarray(a)
|
|
243
|
+
if n is None:
|
|
244
|
+
n = a.shape[axis]
|
|
245
|
+
cost = _batch_count_1d(a, axis) * rfft_cost(n)
|
|
246
|
+
with budget.deduct("fft.rfft", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
247
|
+
result = _call_numpy(
|
|
248
|
+
_np.fft.rfft,
|
|
249
|
+
_to_base_ndarray(a),
|
|
250
|
+
n=n,
|
|
251
|
+
axis=axis,
|
|
252
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
253
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
254
|
+
)
|
|
255
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
attach_docstring(
|
|
259
|
+
rfft, _np.fft.rfft, "fft", "5 \u00d7 (n/2) \u00d7 \u2308log\u2082(n)\u2309 FLOPs"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@_counted_wrapper
|
|
264
|
+
def irfft(
|
|
265
|
+
a: ArrayLike,
|
|
266
|
+
n: int | None = None,
|
|
267
|
+
axis: int = -1,
|
|
268
|
+
norm: str | None = None,
|
|
269
|
+
out: ArrayLike | None = None,
|
|
270
|
+
) -> FlopscopeArray:
|
|
271
|
+
budget = require_budget()
|
|
272
|
+
if not isinstance(a, _np.ndarray):
|
|
273
|
+
a = _np.asarray(a)
|
|
274
|
+
if n is None:
|
|
275
|
+
n = 2 * (a.shape[axis] - 1)
|
|
276
|
+
cost = _batch_count_1d(a, axis) * rfft_cost(n)
|
|
277
|
+
with budget.deduct("fft.irfft", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
278
|
+
result = _call_numpy(
|
|
279
|
+
_np.fft.irfft,
|
|
280
|
+
_to_base_ndarray(a),
|
|
281
|
+
n=n,
|
|
282
|
+
axis=axis,
|
|
283
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
284
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
285
|
+
)
|
|
286
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
attach_docstring(
|
|
290
|
+
irfft, _np.fft.irfft, "fft", "5 \u00d7 (n/2) \u00d7 \u2308log\u2082(n)\u2309 FLOPs"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# 2-D transforms
|
|
295
|
+
@_counted_wrapper
|
|
296
|
+
def fft2(
|
|
297
|
+
a: ArrayLike,
|
|
298
|
+
s: Sequence[int] | None = None,
|
|
299
|
+
axes: Sequence[int] = (-2, -1),
|
|
300
|
+
norm: str | None = None,
|
|
301
|
+
out: ArrayLike | None = None,
|
|
302
|
+
) -> FlopscopeArray:
|
|
303
|
+
budget = require_budget()
|
|
304
|
+
if not isinstance(a, _np.ndarray):
|
|
305
|
+
a = _np.asarray(a)
|
|
306
|
+
if s is None:
|
|
307
|
+
s_for_cost = tuple(a.shape[ax] for ax in axes)
|
|
308
|
+
else:
|
|
309
|
+
s_for_cost = tuple(
|
|
310
|
+
a.shape[axes[i]] if si is None else si for i, si in enumerate(s)
|
|
311
|
+
)
|
|
312
|
+
cost = _batch_count_nd(a, axes) * fftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
313
|
+
with budget.deduct("fft.fft2", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
314
|
+
result = _call_numpy(
|
|
315
|
+
_np.fft.fft2,
|
|
316
|
+
_to_base_ndarray(a),
|
|
317
|
+
s=s,
|
|
318
|
+
axes=axes,
|
|
319
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
320
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
321
|
+
)
|
|
322
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
attach_docstring(
|
|
326
|
+
fft2,
|
|
327
|
+
_np.fft.fft2,
|
|
328
|
+
"fft",
|
|
329
|
+
"5 \u00d7 N \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@_counted_wrapper
|
|
334
|
+
def ifft2(
|
|
335
|
+
a: ArrayLike,
|
|
336
|
+
s: Sequence[int] | None = None,
|
|
337
|
+
axes: Sequence[int] = (-2, -1),
|
|
338
|
+
norm: str | None = None,
|
|
339
|
+
out: ArrayLike | None = None,
|
|
340
|
+
) -> FlopscopeArray:
|
|
341
|
+
budget = require_budget()
|
|
342
|
+
if not isinstance(a, _np.ndarray):
|
|
343
|
+
a = _np.asarray(a)
|
|
344
|
+
if s is None:
|
|
345
|
+
s_for_cost = tuple(a.shape[ax] for ax in axes)
|
|
346
|
+
else:
|
|
347
|
+
s_for_cost = tuple(
|
|
348
|
+
a.shape[axes[i]] if si is None else si for i, si in enumerate(s)
|
|
349
|
+
)
|
|
350
|
+
cost = _batch_count_nd(a, axes) * fftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
351
|
+
with budget.deduct("fft.ifft2", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
352
|
+
result = _call_numpy(
|
|
353
|
+
_np.fft.ifft2,
|
|
354
|
+
_to_base_ndarray(a),
|
|
355
|
+
s=s,
|
|
356
|
+
axes=axes,
|
|
357
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
358
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
359
|
+
)
|
|
360
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
attach_docstring(
|
|
364
|
+
ifft2,
|
|
365
|
+
_np.fft.ifft2,
|
|
366
|
+
"fft",
|
|
367
|
+
"5 \u00d7 N \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@_counted_wrapper
|
|
372
|
+
def rfft2(
|
|
373
|
+
a: ArrayLike,
|
|
374
|
+
s: Sequence[int] | None = None,
|
|
375
|
+
axes: Sequence[int] = (-2, -1),
|
|
376
|
+
norm: str | None = None,
|
|
377
|
+
out: ArrayLike | None = None,
|
|
378
|
+
) -> FlopscopeArray:
|
|
379
|
+
budget = require_budget()
|
|
380
|
+
if not isinstance(a, _np.ndarray):
|
|
381
|
+
a = _np.asarray(a)
|
|
382
|
+
if s is None:
|
|
383
|
+
s_for_cost = tuple(a.shape[ax] for ax in axes)
|
|
384
|
+
else:
|
|
385
|
+
s_for_cost = tuple(
|
|
386
|
+
a.shape[axes[i]] if si is None else si for i, si in enumerate(s)
|
|
387
|
+
)
|
|
388
|
+
cost = _batch_count_nd(a, axes) * rfftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
389
|
+
with budget.deduct("fft.rfft2", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
390
|
+
result = _call_numpy(
|
|
391
|
+
_np.fft.rfft2,
|
|
392
|
+
_to_base_ndarray(a),
|
|
393
|
+
s=s,
|
|
394
|
+
axes=axes,
|
|
395
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
396
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
397
|
+
)
|
|
398
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
attach_docstring(
|
|
402
|
+
rfft2,
|
|
403
|
+
_np.fft.rfft2,
|
|
404
|
+
"fft",
|
|
405
|
+
"5 \u00d7 (N/2) \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@_counted_wrapper
|
|
410
|
+
def irfft2(
|
|
411
|
+
a: ArrayLike,
|
|
412
|
+
s: Sequence[int] | None = None,
|
|
413
|
+
axes: Sequence[int] = (-2, -1),
|
|
414
|
+
norm: str | None = None,
|
|
415
|
+
out: ArrayLike | None = None,
|
|
416
|
+
) -> FlopscopeArray:
|
|
417
|
+
budget = require_budget()
|
|
418
|
+
if not isinstance(a, _np.ndarray):
|
|
419
|
+
a = _np.asarray(a)
|
|
420
|
+
if s is None:
|
|
421
|
+
s_for_cost = (a.shape[axes[0]], 2 * (a.shape[axes[1]] - 1))
|
|
422
|
+
else:
|
|
423
|
+
s_for_cost = tuple(
|
|
424
|
+
(a.shape[axes[i]] if i < len(s) - 1 else 2 * (a.shape[axes[i]] - 1))
|
|
425
|
+
if si is None
|
|
426
|
+
else si
|
|
427
|
+
for i, si in enumerate(s)
|
|
428
|
+
)
|
|
429
|
+
cost = _batch_count_nd(a, axes) * rfftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
430
|
+
with budget.deduct(
|
|
431
|
+
"fft.irfft2", flop_cost=cost, subscripts=None, shapes=(a.shape,)
|
|
432
|
+
):
|
|
433
|
+
result = _call_numpy(
|
|
434
|
+
_np.fft.irfft2,
|
|
435
|
+
_to_base_ndarray(a),
|
|
436
|
+
s=s,
|
|
437
|
+
axes=axes,
|
|
438
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
439
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
440
|
+
)
|
|
441
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
attach_docstring(
|
|
445
|
+
irfft2,
|
|
446
|
+
_np.fft.irfft2,
|
|
447
|
+
"fft",
|
|
448
|
+
"5 \u00d7 (N/2) \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
# N-D transforms
|
|
453
|
+
@_counted_wrapper
|
|
454
|
+
def fftn(
|
|
455
|
+
a: ArrayLike,
|
|
456
|
+
s: Sequence[int] | None = None,
|
|
457
|
+
axes: Sequence[int] | None = None,
|
|
458
|
+
norm: str | None = None,
|
|
459
|
+
out: ArrayLike | None = None,
|
|
460
|
+
) -> FlopscopeArray:
|
|
461
|
+
budget = require_budget()
|
|
462
|
+
if not isinstance(a, _np.ndarray):
|
|
463
|
+
a = _np.asarray(a)
|
|
464
|
+
if s is None:
|
|
465
|
+
s_for_cost = a.shape if axes is None else tuple(a.shape[ax] for ax in axes)
|
|
466
|
+
else:
|
|
467
|
+
eff_axes = tuple(range(a.ndim)) if axes is None else tuple(axes)
|
|
468
|
+
s_for_cost = tuple(
|
|
469
|
+
a.shape[eff_axes[i]] if si is None else si for i, si in enumerate(s)
|
|
470
|
+
)
|
|
471
|
+
cost = _batch_count_nd(a, axes) * fftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
472
|
+
with budget.deduct("fft.fftn", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
473
|
+
result = _call_numpy(
|
|
474
|
+
_np.fft.fftn,
|
|
475
|
+
_to_base_ndarray(a),
|
|
476
|
+
s=s,
|
|
477
|
+
axes=axes,
|
|
478
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
479
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
480
|
+
)
|
|
481
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
attach_docstring(
|
|
485
|
+
fftn,
|
|
486
|
+
_np.fft.fftn,
|
|
487
|
+
"fft",
|
|
488
|
+
"5 \u00d7 N \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@_counted_wrapper
|
|
493
|
+
def ifftn(
|
|
494
|
+
a: ArrayLike,
|
|
495
|
+
s: Sequence[int] | None = None,
|
|
496
|
+
axes: Sequence[int] | None = None,
|
|
497
|
+
norm: str | None = None,
|
|
498
|
+
out: ArrayLike | None = None,
|
|
499
|
+
) -> FlopscopeArray:
|
|
500
|
+
budget = require_budget()
|
|
501
|
+
if not isinstance(a, _np.ndarray):
|
|
502
|
+
a = _np.asarray(a)
|
|
503
|
+
if s is None:
|
|
504
|
+
s_for_cost = a.shape if axes is None else tuple(a.shape[ax] for ax in axes)
|
|
505
|
+
else:
|
|
506
|
+
eff_axes = tuple(range(a.ndim)) if axes is None else tuple(axes)
|
|
507
|
+
s_for_cost = tuple(
|
|
508
|
+
a.shape[eff_axes[i]] if si is None else si for i, si in enumerate(s)
|
|
509
|
+
)
|
|
510
|
+
cost = _batch_count_nd(a, axes) * fftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
511
|
+
with budget.deduct("fft.ifftn", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
512
|
+
result = _call_numpy(
|
|
513
|
+
_np.fft.ifftn,
|
|
514
|
+
_to_base_ndarray(a),
|
|
515
|
+
s=s,
|
|
516
|
+
axes=axes,
|
|
517
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
518
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
519
|
+
)
|
|
520
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
attach_docstring(
|
|
524
|
+
ifftn,
|
|
525
|
+
_np.fft.ifftn,
|
|
526
|
+
"fft",
|
|
527
|
+
"5 \u00d7 N \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@_counted_wrapper
|
|
532
|
+
def rfftn(
|
|
533
|
+
a: ArrayLike,
|
|
534
|
+
s: Sequence[int] | None = None,
|
|
535
|
+
axes: Sequence[int] | None = None,
|
|
536
|
+
norm: str | None = None,
|
|
537
|
+
out: ArrayLike | None = None,
|
|
538
|
+
) -> FlopscopeArray:
|
|
539
|
+
budget = require_budget()
|
|
540
|
+
if not isinstance(a, _np.ndarray):
|
|
541
|
+
a = _np.asarray(a)
|
|
542
|
+
if s is None:
|
|
543
|
+
s_for_cost = a.shape if axes is None else tuple(a.shape[ax] for ax in axes)
|
|
544
|
+
else:
|
|
545
|
+
eff_axes = tuple(range(a.ndim)) if axes is None else tuple(axes)
|
|
546
|
+
s_for_cost = tuple(
|
|
547
|
+
a.shape[eff_axes[i]] if si is None else si for i, si in enumerate(s)
|
|
548
|
+
)
|
|
549
|
+
cost = _batch_count_nd(a, axes) * rfftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
550
|
+
with budget.deduct("fft.rfftn", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
551
|
+
result = _call_numpy(
|
|
552
|
+
_np.fft.rfftn,
|
|
553
|
+
_to_base_ndarray(a),
|
|
554
|
+
s=s,
|
|
555
|
+
axes=axes,
|
|
556
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
557
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
558
|
+
)
|
|
559
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
attach_docstring(
|
|
563
|
+
rfftn,
|
|
564
|
+
_np.fft.rfftn,
|
|
565
|
+
"fft",
|
|
566
|
+
"5 \u00d7 (N/2) \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
@_counted_wrapper
|
|
571
|
+
def irfftn(
|
|
572
|
+
a: ArrayLike,
|
|
573
|
+
s: Sequence[int] | None = None,
|
|
574
|
+
axes: Sequence[int] | None = None,
|
|
575
|
+
norm: str | None = None,
|
|
576
|
+
out: ArrayLike | None = None,
|
|
577
|
+
) -> FlopscopeArray:
|
|
578
|
+
budget = require_budget()
|
|
579
|
+
if not isinstance(a, _np.ndarray):
|
|
580
|
+
a = _np.asarray(a)
|
|
581
|
+
if s is None:
|
|
582
|
+
if axes is None:
|
|
583
|
+
s_for_cost = tuple(
|
|
584
|
+
d if i < len(a.shape) - 1 else 2 * (d - 1)
|
|
585
|
+
for i, d in enumerate(a.shape)
|
|
586
|
+
)
|
|
587
|
+
else:
|
|
588
|
+
s_for_cost = tuple(
|
|
589
|
+
a.shape[ax] if i < len(axes) - 1 else 2 * (a.shape[ax] - 1)
|
|
590
|
+
for i, ax in enumerate(axes)
|
|
591
|
+
)
|
|
592
|
+
else:
|
|
593
|
+
eff_axes = tuple(range(a.ndim)) if axes is None else tuple(axes)
|
|
594
|
+
s_for_cost = tuple(
|
|
595
|
+
(a.shape[eff_axes[i]] if i < len(s) - 1 else 2 * (a.shape[eff_axes[i]] - 1))
|
|
596
|
+
if si is None
|
|
597
|
+
else si
|
|
598
|
+
for i, si in enumerate(s)
|
|
599
|
+
)
|
|
600
|
+
cost = _batch_count_nd(a, axes) * rfftn_cost(s_for_cost) # type: ignore[reportArgumentType]
|
|
601
|
+
with budget.deduct(
|
|
602
|
+
"fft.irfftn", flop_cost=cost, subscripts=None, shapes=(a.shape,)
|
|
603
|
+
):
|
|
604
|
+
result = _call_numpy(
|
|
605
|
+
_np.fft.irfftn,
|
|
606
|
+
_to_base_ndarray(a),
|
|
607
|
+
s=s,
|
|
608
|
+
axes=axes,
|
|
609
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
610
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
611
|
+
)
|
|
612
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
attach_docstring(
|
|
616
|
+
irfftn,
|
|
617
|
+
_np.fft.irfftn,
|
|
618
|
+
"fft",
|
|
619
|
+
"5 \u00d7 (N/2) \u00d7 \u2308log\u2082(N)\u2309 FLOPs, N = product of transform shape",
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
# Hermitian transforms
|
|
624
|
+
@_counted_wrapper
|
|
625
|
+
def hfft(
|
|
626
|
+
a: ArrayLike,
|
|
627
|
+
n: int | None = None,
|
|
628
|
+
axis: int = -1,
|
|
629
|
+
norm: str | None = None,
|
|
630
|
+
out: ArrayLike | None = None,
|
|
631
|
+
) -> FlopscopeArray:
|
|
632
|
+
budget = require_budget()
|
|
633
|
+
if not isinstance(a, _np.ndarray):
|
|
634
|
+
a = _np.asarray(a)
|
|
635
|
+
if n is None:
|
|
636
|
+
n = 2 * (a.shape[axis] - 1)
|
|
637
|
+
cost = _batch_count_1d(a, axis) * hfft_cost(n)
|
|
638
|
+
with budget.deduct("fft.hfft", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
639
|
+
result = _call_numpy(
|
|
640
|
+
_np.fft.hfft,
|
|
641
|
+
_to_base_ndarray(a),
|
|
642
|
+
n=n,
|
|
643
|
+
axis=axis,
|
|
644
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
645
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
646
|
+
)
|
|
647
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
attach_docstring(
|
|
651
|
+
hfft,
|
|
652
|
+
_np.fft.hfft,
|
|
653
|
+
"fft",
|
|
654
|
+
"5 \u00d7 n_out \u00d7 \u2308log\u2082(n_out)\u2309 FLOPs",
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
@_counted_wrapper
|
|
659
|
+
def ihfft(
|
|
660
|
+
a: ArrayLike,
|
|
661
|
+
n: int | None = None,
|
|
662
|
+
axis: int = -1,
|
|
663
|
+
norm: str | None = None,
|
|
664
|
+
out: ArrayLike | None = None,
|
|
665
|
+
) -> FlopscopeArray:
|
|
666
|
+
budget = require_budget()
|
|
667
|
+
if not isinstance(a, _np.ndarray):
|
|
668
|
+
a = _np.asarray(a)
|
|
669
|
+
if n is None:
|
|
670
|
+
n = a.shape[axis]
|
|
671
|
+
cost = _batch_count_1d(a, axis) * hfft_cost(n)
|
|
672
|
+
with budget.deduct("fft.ihfft", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
673
|
+
result = _call_numpy(
|
|
674
|
+
_np.fft.ihfft,
|
|
675
|
+
_to_base_ndarray(a),
|
|
676
|
+
n=n,
|
|
677
|
+
axis=axis,
|
|
678
|
+
norm=norm, # type: ignore[reportArgumentType]
|
|
679
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[reportArgumentType]
|
|
680
|
+
)
|
|
681
|
+
return out if out is not None else result # type: ignore[reportReturnType]
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
attach_docstring(
|
|
685
|
+
ihfft,
|
|
686
|
+
_np.fft.ihfft,
|
|
687
|
+
"fft",
|
|
688
|
+
"5 \u00d7 n_out \u00d7 \u2308log\u2082(n_out)\u2309 FLOPs",
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
import sys as _sys # noqa: E402
|
|
692
|
+
|
|
693
|
+
from flopscope._ndarray import wrap_module_returns as _wrap_module_returns # noqa: E402
|
|
694
|
+
|
|
695
|
+
_wrap_module_returns(_sys.modules[__name__])
|