first commit
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,56 @@
|
||||
import numpy as np
|
||||
|
||||
class _MockFunction:
|
||||
def __init__(self, return_value = None):
|
||||
self.number_calls = 0
|
||||
self.return_value = return_value
|
||||
self.last_args = ([], {})
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
self.number_calls += 1
|
||||
self.last_args = (args, kwargs)
|
||||
return self.return_value
|
||||
|
||||
|
||||
fft = _MockFunction(np.random.random(10))
|
||||
fft2 = _MockFunction(np.random.random(10))
|
||||
fftn = _MockFunction(np.random.random(10))
|
||||
|
||||
ifft = _MockFunction(np.random.random(10))
|
||||
ifft2 = _MockFunction(np.random.random(10))
|
||||
ifftn = _MockFunction(np.random.random(10))
|
||||
|
||||
rfft = _MockFunction(np.random.random(10))
|
||||
rfft2 = _MockFunction(np.random.random(10))
|
||||
rfftn = _MockFunction(np.random.random(10))
|
||||
|
||||
irfft = _MockFunction(np.random.random(10))
|
||||
irfft2 = _MockFunction(np.random.random(10))
|
||||
irfftn = _MockFunction(np.random.random(10))
|
||||
|
||||
hfft = _MockFunction(np.random.random(10))
|
||||
hfft2 = _MockFunction(np.random.random(10))
|
||||
hfftn = _MockFunction(np.random.random(10))
|
||||
|
||||
ihfft = _MockFunction(np.random.random(10))
|
||||
ihfft2 = _MockFunction(np.random.random(10))
|
||||
ihfftn = _MockFunction(np.random.random(10))
|
||||
|
||||
dct = _MockFunction(np.random.random(10))
|
||||
idct = _MockFunction(np.random.random(10))
|
||||
dctn = _MockFunction(np.random.random(10))
|
||||
idctn = _MockFunction(np.random.random(10))
|
||||
|
||||
dst = _MockFunction(np.random.random(10))
|
||||
idst = _MockFunction(np.random.random(10))
|
||||
dstn = _MockFunction(np.random.random(10))
|
||||
idstn = _MockFunction(np.random.random(10))
|
||||
|
||||
|
||||
__ua_domain__ = "numpy.scipy.fft"
|
||||
|
||||
|
||||
def __ua_function__(method, args, kwargs):
|
||||
fn = globals().get(method.__name__)
|
||||
return (fn(*args, **kwargs) if fn is not None
|
||||
else NotImplemented)
|
||||
@@ -0,0 +1,87 @@
|
||||
import numpy as np
|
||||
import scipy.fft
|
||||
from scipy.fft import set_backend
|
||||
from scipy.fft import _pocketfft
|
||||
from scipy.fft.tests import mock_backend # type: ignore[import]
|
||||
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
import pytest
|
||||
|
||||
fnames = ('fft', 'fft2', 'fftn',
|
||||
'ifft', 'ifft2', 'ifftn',
|
||||
'rfft', 'rfft2', 'rfftn',
|
||||
'irfft', 'irfft2', 'irfftn',
|
||||
'dct', 'idct', 'dctn', 'idctn',
|
||||
'dst', 'idst', 'dstn', 'idstn')
|
||||
|
||||
np_funcs = (np.fft.fft, np.fft.fft2, np.fft.fftn,
|
||||
np.fft.ifft, np.fft.ifft2, np.fft.ifftn,
|
||||
np.fft.rfft, np.fft.rfft2, np.fft.rfftn,
|
||||
np.fft.irfft, np.fft.irfft2, np.fft.irfftn,
|
||||
np.fft.hfft, _pocketfft.hfft2, _pocketfft.hfftn, # np has no hfftn
|
||||
np.fft.ihfft, _pocketfft.ihfft2, _pocketfft.ihfftn,
|
||||
_pocketfft.dct, _pocketfft.idct, _pocketfft.dctn, _pocketfft.idctn,
|
||||
_pocketfft.dst, _pocketfft.idst, _pocketfft.dstn, _pocketfft.idstn)
|
||||
|
||||
funcs = (scipy.fft.fft, scipy.fft.fft2, scipy.fft.fftn,
|
||||
scipy.fft.ifft, scipy.fft.ifft2, scipy.fft.ifftn,
|
||||
scipy.fft.rfft, scipy.fft.rfft2, scipy.fft.rfftn,
|
||||
scipy.fft.irfft, scipy.fft.irfft2, scipy.fft.irfftn,
|
||||
scipy.fft.hfft, scipy.fft.hfft2, scipy.fft.hfftn,
|
||||
scipy.fft.ihfft, scipy.fft.ihfft2, scipy.fft.ihfftn,
|
||||
scipy.fft.dct, scipy.fft.idct, scipy.fft.dctn, scipy.fft.idctn,
|
||||
scipy.fft.dst, scipy.fft.idst, scipy.fft.dstn, scipy.fft.idstn)
|
||||
|
||||
mocks = (mock_backend.fft, mock_backend.fft2, mock_backend.fftn,
|
||||
mock_backend.ifft, mock_backend.ifft2, mock_backend.ifftn,
|
||||
mock_backend.rfft, mock_backend.rfft2, mock_backend.rfftn,
|
||||
mock_backend.irfft, mock_backend.irfft2, mock_backend.irfftn,
|
||||
mock_backend.hfft, mock_backend.hfft2, mock_backend.hfftn,
|
||||
mock_backend.ihfft, mock_backend.ihfft2, mock_backend.ihfftn,
|
||||
mock_backend.dct, mock_backend.idct, mock_backend.dctn, mock_backend.idctn,
|
||||
mock_backend.dst, mock_backend.idst, mock_backend.dstn, mock_backend.idstn)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func, np_func, mock", zip(funcs, np_funcs, mocks))
|
||||
def test_backend_call(func, np_func, mock):
|
||||
x = np.arange(20).reshape((10,2))
|
||||
answer = np_func(x)
|
||||
assert_allclose(func(x), answer, atol=1e-10)
|
||||
|
||||
with set_backend(mock_backend, only=True):
|
||||
mock.number_calls = 0
|
||||
y = func(x)
|
||||
assert_equal(y, mock.return_value)
|
||||
assert_equal(mock.number_calls, 1)
|
||||
|
||||
assert_allclose(func(x), answer, atol=1e-10)
|
||||
|
||||
|
||||
plan_funcs = (scipy.fft.fft, scipy.fft.fft2, scipy.fft.fftn,
|
||||
scipy.fft.ifft, scipy.fft.ifft2, scipy.fft.ifftn,
|
||||
scipy.fft.rfft, scipy.fft.rfft2, scipy.fft.rfftn,
|
||||
scipy.fft.irfft, scipy.fft.irfft2, scipy.fft.irfftn,
|
||||
scipy.fft.hfft, scipy.fft.hfft2, scipy.fft.hfftn,
|
||||
scipy.fft.ihfft, scipy.fft.ihfft2, scipy.fft.ihfftn)
|
||||
|
||||
plan_mocks = (mock_backend.fft, mock_backend.fft2, mock_backend.fftn,
|
||||
mock_backend.ifft, mock_backend.ifft2, mock_backend.ifftn,
|
||||
mock_backend.rfft, mock_backend.rfft2, mock_backend.rfftn,
|
||||
mock_backend.irfft, mock_backend.irfft2, mock_backend.irfftn,
|
||||
mock_backend.hfft, mock_backend.hfft2, mock_backend.hfftn,
|
||||
mock_backend.ihfft, mock_backend.ihfft2, mock_backend.ihfftn)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func, mock", zip(plan_funcs, plan_mocks))
|
||||
def test_backend_plan(func, mock):
|
||||
x = np.arange(20).reshape((10, 2))
|
||||
|
||||
with pytest.raises(NotImplementedError, match='precomputed plan'):
|
||||
func(x, plan='foo')
|
||||
|
||||
with set_backend(mock_backend, only=True):
|
||||
mock.number_calls = 0
|
||||
y = func(x, plan='foo')
|
||||
assert_equal(y, mock.return_value)
|
||||
assert_equal(mock.number_calls, 1)
|
||||
assert_equal(mock.last_args[1]['plan'], 'foo')
|
||||
@@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
TEST_BODY = r"""
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import scipy
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
if hasattr(scipy, 'fft'):
|
||||
raise AssertionError("scipy.fft should require an explicit import")
|
||||
|
||||
np.random.seed(1234)
|
||||
x = np.random.randn(10) + 1j * np.random.randn(10)
|
||||
X = np.fft.fft(x)
|
||||
# Callable before scipy.fft is imported
|
||||
with pytest.deprecated_call(match=r'2\.0\.0'):
|
||||
y = scipy.ifft(X)
|
||||
assert_allclose(y, x)
|
||||
|
||||
# Callable after scipy.fft is imported
|
||||
import scipy.fft
|
||||
with pytest.deprecated_call(match=r'2\.0\.0'):
|
||||
y = scipy.ifft(X)
|
||||
assert_allclose(y, x)
|
||||
|
||||
"""
|
||||
|
||||
def test_fft_function():
|
||||
# Historically, scipy.fft was an alias for numpy.fft.fft
|
||||
# Ensure there are no conflicts with the FFT module (gh-10253)
|
||||
|
||||
# Test must run in a subprocess so scipy.fft is not already imported
|
||||
subprocess.check_call([sys.executable, '-c', TEST_BODY])
|
||||
|
||||
# scipy.fft is the correct module
|
||||
from scipy import fft
|
||||
assert not callable(fft)
|
||||
assert fft.__name__ == 'scipy.fft'
|
||||
|
||||
from scipy import ifft
|
||||
assert ifft.__wrapped__ is np.fft.ifft
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
import warnings
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import pytest
|
||||
|
||||
from scipy.fft._fftlog import fht, ifht, fhtoffset
|
||||
from scipy.special import poch
|
||||
|
||||
|
||||
def test_fht_agrees_with_fftlog():
|
||||
# check that fht numerically agrees with the output from Fortran FFTLog,
|
||||
# the results were generated with the provided `fftlogtest` program,
|
||||
# after fixing how the k array is generated (divide range by n-1, not n)
|
||||
|
||||
# test function, analytical Hankel transform is of the same form
|
||||
def f(r, mu):
|
||||
return r**(mu+1)*np.exp(-r**2/2)
|
||||
|
||||
r = np.logspace(-4, 4, 16)
|
||||
|
||||
dln = np.log(r[1]/r[0])
|
||||
mu = 0.3
|
||||
offset = 0.0
|
||||
bias = 0.0
|
||||
|
||||
a = f(r, mu)
|
||||
|
||||
# test 1: compute as given
|
||||
ours = fht(a, dln, mu, offset=offset, bias=bias)
|
||||
theirs = [-0.1159922613593045E-02, +0.1625822618458832E-02,
|
||||
-0.1949518286432330E-02, +0.3789220182554077E-02,
|
||||
+0.5093959119952945E-03, +0.2785387803618774E-01,
|
||||
+0.9944952700848897E-01, +0.4599202164586588E+00,
|
||||
+0.3157462160881342E+00, -0.8201236844404755E-03,
|
||||
-0.7834031308271878E-03, +0.3931444945110708E-03,
|
||||
-0.2697710625194777E-03, +0.3568398050238820E-03,
|
||||
-0.5554454827797206E-03, +0.8286331026468585E-03]
|
||||
assert_allclose(ours, theirs)
|
||||
|
||||
# test 2: change to optimal offset
|
||||
offset = fhtoffset(dln, mu, bias=bias)
|
||||
ours = fht(a, dln, mu, offset=offset, bias=bias)
|
||||
theirs = [+0.4353768523152057E-04, -0.9197045663594285E-05,
|
||||
+0.3150140927838524E-03, +0.9149121960963704E-03,
|
||||
+0.5808089753959363E-02, +0.2548065256377240E-01,
|
||||
+0.1339477692089897E+00, +0.4821530509479356E+00,
|
||||
+0.2659899781579785E+00, -0.1116475278448113E-01,
|
||||
+0.1791441617592385E-02, -0.4181810476548056E-03,
|
||||
+0.1314963536765343E-03, -0.5422057743066297E-04,
|
||||
+0.3208681804170443E-04, -0.2696849476008234E-04]
|
||||
assert_allclose(ours, theirs)
|
||||
|
||||
# test 3: positive bias
|
||||
bias = 0.8
|
||||
offset = fhtoffset(dln, mu, bias=bias)
|
||||
ours = fht(a, dln, mu, offset=offset, bias=bias)
|
||||
theirs = [-7.3436673558316850E+00, +0.1710271207817100E+00,
|
||||
+0.1065374386206564E+00, -0.5121739602708132E-01,
|
||||
+0.2636649319269470E-01, +0.1697209218849693E-01,
|
||||
+0.1250215614723183E+00, +0.4739583261486729E+00,
|
||||
+0.2841149874912028E+00, -0.8312764741645729E-02,
|
||||
+0.1024233505508988E-02, -0.1644902767389120E-03,
|
||||
+0.3305775476926270E-04, -0.7786993194882709E-05,
|
||||
+0.1962258449520547E-05, -0.8977895734909250E-06]
|
||||
assert_allclose(ours, theirs)
|
||||
|
||||
# test 4: negative bias
|
||||
bias = -0.8
|
||||
offset = fhtoffset(dln, mu, bias=bias)
|
||||
ours = fht(a, dln, mu, offset=offset, bias=bias)
|
||||
theirs = [+0.8985777068568745E-05, +0.4074898209936099E-04,
|
||||
+0.2123969254700955E-03, +0.1009558244834628E-02,
|
||||
+0.5131386375222176E-02, +0.2461678673516286E-01,
|
||||
+0.1235812845384476E+00, +0.4719570096404403E+00,
|
||||
+0.2893487490631317E+00, -0.1686570611318716E-01,
|
||||
+0.2231398155172505E-01, -0.1480742256379873E-01,
|
||||
+0.1692387813500801E+00, +0.3097490354365797E+00,
|
||||
+2.7593607182401860E+00, 10.5251075070045800E+00]
|
||||
assert_allclose(ours, theirs)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('optimal', [True, False])
|
||||
@pytest.mark.parametrize('offset', [0.0, 1.0, -1.0])
|
||||
@pytest.mark.parametrize('bias', [0, 0.1, -0.1])
|
||||
@pytest.mark.parametrize('n', [64, 63])
|
||||
def test_fht_identity(n, bias, offset, optimal):
|
||||
rng = np.random.RandomState(3491349965)
|
||||
|
||||
a = rng.standard_normal(n)
|
||||
dln = rng.uniform(-1, 1)
|
||||
mu = rng.uniform(-2, 2)
|
||||
|
||||
if optimal:
|
||||
offset = fhtoffset(dln, mu, initial=offset, bias=bias)
|
||||
|
||||
A = fht(a, dln, mu, offset=offset, bias=bias)
|
||||
a_ = ifht(A, dln, mu, offset=offset, bias=bias)
|
||||
|
||||
assert_allclose(a, a_)
|
||||
|
||||
|
||||
def test_fht_special_cases():
|
||||
rng = np.random.RandomState(3491349965)
|
||||
|
||||
a = rng.standard_normal(64)
|
||||
dln = rng.uniform(-1, 1)
|
||||
|
||||
# let xp = (mu+1+q)/2, xm = (mu+1-q)/2, M = {0, -1, -2, ...}
|
||||
|
||||
# case 1: xp in M, xm in M => well-defined transform
|
||||
mu, bias = -4.0, 1.0
|
||||
with warnings.catch_warnings(record=True) as record:
|
||||
fht(a, dln, mu, bias=bias)
|
||||
assert not record, 'fht warned about a well-defined transform'
|
||||
|
||||
# case 2: xp not in M, xm in M => well-defined transform
|
||||
mu, bias = -2.5, 0.5
|
||||
with warnings.catch_warnings(record=True) as record:
|
||||
fht(a, dln, mu, bias=bias)
|
||||
assert not record, 'fht warned about a well-defined transform'
|
||||
|
||||
# case 3: xp in M, xm not in M => singular transform
|
||||
mu, bias = -3.5, 0.5
|
||||
with pytest.warns(Warning) as record:
|
||||
fht(a, dln, mu, bias=bias)
|
||||
assert record, 'fht did not warn about a singular transform'
|
||||
|
||||
# case 4: xp not in M, xm in M => singular inverse transform
|
||||
mu, bias = -2.5, 0.5
|
||||
with pytest.warns(Warning) as record:
|
||||
ifht(a, dln, mu, bias=bias)
|
||||
assert record, 'ifht did not warn about a singular transform'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('n', [64, 63])
|
||||
def test_fht_exact(n):
|
||||
rng = np.random.RandomState(3491349965)
|
||||
|
||||
# for a(r) a power law r^\gamma, the fast Hankel transform produces the
|
||||
# exact continuous Hankel transform if biased with q = \gamma
|
||||
|
||||
mu = rng.uniform(0, 3)
|
||||
|
||||
# convergence of HT: -1-mu < gamma < 1/2
|
||||
gamma = rng.uniform(-1-mu, 1/2)
|
||||
|
||||
r = np.logspace(-2, 2, n)
|
||||
a = r**gamma
|
||||
|
||||
dln = np.log(r[1]/r[0])
|
||||
|
||||
offset = fhtoffset(dln, mu, initial=0.0, bias=gamma)
|
||||
|
||||
A = fht(a, dln, mu, offset=offset, bias=gamma)
|
||||
|
||||
k = np.exp(offset)/r[::-1]
|
||||
|
||||
# analytical result
|
||||
At = (2/k)**gamma * poch((mu+1-gamma)/2, gamma)
|
||||
|
||||
assert_allclose(A, At)
|
||||
@@ -0,0 +1,300 @@
|
||||
from scipy.fft._helper import next_fast_len, _init_nd_shape_and_axes
|
||||
from numpy.testing import assert_equal, assert_array_equal
|
||||
from pytest import raises as assert_raises
|
||||
import pytest
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
_5_smooth_numbers = [
|
||||
2, 3, 4, 5, 6, 8, 9, 10,
|
||||
2 * 3 * 5,
|
||||
2**3 * 3**5,
|
||||
2**3 * 3**3 * 5**2,
|
||||
]
|
||||
|
||||
def test_next_fast_len():
|
||||
for n in _5_smooth_numbers:
|
||||
assert_equal(next_fast_len(n), n)
|
||||
|
||||
|
||||
def _assert_n_smooth(x, n):
|
||||
x_orig = x
|
||||
if n < 2:
|
||||
assert False
|
||||
|
||||
while True:
|
||||
q, r = divmod(x, 2)
|
||||
if r != 0:
|
||||
break
|
||||
x = q
|
||||
|
||||
for d in range(3, n+1, 2):
|
||||
while True:
|
||||
q, r = divmod(x, d)
|
||||
if r != 0:
|
||||
break
|
||||
x = q
|
||||
|
||||
assert x == 1, \
|
||||
'x={} is not {}-smooth, remainder={}'.format(x_orig, n, x)
|
||||
|
||||
|
||||
class TestNextFastLen:
|
||||
|
||||
def test_next_fast_len(self):
|
||||
np.random.seed(1234)
|
||||
|
||||
def nums():
|
||||
yield from range(1, 1000)
|
||||
yield 2**5 * 3**5 * 4**5 + 1
|
||||
|
||||
for n in nums():
|
||||
m = next_fast_len(n)
|
||||
_assert_n_smooth(m, 11)
|
||||
assert m == next_fast_len(n, False)
|
||||
|
||||
m = next_fast_len(n, True)
|
||||
_assert_n_smooth(m, 5)
|
||||
|
||||
def test_np_integers(self):
|
||||
ITYPES = [np.int16, np.int32, np.int64, np.uint16, np.uint32, np.uint64]
|
||||
for ityp in ITYPES:
|
||||
x = ityp(12345)
|
||||
testN = next_fast_len(x)
|
||||
assert_equal(testN, next_fast_len(int(x)))
|
||||
|
||||
def testnext_fast_len_small(self):
|
||||
hams = {
|
||||
1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 8, 8: 8, 14: 15, 15: 15,
|
||||
16: 16, 17: 18, 1021: 1024, 1536: 1536, 51200000: 51200000
|
||||
}
|
||||
for x, y in hams.items():
|
||||
assert_equal(next_fast_len(x, True), y)
|
||||
|
||||
@pytest.mark.xfail(sys.maxsize < 2**32,
|
||||
reason="Hamming Numbers too large for 32-bit",
|
||||
raises=ValueError, strict=True)
|
||||
def testnext_fast_len_big(self):
|
||||
hams = {
|
||||
510183360: 510183360, 510183360 + 1: 512000000,
|
||||
511000000: 512000000,
|
||||
854296875: 854296875, 854296875 + 1: 859963392,
|
||||
196608000000: 196608000000, 196608000000 + 1: 196830000000,
|
||||
8789062500000: 8789062500000, 8789062500000 + 1: 8796093022208,
|
||||
206391214080000: 206391214080000,
|
||||
206391214080000 + 1: 206624260800000,
|
||||
470184984576000: 470184984576000,
|
||||
470184984576000 + 1: 470715894135000,
|
||||
7222041363087360: 7222041363087360,
|
||||
7222041363087360 + 1: 7230196133913600,
|
||||
# power of 5 5**23
|
||||
11920928955078125: 11920928955078125,
|
||||
11920928955078125 - 1: 11920928955078125,
|
||||
# power of 3 3**34
|
||||
16677181699666569: 16677181699666569,
|
||||
16677181699666569 - 1: 16677181699666569,
|
||||
# power of 2 2**54
|
||||
18014398509481984: 18014398509481984,
|
||||
18014398509481984 - 1: 18014398509481984,
|
||||
# above this, int(ceil(n)) == int(ceil(n+1))
|
||||
19200000000000000: 19200000000000000,
|
||||
19200000000000000 + 1: 19221679687500000,
|
||||
288230376151711744: 288230376151711744,
|
||||
288230376151711744 + 1: 288325195312500000,
|
||||
288325195312500000 - 1: 288325195312500000,
|
||||
288325195312500000: 288325195312500000,
|
||||
288325195312500000 + 1: 288555831593533440,
|
||||
}
|
||||
for x, y in hams.items():
|
||||
assert_equal(next_fast_len(x, True), y)
|
||||
|
||||
def test_keyword_args(self):
|
||||
assert next_fast_len(11, real=True) == 12
|
||||
assert next_fast_len(target=7, real=False) == 7
|
||||
|
||||
|
||||
class Test_init_nd_shape_and_axes:
|
||||
|
||||
def test_py_0d_defaults(self):
|
||||
x = np.array(4)
|
||||
shape = None
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([])
|
||||
axes_expected = np.array([])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_np_0d_defaults(self):
|
||||
x = np.array(7.)
|
||||
shape = None
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([])
|
||||
axes_expected = np.array([])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_py_1d_defaults(self):
|
||||
x = np.array([1, 2, 3])
|
||||
shape = None
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([3])
|
||||
axes_expected = np.array([0])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_np_1d_defaults(self):
|
||||
x = np.arange(0, 1, .1)
|
||||
shape = None
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([10])
|
||||
axes_expected = np.array([0])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_py_2d_defaults(self):
|
||||
x = np.array([[1, 2, 3, 4],
|
||||
[5, 6, 7, 8]])
|
||||
shape = None
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([2, 4])
|
||||
axes_expected = np.array([0, 1])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_np_2d_defaults(self):
|
||||
x = np.arange(0, 1, .1).reshape(5, 2)
|
||||
shape = None
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([5, 2])
|
||||
axes_expected = np.array([0, 1])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_np_5d_defaults(self):
|
||||
x = np.zeros([6, 2, 5, 3, 4])
|
||||
shape = None
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([6, 2, 5, 3, 4])
|
||||
axes_expected = np.array([0, 1, 2, 3, 4])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_np_5d_set_shape(self):
|
||||
x = np.zeros([6, 2, 5, 3, 4])
|
||||
shape = [10, -1, -1, 1, 4]
|
||||
axes = None
|
||||
|
||||
shape_expected = np.array([10, 2, 5, 1, 4])
|
||||
axes_expected = np.array([0, 1, 2, 3, 4])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_np_5d_set_axes(self):
|
||||
x = np.zeros([6, 2, 5, 3, 4])
|
||||
shape = None
|
||||
axes = [4, 1, 2]
|
||||
|
||||
shape_expected = np.array([4, 2, 5])
|
||||
axes_expected = np.array([4, 1, 2])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_np_5d_set_shape_axes(self):
|
||||
x = np.zeros([6, 2, 5, 3, 4])
|
||||
shape = [10, -1, 2]
|
||||
axes = [1, 0, 3]
|
||||
|
||||
shape_expected = np.array([10, 6, 2])
|
||||
axes_expected = np.array([1, 0, 3])
|
||||
|
||||
shape_res, axes_res = _init_nd_shape_and_axes(x, shape, axes)
|
||||
|
||||
assert_equal(shape_res, shape_expected)
|
||||
assert_equal(axes_res, axes_expected)
|
||||
|
||||
def test_shape_axes_subset(self):
|
||||
x = np.zeros((2, 3, 4, 5))
|
||||
shape, axes = _init_nd_shape_and_axes(x, shape=(5, 5, 5), axes=None)
|
||||
|
||||
assert_array_equal(shape, [5, 5, 5])
|
||||
assert_array_equal(axes, [1, 2, 3])
|
||||
|
||||
def test_errors(self):
|
||||
x = np.zeros(1)
|
||||
with assert_raises(ValueError, match="axes must be a scalar or "
|
||||
"iterable of integers"):
|
||||
_init_nd_shape_and_axes(x, shape=None, axes=[[1, 2], [3, 4]])
|
||||
|
||||
with assert_raises(ValueError, match="axes must be a scalar or "
|
||||
"iterable of integers"):
|
||||
_init_nd_shape_and_axes(x, shape=None, axes=[1., 2., 3., 4.])
|
||||
|
||||
with assert_raises(ValueError,
|
||||
match="axes exceeds dimensionality of input"):
|
||||
_init_nd_shape_and_axes(x, shape=None, axes=[1])
|
||||
|
||||
with assert_raises(ValueError,
|
||||
match="axes exceeds dimensionality of input"):
|
||||
_init_nd_shape_and_axes(x, shape=None, axes=[-2])
|
||||
|
||||
with assert_raises(ValueError,
|
||||
match="all axes must be unique"):
|
||||
_init_nd_shape_and_axes(x, shape=None, axes=[0, 0])
|
||||
|
||||
with assert_raises(ValueError, match="shape must be a scalar or "
|
||||
"iterable of integers"):
|
||||
_init_nd_shape_and_axes(x, shape=[[1, 2], [3, 4]], axes=None)
|
||||
|
||||
with assert_raises(ValueError, match="shape must be a scalar or "
|
||||
"iterable of integers"):
|
||||
_init_nd_shape_and_axes(x, shape=[1., 2., 3., 4.], axes=None)
|
||||
|
||||
with assert_raises(ValueError,
|
||||
match="when given, axes and shape arguments"
|
||||
" have to be of the same length"):
|
||||
_init_nd_shape_and_axes(np.zeros([1, 1, 1, 1]),
|
||||
shape=[1, 2, 3], axes=[1])
|
||||
|
||||
with assert_raises(ValueError,
|
||||
match="invalid number of data points"
|
||||
r" \(\[0\]\) specified"):
|
||||
_init_nd_shape_and_axes(x, shape=[0], axes=None)
|
||||
|
||||
with assert_raises(ValueError,
|
||||
match="invalid number of data points"
|
||||
r" \(\[-2\]\) specified"):
|
||||
_init_nd_shape_and_axes(x, shape=-2, axes=None)
|
||||
@@ -0,0 +1,83 @@
|
||||
from scipy import fft
|
||||
import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_allclose
|
||||
import multiprocessing
|
||||
import os
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def x():
|
||||
return np.random.randn(512, 128) # Must be large enough to qualify for mt
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func", [
|
||||
fft.fft, fft.ifft, fft.fft2, fft.ifft2, fft.fftn, fft.ifftn,
|
||||
fft.rfft, fft.irfft, fft.rfft2, fft.irfft2, fft.rfftn, fft.irfftn,
|
||||
fft.hfft, fft.ihfft, fft.hfft2, fft.ihfft2, fft.hfftn, fft.ihfftn,
|
||||
fft.dct, fft.idct, fft.dctn, fft.idctn,
|
||||
fft.dst, fft.idst, fft.dstn, fft.idstn,
|
||||
])
|
||||
@pytest.mark.parametrize("workers", [2, -1])
|
||||
def test_threaded_same(x, func, workers):
|
||||
expected = func(x, workers=1)
|
||||
actual = func(x, workers=workers)
|
||||
assert_allclose(actual, expected)
|
||||
|
||||
|
||||
def _mt_fft(x):
|
||||
return fft.fft(x, workers=2)
|
||||
|
||||
|
||||
def test_mixed_threads_processes(x):
|
||||
# Test that the fft threadpool is safe to use before & after fork
|
||||
|
||||
expect = fft.fft(x, workers=2)
|
||||
|
||||
with multiprocessing.Pool(2) as p:
|
||||
res = p.map(_mt_fft, [x for _ in range(4)])
|
||||
|
||||
for r in res:
|
||||
assert_allclose(r, expect)
|
||||
|
||||
fft.fft(x, workers=2)
|
||||
|
||||
|
||||
def test_invalid_workers(x):
|
||||
cpus = os.cpu_count()
|
||||
|
||||
fft.ifft([1], workers=-cpus)
|
||||
|
||||
with pytest.raises(ValueError, match='workers must not be zero'):
|
||||
fft.fft(x, workers=0)
|
||||
|
||||
with pytest.raises(ValueError, match='workers value out of range'):
|
||||
fft.ifft(x, workers=-cpus-1)
|
||||
|
||||
|
||||
def test_set_get_workers():
|
||||
cpus = os.cpu_count()
|
||||
assert fft.get_workers() == 1
|
||||
with fft.set_workers(4):
|
||||
assert fft.get_workers() == 4
|
||||
|
||||
with fft.set_workers(-1):
|
||||
assert fft.get_workers() == cpus
|
||||
|
||||
assert fft.get_workers() == 4
|
||||
|
||||
assert fft.get_workers() == 1
|
||||
|
||||
with fft.set_workers(-cpus):
|
||||
assert fft.get_workers() == 1
|
||||
|
||||
|
||||
def test_set_workers_invalid():
|
||||
|
||||
with pytest.raises(ValueError, match='workers must not be zero'):
|
||||
with fft.set_workers(0):
|
||||
pass
|
||||
|
||||
with pytest.raises(ValueError, match='workers value out of range'):
|
||||
with fft.set_workers(-os.cpu_count()-1):
|
||||
pass
|
||||
@@ -0,0 +1,364 @@
|
||||
import queue
|
||||
import threading
|
||||
import multiprocessing
|
||||
import numpy as np
|
||||
import pytest
|
||||
from numpy.random import random
|
||||
from numpy.testing import (
|
||||
assert_array_almost_equal, assert_array_equal, assert_allclose
|
||||
)
|
||||
from pytest import raises as assert_raises
|
||||
import scipy.fft as fft
|
||||
|
||||
def fft1(x):
|
||||
L = len(x)
|
||||
phase = -2j*np.pi*(np.arange(L)/float(L))
|
||||
phase = np.arange(L).reshape(-1, 1) * phase
|
||||
return np.sum(x*np.exp(phase), axis=1)
|
||||
|
||||
|
||||
class TestFFTShift:
|
||||
|
||||
def test_fft_n(self):
|
||||
assert_raises(ValueError, fft.fft, [1, 2, 3], 0)
|
||||
|
||||
|
||||
class TestFFT1D:
|
||||
|
||||
def test_identity(self):
|
||||
maxlen = 512
|
||||
x = random(maxlen) + 1j*random(maxlen)
|
||||
xr = random(maxlen)
|
||||
for i in range(1,maxlen):
|
||||
assert_array_almost_equal(fft.ifft(fft.fft(x[0:i])), x[0:i],
|
||||
decimal=12)
|
||||
assert_array_almost_equal(fft.irfft(fft.rfft(xr[0:i]),i),
|
||||
xr[0:i], decimal=12)
|
||||
|
||||
def test_fft(self):
|
||||
x = random(30) + 1j*random(30)
|
||||
expect = fft1(x)
|
||||
assert_array_almost_equal(expect, fft.fft(x))
|
||||
assert_array_almost_equal(expect, fft.fft(x, norm="backward"))
|
||||
assert_array_almost_equal(expect / np.sqrt(30),
|
||||
fft.fft(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect / 30, fft.fft(x, norm="forward"))
|
||||
|
||||
def test_ifft(self):
|
||||
x = random(30) + 1j*random(30)
|
||||
assert_array_almost_equal(x, fft.ifft(fft.fft(x)))
|
||||
for norm in ["backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
x, fft.ifft(fft.fft(x, norm=norm), norm=norm))
|
||||
|
||||
def test_fft2(self):
|
||||
x = random((30, 20)) + 1j*random((30, 20))
|
||||
expect = fft.fft(fft.fft(x, axis=1), axis=0)
|
||||
assert_array_almost_equal(expect, fft.fft2(x))
|
||||
assert_array_almost_equal(expect, fft.fft2(x, norm="backward"))
|
||||
assert_array_almost_equal(expect / np.sqrt(30 * 20),
|
||||
fft.fft2(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect / (30 * 20),
|
||||
fft.fft2(x, norm="forward"))
|
||||
|
||||
def test_ifft2(self):
|
||||
x = random((30, 20)) + 1j*random((30, 20))
|
||||
expect = fft.ifft(fft.ifft(x, axis=1), axis=0)
|
||||
assert_array_almost_equal(expect, fft.ifft2(x))
|
||||
assert_array_almost_equal(expect, fft.ifft2(x, norm="backward"))
|
||||
assert_array_almost_equal(expect * np.sqrt(30 * 20),
|
||||
fft.ifft2(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect * (30 * 20),
|
||||
fft.ifft2(x, norm="forward"))
|
||||
|
||||
def test_fftn(self):
|
||||
x = random((30, 20, 10)) + 1j*random((30, 20, 10))
|
||||
expect = fft.fft(fft.fft(fft.fft(x, axis=2), axis=1), axis=0)
|
||||
assert_array_almost_equal(expect, fft.fftn(x))
|
||||
assert_array_almost_equal(expect, fft.fftn(x, norm="backward"))
|
||||
assert_array_almost_equal(expect / np.sqrt(30 * 20 * 10),
|
||||
fft.fftn(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect / (30 * 20 * 10),
|
||||
fft.fftn(x, norm="forward"))
|
||||
|
||||
def test_ifftn(self):
|
||||
x = random((30, 20, 10)) + 1j*random((30, 20, 10))
|
||||
expect = fft.ifft(fft.ifft(fft.ifft(x, axis=2), axis=1), axis=0)
|
||||
assert_array_almost_equal(expect, fft.ifftn(x))
|
||||
assert_array_almost_equal(expect, fft.ifftn(x, norm="backward"))
|
||||
assert_array_almost_equal(fft.ifftn(x) * np.sqrt(30 * 20 * 10),
|
||||
fft.ifftn(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect * (30 * 20 * 10),
|
||||
fft.ifftn(x, norm="forward"))
|
||||
|
||||
def test_rfft(self):
|
||||
x = random(29)
|
||||
for n in [x.size, 2*x.size]:
|
||||
for norm in [None, "backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
fft.fft(x, n=n, norm=norm)[:(n//2 + 1)],
|
||||
fft.rfft(x, n=n, norm=norm))
|
||||
assert_array_almost_equal(fft.rfft(x, n=n) / np.sqrt(n),
|
||||
fft.rfft(x, n=n, norm="ortho"))
|
||||
|
||||
def test_irfft(self):
|
||||
x = random(30)
|
||||
assert_array_almost_equal(x, fft.irfft(fft.rfft(x)))
|
||||
for norm in ["backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
x, fft.irfft(fft.rfft(x, norm=norm), norm=norm))
|
||||
|
||||
def test_rfft2(self):
|
||||
x = random((30, 20))
|
||||
expect = fft.fft2(x)[:, :11]
|
||||
assert_array_almost_equal(expect, fft.rfft2(x))
|
||||
assert_array_almost_equal(expect, fft.rfft2(x, norm="backward"))
|
||||
assert_array_almost_equal(expect / np.sqrt(30 * 20),
|
||||
fft.rfft2(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect / (30 * 20),
|
||||
fft.rfft2(x, norm="forward"))
|
||||
|
||||
def test_irfft2(self):
|
||||
x = random((30, 20))
|
||||
assert_array_almost_equal(x, fft.irfft2(fft.rfft2(x)))
|
||||
for norm in ["backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
x, fft.irfft2(fft.rfft2(x, norm=norm), norm=norm))
|
||||
|
||||
def test_rfftn(self):
|
||||
x = random((30, 20, 10))
|
||||
expect = fft.fftn(x)[:, :, :6]
|
||||
assert_array_almost_equal(expect, fft.rfftn(x))
|
||||
assert_array_almost_equal(expect, fft.rfftn(x, norm="backward"))
|
||||
assert_array_almost_equal(expect / np.sqrt(30 * 20 * 10),
|
||||
fft.rfftn(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect / (30 * 20 * 10),
|
||||
fft.rfftn(x, norm="forward"))
|
||||
|
||||
def test_irfftn(self):
|
||||
x = random((30, 20, 10))
|
||||
assert_array_almost_equal(x, fft.irfftn(fft.rfftn(x)))
|
||||
for norm in ["backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
x, fft.irfftn(fft.rfftn(x, norm=norm), norm=norm))
|
||||
|
||||
def test_hfft(self):
|
||||
x = random(14) + 1j*random(14)
|
||||
x_herm = np.concatenate((random(1), x, random(1)))
|
||||
x = np.concatenate((x_herm, x[::-1].conj()))
|
||||
expect = fft.fft(x)
|
||||
assert_array_almost_equal(expect, fft.hfft(x_herm))
|
||||
assert_array_almost_equal(expect, fft.hfft(x_herm, norm="backward"))
|
||||
assert_array_almost_equal(expect / np.sqrt(30),
|
||||
fft.hfft(x_herm, norm="ortho"))
|
||||
assert_array_almost_equal(expect / 30,
|
||||
fft.hfft(x_herm, norm="forward"))
|
||||
|
||||
def test_ihfft(self):
|
||||
x = random(14) + 1j*random(14)
|
||||
x_herm = np.concatenate((random(1), x, random(1)))
|
||||
x = np.concatenate((x_herm, x[::-1].conj()))
|
||||
assert_array_almost_equal(x_herm, fft.ihfft(fft.hfft(x_herm)))
|
||||
for norm in ["backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
x_herm, fft.ihfft(fft.hfft(x_herm, norm=norm), norm=norm))
|
||||
|
||||
def test_hfft2(self):
|
||||
x = random((30, 20))
|
||||
assert_array_almost_equal(x, fft.hfft2(fft.ihfft2(x)))
|
||||
for norm in ["backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
x, fft.hfft2(fft.ihfft2(x, norm=norm), norm=norm))
|
||||
|
||||
def test_ihfft2(self):
|
||||
x = random((30, 20))
|
||||
expect = fft.ifft2(x)[:, :11]
|
||||
assert_array_almost_equal(expect, fft.ihfft2(x))
|
||||
assert_array_almost_equal(expect, fft.ihfft2(x, norm="backward"))
|
||||
assert_array_almost_equal(expect * np.sqrt(30 * 20),
|
||||
fft.ihfft2(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect * (30 * 20),
|
||||
fft.ihfft2(x, norm="forward"))
|
||||
|
||||
def test_hfftn(self):
|
||||
x = random((30, 20, 10))
|
||||
assert_array_almost_equal(x, fft.hfftn(fft.ihfftn(x)))
|
||||
for norm in ["backward", "ortho", "forward"]:
|
||||
assert_array_almost_equal(
|
||||
x, fft.hfftn(fft.ihfftn(x, norm=norm), norm=norm))
|
||||
|
||||
def test_ihfftn(self):
|
||||
x = random((30, 20, 10))
|
||||
expect = fft.ifftn(x)[:, :, :6]
|
||||
assert_array_almost_equal(expect, fft.ihfftn(x))
|
||||
assert_array_almost_equal(expect, fft.ihfftn(x, norm="backward"))
|
||||
assert_array_almost_equal(expect * np.sqrt(30 * 20 * 10),
|
||||
fft.ihfftn(x, norm="ortho"))
|
||||
assert_array_almost_equal(expect * (30 * 20 * 10),
|
||||
fft.ihfftn(x, norm="forward"))
|
||||
|
||||
@pytest.mark.parametrize("op", [fft.fftn, fft.ifftn,
|
||||
fft.rfftn, fft.irfftn,
|
||||
fft.hfftn, fft.ihfftn])
|
||||
def test_axes(self, op):
|
||||
x = random((30, 20, 10))
|
||||
axes = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
|
||||
for a in axes:
|
||||
op_tr = op(np.transpose(x, a))
|
||||
tr_op = np.transpose(op(x, axes=a), a)
|
||||
assert_array_almost_equal(op_tr, tr_op)
|
||||
|
||||
@pytest.mark.parametrize("op", [fft.fft2, fft.ifft2,
|
||||
fft.rfft2, fft.irfft2,
|
||||
fft.hfft2, fft.ihfft2,
|
||||
fft.fftn, fft.ifftn,
|
||||
fft.rfftn, fft.irfftn,
|
||||
fft.hfftn, fft.ihfftn])
|
||||
def test_axes_subset_with_shape(self, op):
|
||||
x = random((16, 8, 4))
|
||||
axes = [(0, 1, 2), (0, 2, 1), (1, 2, 0)]
|
||||
for a in axes:
|
||||
# different shape on the first two axes
|
||||
shape = tuple([2*x.shape[ax] if ax in a[:2] else x.shape[ax]
|
||||
for ax in range(x.ndim)])
|
||||
# transform only the first two axes
|
||||
op_tr = op(np.transpose(x, a), s=shape[:2], axes=(0, 1))
|
||||
tr_op = np.transpose(op(x, s=shape[:2], axes=a[:2]), a)
|
||||
assert_array_almost_equal(op_tr, tr_op)
|
||||
|
||||
def test_all_1d_norm_preserving(self):
|
||||
# verify that round-trip transforms are norm-preserving
|
||||
x = random(30)
|
||||
x_norm = np.linalg.norm(x)
|
||||
n = x.size * 2
|
||||
func_pairs = [(fft.fft, fft.ifft),
|
||||
(fft.rfft, fft.irfft),
|
||||
# hfft: order so the first function takes x.size samples
|
||||
# (necessary for comparison to x_norm above)
|
||||
(fft.ihfft, fft.hfft),
|
||||
]
|
||||
for forw, back in func_pairs:
|
||||
for n in [x.size, 2*x.size]:
|
||||
for norm in ['backward', 'ortho', 'forward']:
|
||||
tmp = forw(x, n=n, norm=norm)
|
||||
tmp = back(tmp, n=n, norm=norm)
|
||||
assert_array_almost_equal(x_norm,
|
||||
np.linalg.norm(tmp))
|
||||
|
||||
@pytest.mark.parametrize("dtype", [np.half, np.single, np.double,
|
||||
np.longdouble])
|
||||
def test_dtypes(self, dtype):
|
||||
# make sure that all input precisions are accepted
|
||||
x = random(30).astype(dtype)
|
||||
assert_array_almost_equal(fft.ifft(fft.fft(x)), x)
|
||||
assert_array_almost_equal(fft.irfft(fft.rfft(x)), x)
|
||||
assert_array_almost_equal(fft.hfft(fft.ihfft(x), len(x)), x)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"dtype",
|
||||
[np.float32, np.float64, np.longfloat,
|
||||
np.complex64, np.complex128, np.longcomplex])
|
||||
@pytest.mark.parametrize("order", ["F", 'non-contiguous'])
|
||||
@pytest.mark.parametrize(
|
||||
"fft",
|
||||
[fft.fft, fft.fft2, fft.fftn,
|
||||
fft.ifft, fft.ifft2, fft.ifftn])
|
||||
def test_fft_with_order(dtype, order, fft):
|
||||
# Check that FFT/IFFT produces identical results for C, Fortran and
|
||||
# non contiguous arrays
|
||||
rng = np.random.RandomState(42)
|
||||
X = rng.rand(8, 7, 13).astype(dtype, copy=False)
|
||||
if order == 'F':
|
||||
Y = np.asfortranarray(X)
|
||||
else:
|
||||
# Make a non contiguous array
|
||||
Y = X[::-1]
|
||||
X = np.ascontiguousarray(X[::-1])
|
||||
|
||||
if fft.__name__.endswith('fft'):
|
||||
for axis in range(3):
|
||||
X_res = fft(X, axis=axis)
|
||||
Y_res = fft(Y, axis=axis)
|
||||
assert_array_almost_equal(X_res, Y_res)
|
||||
elif fft.__name__.endswith(('fft2', 'fftn')):
|
||||
axes = [(0, 1), (1, 2), (0, 2)]
|
||||
if fft.__name__.endswith('fftn'):
|
||||
axes.extend([(0,), (1,), (2,), None])
|
||||
for ax in axes:
|
||||
X_res = fft(X, axes=ax)
|
||||
Y_res = fft(Y, axes=ax)
|
||||
assert_array_almost_equal(X_res, Y_res)
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
|
||||
class TestFFTThreadSafe:
|
||||
threads = 16
|
||||
input_shape = (800, 200)
|
||||
|
||||
def _test_mtsame(self, func, *args):
|
||||
def worker(args, q):
|
||||
q.put(func(*args))
|
||||
|
||||
q = queue.Queue()
|
||||
expected = func(*args)
|
||||
|
||||
# Spin off a bunch of threads to call the same function simultaneously
|
||||
t = [threading.Thread(target=worker, args=(args, q))
|
||||
for i in range(self.threads)]
|
||||
[x.start() for x in t]
|
||||
|
||||
[x.join() for x in t]
|
||||
# Make sure all threads returned the correct value
|
||||
for i in range(self.threads):
|
||||
assert_array_equal(q.get(timeout=5), expected,
|
||||
'Function returned wrong value in multithreaded context')
|
||||
|
||||
def test_fft(self):
|
||||
a = np.ones(self.input_shape, dtype=np.complex128)
|
||||
self._test_mtsame(fft.fft, a)
|
||||
|
||||
def test_ifft(self):
|
||||
a = np.full(self.input_shape, 1+0j)
|
||||
self._test_mtsame(fft.ifft, a)
|
||||
|
||||
def test_rfft(self):
|
||||
a = np.ones(self.input_shape)
|
||||
self._test_mtsame(fft.rfft, a)
|
||||
|
||||
def test_irfft(self):
|
||||
a = np.full(self.input_shape, 1+0j)
|
||||
self._test_mtsame(fft.irfft, a)
|
||||
|
||||
def test_hfft(self):
|
||||
a = np.ones(self.input_shape, np.complex64)
|
||||
self._test_mtsame(fft.hfft, a)
|
||||
|
||||
def test_ihfft(self):
|
||||
a = np.ones(self.input_shape)
|
||||
self._test_mtsame(fft.ihfft, a)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func", [fft.fft, fft.ifft, fft.rfft, fft.irfft])
|
||||
def test_multiprocess(func):
|
||||
# Test that fft still works after fork (gh-10422)
|
||||
|
||||
with multiprocessing.Pool(2) as p:
|
||||
res = p.map(func, [np.ones(100) for _ in range(4)])
|
||||
|
||||
expect = func(np.ones(100))
|
||||
for x in res:
|
||||
assert_allclose(x, expect)
|
||||
|
||||
|
||||
class TestIRFFTN:
|
||||
|
||||
def test_not_last_axis_success(self):
|
||||
ar, ai = np.random.random((2, 16, 8, 32))
|
||||
a = ar + 1j*ai
|
||||
|
||||
axes = (-2,)
|
||||
|
||||
# Should not raise error
|
||||
fft.irfftn(a, axes=axes)
|
||||
@@ -0,0 +1,216 @@
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_array_equal
|
||||
import pytest
|
||||
|
||||
from scipy.fft import dct, idct, dctn, idctn, dst, idst, dstn, idstn
|
||||
import scipy.fft as fft
|
||||
from scipy import fftpack
|
||||
|
||||
import math
|
||||
SQRT_2 = math.sqrt(2)
|
||||
|
||||
# scipy.fft wraps the fftpack versions but with normalized inverse transforms.
|
||||
# So, the forward transforms and definitions are already thoroughly tested in
|
||||
# fftpack/test_real_transforms.py
|
||||
|
||||
|
||||
@pytest.mark.parametrize("forward, backward", [(dct, idct), (dst, idst)])
|
||||
@pytest.mark.parametrize("type", [1, 2, 3, 4])
|
||||
@pytest.mark.parametrize("n", [2, 3, 4, 5, 10, 16])
|
||||
@pytest.mark.parametrize("axis", [0, 1])
|
||||
@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward'])
|
||||
@pytest.mark.parametrize("orthogonalize", [False, True])
|
||||
def test_identity_1d(forward, backward, type, n, axis, norm, orthogonalize):
|
||||
# Test the identity f^-1(f(x)) == x
|
||||
x = np.random.rand(n, n)
|
||||
|
||||
y = forward(x, type, axis=axis, norm=norm, orthogonalize=orthogonalize)
|
||||
z = backward(y, type, axis=axis, norm=norm, orthogonalize=orthogonalize)
|
||||
assert_allclose(z, x)
|
||||
|
||||
pad = [(0, 0)] * 2
|
||||
pad[axis] = (0, 4)
|
||||
|
||||
y2 = np.pad(y, pad, mode='edge')
|
||||
z2 = backward(y2, type, n, axis, norm, orthogonalize=orthogonalize)
|
||||
assert_allclose(z2, x)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("forward, backward", [(dct, idct), (dst, idst)])
|
||||
@pytest.mark.parametrize("type", [1, 2, 3, 4])
|
||||
@pytest.mark.parametrize("dtype", [np.float16, np.float32, np.float64,
|
||||
np.complex64, np.complex128])
|
||||
@pytest.mark.parametrize("axis", [0, 1])
|
||||
@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward'])
|
||||
@pytest.mark.parametrize("overwrite_x", [True, False])
|
||||
def test_identity_1d_overwrite(forward, backward, type, dtype, axis, norm,
|
||||
overwrite_x):
|
||||
# Test the identity f^-1(f(x)) == x
|
||||
x = np.random.rand(7, 8)
|
||||
x_orig = x.copy()
|
||||
|
||||
y = forward(x, type, axis=axis, norm=norm, overwrite_x=overwrite_x)
|
||||
y_orig = y.copy()
|
||||
z = backward(y, type, axis=axis, norm=norm, overwrite_x=overwrite_x)
|
||||
if not overwrite_x:
|
||||
assert_allclose(z, x, rtol=1e-6, atol=1e-6)
|
||||
assert_array_equal(x, x_orig)
|
||||
assert_array_equal(y, y_orig)
|
||||
else:
|
||||
assert_allclose(z, x_orig, rtol=1e-6, atol=1e-6)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("forward, backward", [(dctn, idctn), (dstn, idstn)])
|
||||
@pytest.mark.parametrize("type", [1, 2, 3, 4])
|
||||
@pytest.mark.parametrize("shape, axes",
|
||||
[
|
||||
((4, 4), 0),
|
||||
((4, 4), 1),
|
||||
((4, 4), None),
|
||||
((4, 4), (0, 1)),
|
||||
((10, 12), None),
|
||||
((10, 12), (0, 1)),
|
||||
((4, 5, 6), None),
|
||||
((4, 5, 6), 1),
|
||||
((4, 5, 6), (0, 2)),
|
||||
])
|
||||
@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward'])
|
||||
@pytest.mark.parametrize("orthogonalize", [False, True])
|
||||
def test_identity_nd(forward, backward, type, shape, axes, norm,
|
||||
orthogonalize):
|
||||
# Test the identity f^-1(f(x)) == x
|
||||
|
||||
x = np.random.random(shape)
|
||||
|
||||
if axes is not None:
|
||||
shape = np.take(shape, axes)
|
||||
|
||||
y = forward(x, type, axes=axes, norm=norm, orthogonalize=orthogonalize)
|
||||
z = backward(y, type, axes=axes, norm=norm, orthogonalize=orthogonalize)
|
||||
assert_allclose(z, x)
|
||||
|
||||
if axes is None:
|
||||
pad = [(0, 4)] * x.ndim
|
||||
elif isinstance(axes, int):
|
||||
pad = [(0, 0)] * x.ndim
|
||||
pad[axes] = (0, 4)
|
||||
else:
|
||||
pad = [(0, 0)] * x.ndim
|
||||
|
||||
for a in axes:
|
||||
pad[a] = (0, 4)
|
||||
|
||||
y2 = np.pad(y, pad, mode='edge')
|
||||
z2 = backward(y2, type, shape, axes, norm, orthogonalize=orthogonalize)
|
||||
assert_allclose(z2, x)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("forward, backward", [(dctn, idctn), (dstn, idstn)])
|
||||
@pytest.mark.parametrize("type", [1, 2, 3, 4])
|
||||
@pytest.mark.parametrize("shape, axes",
|
||||
[
|
||||
((4, 5), 0),
|
||||
((4, 5), 1),
|
||||
((4, 5), None),
|
||||
])
|
||||
@pytest.mark.parametrize("dtype", [np.float16, np.float32, np.float64,
|
||||
np.complex64, np.complex128])
|
||||
@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward'])
|
||||
@pytest.mark.parametrize("overwrite_x", [False, True])
|
||||
def test_identity_nd_overwrite(forward, backward, type, shape, axes, dtype,
|
||||
norm, overwrite_x):
|
||||
# Test the identity f^-1(f(x)) == x
|
||||
|
||||
x = np.random.random(shape).astype(dtype)
|
||||
x_orig = x.copy()
|
||||
|
||||
if axes is not None:
|
||||
shape = np.take(shape, axes)
|
||||
|
||||
y = forward(x, type, axes=axes, norm=norm)
|
||||
y_orig = y.copy()
|
||||
z = backward(y, type, axes=axes, norm=norm)
|
||||
if overwrite_x:
|
||||
assert_allclose(z, x_orig, rtol=1e-6, atol=1e-6)
|
||||
else:
|
||||
assert_allclose(z, x, rtol=1e-6, atol=1e-6)
|
||||
assert_array_equal(x, x_orig)
|
||||
assert_array_equal(y, y_orig)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func", ['dct', 'dst', 'dctn', 'dstn'])
|
||||
@pytest.mark.parametrize("type", [1, 2, 3, 4])
|
||||
@pytest.mark.parametrize("norm", [None, 'backward', 'ortho', 'forward'])
|
||||
def test_fftpack_equivalience(func, type, norm):
|
||||
x = np.random.rand(8, 16)
|
||||
fft_res = getattr(fft, func)(x, type, norm=norm)
|
||||
fftpack_res = getattr(fftpack, func)(x, type, norm=norm)
|
||||
|
||||
assert_allclose(fft_res, fftpack_res)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("func", [dct, dst, dctn, dstn])
|
||||
@pytest.mark.parametrize("type", [1, 2, 3, 4])
|
||||
def test_orthogonalize_default(func, type):
|
||||
# Test orthogonalize is the default when norm="ortho", but not otherwise
|
||||
x = np.random.rand(100)
|
||||
|
||||
for norm, ortho in [
|
||||
("forward", False),
|
||||
("backward", False),
|
||||
("ortho", True),
|
||||
]:
|
||||
a = func(x, type=type, norm=norm, orthogonalize=ortho)
|
||||
b = func(x, type=type, norm=norm)
|
||||
assert_allclose(a, b)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"])
|
||||
@pytest.mark.parametrize("func, type", [
|
||||
(dct, 4), (dst, 1), (dst, 4)])
|
||||
def test_orthogonalize_noop(func, type, norm):
|
||||
# Transforms where orthogonalize is a no-op
|
||||
x = np.random.rand(100)
|
||||
y1 = func(x, type=type, norm=norm, orthogonalize=True)
|
||||
y2 = func(x, type=type, norm=norm, orthogonalize=False)
|
||||
assert_allclose(y1, y2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"])
|
||||
def test_orthogonalize_dct1(norm):
|
||||
x = np.random.rand(100)
|
||||
|
||||
x2 = x.copy()
|
||||
x2[0] *= SQRT_2
|
||||
x2[-1] *= SQRT_2
|
||||
|
||||
y1 = dct(x, type=1, norm=norm, orthogonalize=True)
|
||||
y2 = dct(x2, type=1, norm=norm, orthogonalize=False)
|
||||
|
||||
y2[0] /= SQRT_2
|
||||
y2[-1] /= SQRT_2
|
||||
assert_allclose(y1, y2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"])
|
||||
@pytest.mark.parametrize("func", [dct, dst])
|
||||
def test_orthogonalize_dcst2(func, norm):
|
||||
x = np.random.rand(100)
|
||||
y1 = func(x, type=2, norm=norm, orthogonalize=True)
|
||||
y2 = func(x, type=2, norm=norm, orthogonalize=False)
|
||||
|
||||
y2[0] /= SQRT_2
|
||||
assert_allclose(y1, y2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"])
|
||||
@pytest.mark.parametrize("func", [dct, dst])
|
||||
def test_orthogonalize_dcst3(func, norm):
|
||||
x = np.random.rand(100)
|
||||
x2 = x.copy()
|
||||
x2[0] *= SQRT_2
|
||||
|
||||
y1 = func(x, type=3, norm=norm, orthogonalize=True)
|
||||
y2 = func(x2, type=3, norm=norm, orthogonalize=False)
|
||||
assert_allclose(y1, y2)
|
||||
Reference in New Issue
Block a user