|
|
|
import numpy as np |
|
import pytest |
|
from scipy._lib._array_api import xp_assert_close |
|
|
|
from scipy.signal._spline import ( |
|
symiirorder1_ic, symiirorder2_ic_fwd, symiirorder2_ic_bwd) |
|
from scipy.signal import symiirorder1, symiirorder2 |
|
|
|
|
|
def _compute_symiirorder2_bwd_hs(k, cs, rsq, omega): |
|
cssq = cs * cs |
|
k = np.abs(k) |
|
rsupk = np.power(rsq, k / 2.0) |
|
|
|
c0 = (cssq * (1.0 + rsq) / (1.0 - rsq) / |
|
(1 - 2 * rsq * np.cos(2 * omega) + rsq * rsq)) |
|
gamma = (1.0 - rsq) / (1.0 + rsq) / np.tan(omega) |
|
return c0 * rsupk * (np.cos(omega * k) + gamma * np.sin(omega * k)) |
|
|
|
|
|
class TestSymIIR: |
|
@pytest.mark.parametrize( |
|
'dtype', [np.float32, np.float64, np.complex64, np.complex128]) |
|
@pytest.mark.parametrize('precision', [-1.0, 0.7, 0.5, 0.25, 0.0075]) |
|
def test_symiir1_ic(self, dtype, precision): |
|
c_precision = precision |
|
if precision <= 0.0 or precision > 1.0: |
|
if dtype in {np.float32, np.complex64}: |
|
c_precision = 1e-6 |
|
else: |
|
c_precision = 1e-11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b = 0.85 |
|
n_exp = int(np.ceil(np.log(c_precision) / np.log(b))) |
|
expected = np.asarray([[(1 - b ** n_exp) / (1 - b)]], dtype=dtype) |
|
expected = 1 + b * expected |
|
|
|
|
|
x = np.ones(n_exp + 1, dtype=dtype) |
|
xp_assert_close(symiirorder1_ic(x, b, precision), expected, |
|
atol=2e-6, rtol=2e-7) |
|
|
|
|
|
|
|
|
|
b_d = np.asarray(b, dtype=dtype) |
|
expected = np.asarray( |
|
[[(1 - (0.5 * b_d) ** n_exp) / (1 - (0.5 * b_d))]], dtype=dtype) |
|
expected = 1 + b_d * expected |
|
|
|
|
|
x = 2 ** -np.arange(n_exp + 1, dtype=dtype) |
|
xp_assert_close(symiirorder1_ic(x, b, precision), expected, |
|
atol=2e-6, rtol=2e-7) |
|
|
|
def test_symiir1_ic_fails(self): |
|
|
|
b = 0.85 |
|
|
|
x = np.ones(100, dtype=np.float64) |
|
|
|
|
|
precision = 1 / (1 - b) |
|
pytest.raises(ValueError, symiirorder1_ic, x, b, precision) |
|
|
|
|
|
pytest.raises(ValueError, symiirorder1_ic, x, 1.0, -1) |
|
pytest.raises(ValueError, symiirorder1_ic, x, 2.0, -1) |
|
|
|
@pytest.mark.parametrize( |
|
'dtype', [np.float32, np.float64, np.complex64, np.complex128]) |
|
@pytest.mark.parametrize('precision', [-1.0, 0.7, 0.5, 0.25, 0.0075]) |
|
def test_symiir1(self, dtype, precision): |
|
c_precision = precision |
|
if precision <= 0.0 or precision > 1.0: |
|
if dtype in {np.float32, np.complex64}: |
|
c_precision = 1e-6 |
|
else: |
|
c_precision = 1e-11 |
|
|
|
|
|
|
|
c0 = 0.15 |
|
z1 = 0.85 |
|
n = 200 |
|
signal = np.ones(n, dtype=dtype) |
|
|
|
|
|
|
|
n_exp = int(np.ceil(np.log(c_precision) / np.log(z1))) |
|
initial = np.asarray((1 - z1 ** n_exp) / (1 - z1), dtype=dtype) |
|
initial = 1 + z1 * initial |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pos = np.arange(n, dtype=dtype) |
|
comp1 = initial * z1**pos |
|
|
|
|
|
comp2 = np.zeros(n, dtype=dtype) |
|
comp2[1:] = -z1 / (1 - z1) * z1**pos[:-1] |
|
|
|
|
|
comp3 = np.zeros(n, dtype=dtype) |
|
comp3[1:] = 1 / (1 - z1) |
|
|
|
expected_fwd = comp1 + comp2 + comp3 |
|
|
|
|
|
sym_cond = -c0 / (z1 - 1.0) * expected_fwd[-1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exp_out = np.zeros(n, dtype=dtype) |
|
exp_out[0] = sym_cond |
|
|
|
for i in range(1, n): |
|
exp_out[i] = c0 * expected_fwd[n - 1 - i] + z1 * exp_out[i - 1] |
|
|
|
exp_out = exp_out[::-1] |
|
|
|
out = symiirorder1(signal, c0, z1, precision) |
|
xp_assert_close(out, exp_out, atol=4e-6, rtol=6e-7) |
|
|
|
@pytest.mark.parametrize('dtype', ['float32', 'float64']) |
|
def test_symiir1_values(self, dtype): |
|
rng = np.random.RandomState(1234) |
|
dtype = getattr(np, dtype) |
|
s = rng.uniform(size=16).astype(dtype) |
|
res = symiirorder1(s, 0.5, 0.1) |
|
|
|
|
|
exp_res = np.array([0.14387447, 0.35166047, 0.29735238, 0.46295986, 0.45174927, |
|
0.19982875, 0.20355805, 0.47378628, 0.57232247, 0.51597393, |
|
0.25935107, 0.31438554, 0.41096728, 0.4190693 , 0.25812255, |
|
0.33671467], dtype=res.dtype) |
|
assert res.dtype == dtype |
|
atol = {np.float64: 1e-15, np.float32: 1e-7}[dtype] |
|
xp_assert_close(res, exp_res, atol=atol) |
|
|
|
s = s + 1j*s |
|
res = symiirorder1(s, 0.5, 0.1) |
|
assert res.dtype == np.complex64 if dtype == np.float32 else np.complex128 |
|
xp_assert_close(res, exp_res + 1j*exp_res, atol=atol) |
|
|
|
@pytest.mark.parametrize( |
|
'dtype', ['float32', 'float64']) |
|
@pytest.mark.parametrize('precision', [-1.0, 0.7, 0.5, 0.25, 0.0075]) |
|
def test_symiir2_initial_fwd(self, dtype, precision): |
|
dtype = getattr(np, dtype) |
|
c_precision = precision |
|
if precision <= 0.0 or precision > 1.0: |
|
if dtype in {np.float32, np.complex64}: |
|
c_precision = 1e-6 |
|
else: |
|
c_precision = 1e-11 |
|
|
|
|
|
|
|
r = np.asarray(0.5, dtype=dtype) |
|
omega = np.asarray(np.pi / 3.0, dtype=dtype) |
|
cs = 1 - 2 * r * np.cos(omega) + r**2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
ub = np.ceil(np.log(c_precision / np.sin(omega)) / np.log(c_precision)) |
|
lb = np.ceil(np.pi / omega) - 2 |
|
n_exp = min(ub, lb) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fwd_initial_1 = ( |
|
cs + |
|
2 * r * np.cos(omega) - |
|
r**2 - |
|
r**(n_exp + 2) * np.sin(omega * (n_exp + 3)) / np.sin(omega) + |
|
r**(n_exp + 3) * np.sin(omega * (n_exp + 2)) / np.sin(omega)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ub = np.ceil(np.log(c_precision / np.sin(omega)) / np.log(c_precision)) |
|
lb = np.ceil(np.pi / omega) - 3 |
|
n_exp = min(ub, lb) |
|
|
|
fwd_initial_2 = ( |
|
cs + cs * 2 * r * np.cos(omega) + |
|
(r**2 * np.sin(3 * omega) - |
|
r**3 * np.sin(2 * omega) - |
|
r**(n_exp + 3) * np.sin(omega * (n_exp + 4)) + |
|
r**(n_exp + 4) * np.sin(omega * (n_exp + 3))) / np.sin(omega)) |
|
|
|
expected = np.r_[fwd_initial_1, fwd_initial_2][None, :] |
|
expected = expected.astype(dtype) |
|
|
|
n = 100 |
|
signal = np.ones(n, dtype=dtype) |
|
|
|
out = symiirorder2_ic_fwd(signal, r, omega, precision) |
|
xp_assert_close(out, expected, atol=4e-6, rtol=6e-7) |
|
|
|
@pytest.mark.parametrize( |
|
'dtype', [np.float32, np.float64]) |
|
@pytest.mark.parametrize('precision', [-1.0, 0.7, 0.5, 0.25, 0.0075]) |
|
def test_symiir2_initial_bwd(self, dtype, precision): |
|
c_precision = precision |
|
if precision <= 0.0 or precision > 1.0: |
|
if dtype in {np.float32, np.complex64}: |
|
c_precision = 1e-6 |
|
else: |
|
c_precision = 1e-11 |
|
|
|
r = np.asarray(0.5, dtype=dtype) |
|
omega = np.asarray(np.pi / 3.0, dtype=dtype) |
|
cs = 1 - 2 * r * np.cos(omega) + r * r |
|
a2 = 2 * r * np.cos(omega) |
|
a3 = -r * r |
|
|
|
n = 100 |
|
signal = np.ones(n, dtype=dtype) |
|
|
|
|
|
ic = symiirorder2_ic_fwd(signal, r, omega, precision) |
|
out = np.zeros(n + 2, dtype=dtype) |
|
out[:2] = ic[0] |
|
|
|
|
|
for i in range(2, n + 2): |
|
out[i] = cs * signal[i - 2] + a2 * out[i - 1] + a3 * out[i - 2] |
|
|
|
|
|
ic2 = np.zeros(2, dtype=dtype) |
|
idx = np.arange(n) |
|
|
|
diff = (_compute_symiirorder2_bwd_hs(idx, cs, r * r, omega) + |
|
_compute_symiirorder2_bwd_hs(idx + 1, cs, r * r, omega)) |
|
ic2_0_all = np.cumsum(diff * out[:1:-1]) |
|
pos = np.where(diff ** 2 < c_precision)[0] |
|
ic2[0] = ic2_0_all[pos[0]] |
|
|
|
diff = (_compute_symiirorder2_bwd_hs(idx - 1, cs, r * r, omega) + |
|
_compute_symiirorder2_bwd_hs(idx + 2, cs, r * r, omega)) |
|
ic2_1_all = np.cumsum(diff * out[:1:-1]) |
|
pos = np.where(diff ** 2 < c_precision)[0] |
|
ic2[1] = ic2_1_all[pos[0]] |
|
|
|
out_ic = symiirorder2_ic_bwd(out, r, omega, precision)[0] |
|
xp_assert_close(out_ic, ic2, atol=4e-6, rtol=6e-7) |
|
|
|
@pytest.mark.parametrize( |
|
'dtype', [np.float32, np.float64]) |
|
@pytest.mark.parametrize('precision', [-1.0, 0.7, 0.5, 0.25, 0.0075]) |
|
def test_symiir2(self, dtype, precision): |
|
r = np.asarray(0.5, dtype=dtype) |
|
omega = np.asarray(np.pi / 3.0, dtype=dtype) |
|
cs = 1 - 2 * r * np.cos(omega) + r * r |
|
a2 = 2 * r * np.cos(omega) |
|
a3 = -r * r |
|
|
|
n = 100 |
|
signal = np.ones(n, dtype=dtype) |
|
|
|
|
|
ic = symiirorder2_ic_fwd(signal, r, omega, precision) |
|
out1 = np.zeros(n + 2, dtype=dtype) |
|
out1[:2] = ic[0] |
|
|
|
|
|
for i in range(2, n + 2): |
|
out1[i] = cs * signal[i - 2] + a2 * out1[i - 1] + a3 * out1[i - 2] |
|
|
|
|
|
ic2 = symiirorder2_ic_bwd(out1, r, omega, precision)[0] |
|
|
|
|
|
exp = np.empty(n, dtype=dtype) |
|
exp[-2:] = ic2[::-1] |
|
|
|
for i in range(n - 3, -1, -1): |
|
exp[i] = cs * out1[i] + a2 * exp[i + 1] + a3 * exp[i + 2] |
|
|
|
out = symiirorder2(signal, r, omega, precision) |
|
xp_assert_close(out, exp, atol=4e-6, rtol=6e-7) |
|
|
|
@pytest.mark.parametrize('dtyp', ['float32', 'float64']) |
|
def test_symiir2_values(self, dtyp): |
|
dtyp = getattr(np, dtyp) |
|
rng = np.random.RandomState(1234) |
|
s = rng.uniform(size=16).astype(dtyp) |
|
res = symiirorder2(s, 0.1, 0.1, precision=1e-10) |
|
|
|
|
|
exp_res = np.array([0.26572609, 0.53408018, 0.51032696, 0.72115829, 0.69486885, |
|
0.3649055 , 0.37349478, 0.74165032, 0.89718521, 0.80582483, |
|
0.46758053, 0.51898709, 0.65025605, 0.65394321, 0.45273595, |
|
0.53539183], dtype=dtyp) |
|
|
|
assert res.dtype == dtyp |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xp_assert_close(res, exp_res, atol=2e-6) |
|
|
|
s = s + 1j*s |
|
with pytest.raises(TypeError): |
|
res = symiirorder2(s, 0.5, 0.1) |
|
|
|
def test_symiir1_integer_input(self): |
|
s = np.where(np.arange(100) % 2, -1, 1) |
|
expected = symiirorder1(s.astype(float), 0.5, 0.5) |
|
out = symiirorder1(s, 0.5, 0.5) |
|
xp_assert_close(out, expected) |
|
|
|
def test_symiir2_integer_input(self): |
|
s = np.where(np.arange(100) % 2, -1, 1) |
|
expected = symiirorder2(s.astype(float), 0.5, np.pi / 3.0) |
|
out = symiirorder2(s, 0.5, np.pi / 3.0) |
|
xp_assert_close(out, expected) |
|
|