up follow livre
This commit is contained in:
parent
70a5c3465c
commit
cffb31c1ef
12198 changed files with 2562132 additions and 35 deletions
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.
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.
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.
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.
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.
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.
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.
Binary file not shown.
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
#cython: language_level=3
|
||||
#cython: boundscheck=False
|
||||
#cython: wraparound=False
|
||||
|
||||
from scipy.special.cython_special cimport beta, gamma
|
||||
|
||||
cpdef double cy_beta(double a, double b):
|
||||
return beta(a, b)
|
||||
|
||||
cpdef double complex cy_gamma(double complex z):
|
||||
return gamma(z)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
project('random-build-examples', 'c', 'cpp', 'cython')
|
||||
|
||||
fs = import('fs')
|
||||
|
||||
py3 = import('python').find_installation(pure: false)
|
||||
|
||||
cy = meson.get_compiler('cython')
|
||||
|
||||
if not cy.version().version_compare('>=3.0.8')
|
||||
error('tests requires Cython >= 3.0.8')
|
||||
endif
|
||||
|
||||
cython_args = []
|
||||
if cy.version().version_compare('>=3.1.0')
|
||||
cython_args += ['-Xfreethreading_compatible=True']
|
||||
endif
|
||||
|
||||
py3.extension_module(
|
||||
'extending',
|
||||
'extending.pyx',
|
||||
install: false,
|
||||
cython_args: cython_args,
|
||||
c_args: ['-DCYTHON_CCOMPLEX=0'] # see gh-18975 for why we need this
|
||||
)
|
||||
|
||||
extending_cpp = fs.copyfile('extending.pyx', 'extending_cpp.pyx')
|
||||
py3.extension_module(
|
||||
'extending_cpp',
|
||||
extending_cpp,
|
||||
install: false,
|
||||
override_options : ['cython_language=cpp'],
|
||||
cython_args: cython_args,
|
||||
cpp_args: ['-DCYTHON_CCOMPLEX=0']
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4815
venv/lib/python3.13/site-packages/scipy/special/tests/test_basic.py
Normal file
4815
venv/lib/python3.13/site-packages/scipy/special/tests/test_basic.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,112 @@
|
|||
import numpy as np
|
||||
import scipy.special as sc
|
||||
import pytest
|
||||
from numpy.testing import assert_allclose, assert_array_equal, suppress_warnings
|
||||
|
||||
|
||||
class TestBdtr:
|
||||
def test(self):
|
||||
val = sc.bdtr(0, 1, 0.5)
|
||||
assert_allclose(val, 0.5)
|
||||
|
||||
def test_sum_is_one(self):
|
||||
val = sc.bdtr([0, 1, 2], 2, 0.5)
|
||||
assert_array_equal(val, [0.25, 0.75, 1.0])
|
||||
|
||||
def test_rounding(self):
|
||||
double_val = sc.bdtr([0.1, 1.1, 2.1], 2, 0.5)
|
||||
int_val = sc.bdtr([0, 1, 2], 2, 0.5)
|
||||
assert_array_equal(double_val, int_val)
|
||||
|
||||
@pytest.mark.parametrize('k, n, p', [
|
||||
(np.inf, 2, 0.5),
|
||||
(1.0, np.inf, 0.5),
|
||||
(1.0, 2, np.inf)
|
||||
])
|
||||
def test_inf(self, k, n, p):
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(DeprecationWarning)
|
||||
val = sc.bdtr(k, n, p)
|
||||
assert np.isnan(val)
|
||||
|
||||
def test_domain(self):
|
||||
val = sc.bdtr(-1.1, 1, 0.5)
|
||||
assert np.isnan(val)
|
||||
|
||||
|
||||
class TestBdtrc:
|
||||
def test_value(self):
|
||||
val = sc.bdtrc(0, 1, 0.5)
|
||||
assert_allclose(val, 0.5)
|
||||
|
||||
def test_sum_is_one(self):
|
||||
val = sc.bdtrc([0, 1, 2], 2, 0.5)
|
||||
assert_array_equal(val, [0.75, 0.25, 0.0])
|
||||
|
||||
def test_rounding(self):
|
||||
double_val = sc.bdtrc([0.1, 1.1, 2.1], 2, 0.5)
|
||||
int_val = sc.bdtrc([0, 1, 2], 2, 0.5)
|
||||
assert_array_equal(double_val, int_val)
|
||||
|
||||
@pytest.mark.parametrize('k, n, p', [
|
||||
(np.inf, 2, 0.5),
|
||||
(1.0, np.inf, 0.5),
|
||||
(1.0, 2, np.inf)
|
||||
])
|
||||
def test_inf(self, k, n, p):
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(DeprecationWarning)
|
||||
val = sc.bdtrc(k, n, p)
|
||||
assert np.isnan(val)
|
||||
|
||||
def test_domain(self):
|
||||
val = sc.bdtrc(-1.1, 1, 0.5)
|
||||
val2 = sc.bdtrc(2.1, 1, 0.5)
|
||||
assert np.isnan(val2)
|
||||
assert_allclose(val, 1.0)
|
||||
|
||||
def test_bdtr_bdtrc_sum_to_one(self):
|
||||
bdtr_vals = sc.bdtr([0, 1, 2], 2, 0.5)
|
||||
bdtrc_vals = sc.bdtrc([0, 1, 2], 2, 0.5)
|
||||
vals = bdtr_vals + bdtrc_vals
|
||||
assert_allclose(vals, [1.0, 1.0, 1.0])
|
||||
|
||||
|
||||
class TestBdtri:
|
||||
def test_value(self):
|
||||
val = sc.bdtri(0, 1, 0.5)
|
||||
assert_allclose(val, 0.5)
|
||||
|
||||
def test_sum_is_one(self):
|
||||
val = sc.bdtri([0, 1], 2, 0.5)
|
||||
actual = np.asarray([1 - 1/np.sqrt(2), 1/np.sqrt(2)])
|
||||
assert_allclose(val, actual)
|
||||
|
||||
def test_rounding(self):
|
||||
double_val = sc.bdtri([0.1, 1.1], 2, 0.5)
|
||||
int_val = sc.bdtri([0, 1], 2, 0.5)
|
||||
assert_allclose(double_val, int_val)
|
||||
|
||||
@pytest.mark.parametrize('k, n, p', [
|
||||
(np.inf, 2, 0.5),
|
||||
(1.0, np.inf, 0.5),
|
||||
(1.0, 2, np.inf)
|
||||
])
|
||||
def test_inf(self, k, n, p):
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(DeprecationWarning)
|
||||
val = sc.bdtri(k, n, p)
|
||||
assert np.isnan(val)
|
||||
|
||||
@pytest.mark.parametrize('k, n, p', [
|
||||
(-1.1, 1, 0.5),
|
||||
(2.1, 1, 0.5)
|
||||
])
|
||||
def test_domain(self, k, n, p):
|
||||
val = sc.bdtri(k, n, p)
|
||||
assert np.isnan(val)
|
||||
|
||||
def test_bdtr_bdtri_roundtrip(self):
|
||||
bdtr_vals = sc.bdtr([0, 1, 2], 2, 0.5)
|
||||
roundtrip_vals = sc.bdtri([0, 1, 2], 2, bdtr_vals)
|
||||
assert_allclose(roundtrip_vals, [0.5, 0.5, np.nan])
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import scipy.special._ufuncs as scu
|
||||
from scipy.integrate import tanhsinh
|
||||
|
||||
|
||||
type_char_to_type_tol = {'f': (np.float32, 32*np.finfo(np.float32).eps),
|
||||
'd': (np.float64, 32*np.finfo(np.float64).eps)}
|
||||
|
||||
|
||||
# Each item in this list is
|
||||
# (func, args, expected_value)
|
||||
# All the values can be represented exactly, even with np.float32.
|
||||
#
|
||||
# This is not an exhaustive test data set of all the functions!
|
||||
# It is a spot check of several functions, primarily for
|
||||
# checking that the different data types are handled correctly.
|
||||
test_data = [
|
||||
(scu._beta_pdf, (0.5, 2, 3), 1.5),
|
||||
(scu._beta_pdf, (0, 1, 5), 5.0),
|
||||
(scu._beta_pdf, (1, 5, 1), 5.0),
|
||||
(scu._beta_ppf, (0.5, 5., 5.), 0.5), # gh-21303
|
||||
(scu._binom_cdf, (1, 3, 0.5), 0.5),
|
||||
(scu._binom_pmf, (1, 4, 0.5), 0.25),
|
||||
(scu._hypergeom_cdf, (2, 3, 5, 6), 0.5),
|
||||
(scu._nbinom_cdf, (1, 4, 0.25), 0.015625),
|
||||
(scu._ncf_mean, (10, 12, 2.5), 1.5),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func, args, expected', test_data)
|
||||
def test_stats_boost_ufunc(func, args, expected):
|
||||
type_sigs = func.types
|
||||
type_chars = [sig.split('->')[-1] for sig in type_sigs]
|
||||
for type_char in type_chars:
|
||||
typ, rtol = type_char_to_type_tol[type_char]
|
||||
args = [typ(arg) for arg in args]
|
||||
# Harmless overflow warnings are a "feature" of some wrappers on some
|
||||
# platforms. This test is about dtype and accuracy, so let's avoid false
|
||||
# test failures cause by these warnings. See gh-17432.
|
||||
with np.errstate(over='ignore'):
|
||||
value = func(*args)
|
||||
assert isinstance(value, typ)
|
||||
assert_allclose(value, expected, rtol=rtol)
|
||||
|
||||
|
||||
def test_landau():
|
||||
# Test that Landau distribution ufuncs are wrapped as expected;
|
||||
# accuracy is tested by Boost.
|
||||
x = np.linspace(-3, 10, 10)
|
||||
args = (0, 1)
|
||||
res = tanhsinh(lambda x: scu._landau_pdf(x, *args), -np.inf, x)
|
||||
cdf = scu._landau_cdf(x, *args)
|
||||
assert_allclose(res.integral, cdf)
|
||||
sf = scu._landau_sf(x, *args)
|
||||
assert_allclose(sf, 1-cdf)
|
||||
ppf = scu._landau_ppf(cdf, *args)
|
||||
assert_allclose(ppf, x)
|
||||
isf = scu._landau_isf(sf, *args)
|
||||
assert_allclose(isf, x, rtol=1e-6)
|
||||
|
||||
def test_gh22956():
|
||||
_ = scu._ncx2_pdf(30, 1e307, 16)
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_almost_equal, assert_allclose
|
||||
from scipy.special import boxcox, boxcox1p, inv_boxcox, inv_boxcox1p
|
||||
import pytest
|
||||
|
||||
|
||||
# There are more tests of boxcox and boxcox1p in test_mpmath.py.
|
||||
|
||||
def test_boxcox_basic():
|
||||
x = np.array([0.5, 1, 2, 4])
|
||||
|
||||
# lambda = 0 => y = log(x)
|
||||
y = boxcox(x, 0)
|
||||
assert_almost_equal(y, np.log(x))
|
||||
|
||||
# lambda = 1 => y = x - 1
|
||||
y = boxcox(x, 1)
|
||||
assert_almost_equal(y, x - 1)
|
||||
|
||||
# lambda = 2 => y = 0.5*(x**2 - 1)
|
||||
y = boxcox(x, 2)
|
||||
assert_almost_equal(y, 0.5*(x**2 - 1))
|
||||
|
||||
# x = 0 and lambda > 0 => y = -1 / lambda
|
||||
lam = np.array([0.5, 1, 2])
|
||||
y = boxcox(0, lam)
|
||||
assert_almost_equal(y, -1.0 / lam)
|
||||
|
||||
def test_boxcox_underflow():
|
||||
x = 1 + 1e-15
|
||||
lmbda = 1e-306
|
||||
y = boxcox(x, lmbda)
|
||||
assert_allclose(y, np.log(x), rtol=1e-14)
|
||||
|
||||
|
||||
def test_boxcox_nonfinite():
|
||||
# x < 0 => y = nan
|
||||
x = np.array([-1, -1, -0.5])
|
||||
y = boxcox(x, [0.5, 2.0, -1.5])
|
||||
assert_equal(y, np.array([np.nan, np.nan, np.nan]))
|
||||
|
||||
# x = 0 and lambda <= 0 => y = -inf
|
||||
x = 0
|
||||
y = boxcox(x, [-2.5, 0])
|
||||
assert_equal(y, np.array([-np.inf, -np.inf]))
|
||||
|
||||
|
||||
def test_boxcox1p_basic():
|
||||
x = np.array([-0.25, -1e-20, 0, 1e-20, 0.25, 1, 3])
|
||||
|
||||
# lambda = 0 => y = log(1+x)
|
||||
y = boxcox1p(x, 0)
|
||||
assert_almost_equal(y, np.log1p(x))
|
||||
|
||||
# lambda = 1 => y = x
|
||||
y = boxcox1p(x, 1)
|
||||
assert_almost_equal(y, x)
|
||||
|
||||
# lambda = 2 => y = 0.5*((1+x)**2 - 1) = 0.5*x*(2 + x)
|
||||
y = boxcox1p(x, 2)
|
||||
assert_almost_equal(y, 0.5*x*(2 + x))
|
||||
|
||||
# x = -1 and lambda > 0 => y = -1 / lambda
|
||||
lam = np.array([0.5, 1, 2])
|
||||
y = boxcox1p(-1, lam)
|
||||
assert_almost_equal(y, -1.0 / lam)
|
||||
|
||||
|
||||
def test_boxcox1p_underflow():
|
||||
x = np.array([1e-15, 1e-306])
|
||||
lmbda = np.array([1e-306, 1e-18])
|
||||
y = boxcox1p(x, lmbda)
|
||||
assert_allclose(y, np.log1p(x), rtol=1e-14)
|
||||
|
||||
|
||||
def test_boxcox1p_nonfinite():
|
||||
# x < -1 => y = nan
|
||||
x = np.array([-2, -2, -1.5])
|
||||
y = boxcox1p(x, [0.5, 2.0, -1.5])
|
||||
assert_equal(y, np.array([np.nan, np.nan, np.nan]))
|
||||
|
||||
# x = -1 and lambda <= 0 => y = -inf
|
||||
x = -1
|
||||
y = boxcox1p(x, [-2.5, 0])
|
||||
assert_equal(y, np.array([-np.inf, -np.inf]))
|
||||
|
||||
|
||||
def test_inv_boxcox():
|
||||
x = np.array([0., 1., 2.])
|
||||
lam = np.array([0., 1., 2.])
|
||||
y = boxcox(x, lam)
|
||||
x2 = inv_boxcox(y, lam)
|
||||
assert_almost_equal(x, x2)
|
||||
|
||||
x = np.array([0., 1., 2.])
|
||||
lam = np.array([0., 1., 2.])
|
||||
y = boxcox1p(x, lam)
|
||||
x2 = inv_boxcox1p(y, lam)
|
||||
assert_almost_equal(x, x2)
|
||||
|
||||
|
||||
def test_inv_boxcox1p_underflow():
|
||||
x = 1e-15
|
||||
lam = 1e-306
|
||||
y = inv_boxcox1p(x, lam)
|
||||
assert_allclose(y, x, rtol=1e-14)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"x, lmb",
|
||||
[[100, 155],
|
||||
[0.01, -155]]
|
||||
)
|
||||
def test_boxcox_premature_overflow(x, lmb):
|
||||
# test boxcox & inv_boxcox
|
||||
y = boxcox(x, lmb)
|
||||
assert np.isfinite(y)
|
||||
x_inv = inv_boxcox(y, lmb)
|
||||
assert_allclose(x, x_inv)
|
||||
|
||||
# test boxcox1p & inv_boxcox1p
|
||||
y1p = boxcox1p(x-1, lmb)
|
||||
assert np.isfinite(y1p)
|
||||
x1p_inv = inv_boxcox1p(y1p, lmb)
|
||||
assert_allclose(x-1, x1p_inv)
|
||||
|
|
@ -0,0 +1,712 @@
|
|||
"""
|
||||
Test cdflib functions versus mpmath, if available.
|
||||
|
||||
The following functions still need tests:
|
||||
|
||||
- ncfdtridfn
|
||||
- ncfdtridfd
|
||||
- ncfdtrinc
|
||||
- nbdtrik
|
||||
- nbdtrin
|
||||
- pdtrik
|
||||
- nctdtridf
|
||||
- nctdtrinc
|
||||
|
||||
"""
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_allclose
|
||||
import pytest
|
||||
|
||||
import scipy.special as sp
|
||||
from scipy.special._testutils import (
|
||||
MissingModule, check_version, FuncData)
|
||||
from scipy.special._mptestutils import (
|
||||
Arg, IntArg, get_args, mpf2float, assert_mpmath_equal)
|
||||
|
||||
try:
|
||||
import mpmath
|
||||
except ImportError:
|
||||
mpmath = MissingModule('mpmath')
|
||||
|
||||
|
||||
class ProbArg:
|
||||
"""Generate a set of probabilities on [0, 1]."""
|
||||
|
||||
def __init__(self):
|
||||
# Include the endpoints for compatibility with Arg et. al.
|
||||
self.a = 0
|
||||
self.b = 1
|
||||
|
||||
def values(self, n):
|
||||
"""Return an array containing approximately n numbers."""
|
||||
m = max(1, n//3)
|
||||
v1 = np.logspace(-30, np.log10(0.3), m)
|
||||
v2 = np.linspace(0.3, 0.7, m + 1, endpoint=False)[1:]
|
||||
v3 = 1 - np.logspace(np.log10(0.3), -15, m)
|
||||
v = np.r_[v1, v2, v3]
|
||||
return np.unique(v)
|
||||
|
||||
|
||||
class EndpointFilter:
|
||||
def __init__(self, a, b, rtol, atol):
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.rtol = rtol
|
||||
self.atol = atol
|
||||
|
||||
def __call__(self, x):
|
||||
mask1 = np.abs(x - self.a) < self.rtol*np.abs(self.a) + self.atol
|
||||
mask2 = np.abs(x - self.b) < self.rtol*np.abs(self.b) + self.atol
|
||||
return np.where(mask1 | mask2, False, True)
|
||||
|
||||
|
||||
class _CDFData:
|
||||
def __init__(self, spfunc, mpfunc, index, argspec, spfunc_first=True,
|
||||
dps=20, n=5000, rtol=None, atol=None,
|
||||
endpt_rtol=None, endpt_atol=None):
|
||||
self.spfunc = spfunc
|
||||
self.mpfunc = mpfunc
|
||||
self.index = index
|
||||
self.argspec = argspec
|
||||
self.spfunc_first = spfunc_first
|
||||
self.dps = dps
|
||||
self.n = n
|
||||
self.rtol = rtol
|
||||
self.atol = atol
|
||||
|
||||
if not isinstance(argspec, list):
|
||||
self.endpt_rtol = None
|
||||
self.endpt_atol = None
|
||||
elif endpt_rtol is not None or endpt_atol is not None:
|
||||
if isinstance(endpt_rtol, list):
|
||||
self.endpt_rtol = endpt_rtol
|
||||
else:
|
||||
self.endpt_rtol = [endpt_rtol]*len(self.argspec)
|
||||
if isinstance(endpt_atol, list):
|
||||
self.endpt_atol = endpt_atol
|
||||
else:
|
||||
self.endpt_atol = [endpt_atol]*len(self.argspec)
|
||||
else:
|
||||
self.endpt_rtol = None
|
||||
self.endpt_atol = None
|
||||
|
||||
def idmap(self, *args):
|
||||
if self.spfunc_first:
|
||||
res = self.spfunc(*args)
|
||||
if np.isnan(res):
|
||||
return np.nan
|
||||
args = list(args)
|
||||
args[self.index] = res
|
||||
with mpmath.workdps(self.dps):
|
||||
res = self.mpfunc(*tuple(args))
|
||||
# Imaginary parts are spurious
|
||||
res = mpf2float(res.real)
|
||||
else:
|
||||
with mpmath.workdps(self.dps):
|
||||
res = self.mpfunc(*args)
|
||||
res = mpf2float(res.real)
|
||||
args = list(args)
|
||||
args[self.index] = res
|
||||
res = self.spfunc(*tuple(args))
|
||||
return res
|
||||
|
||||
def get_param_filter(self):
|
||||
if self.endpt_rtol is None and self.endpt_atol is None:
|
||||
return None
|
||||
|
||||
filters = []
|
||||
for rtol, atol, spec in zip(self.endpt_rtol, self.endpt_atol, self.argspec):
|
||||
if rtol is None and atol is None:
|
||||
filters.append(None)
|
||||
continue
|
||||
elif rtol is None:
|
||||
rtol = 0.0
|
||||
elif atol is None:
|
||||
atol = 0.0
|
||||
|
||||
filters.append(EndpointFilter(spec.a, spec.b, rtol, atol))
|
||||
return filters
|
||||
|
||||
def check(self):
|
||||
# Generate values for the arguments
|
||||
args = get_args(self.argspec, self.n)
|
||||
param_filter = self.get_param_filter()
|
||||
param_columns = tuple(range(args.shape[1]))
|
||||
result_columns = args.shape[1]
|
||||
args = np.hstack((args, args[:, self.index].reshape(args.shape[0], 1)))
|
||||
FuncData(self.idmap, args,
|
||||
param_columns=param_columns, result_columns=result_columns,
|
||||
rtol=self.rtol, atol=self.atol, vectorized=False,
|
||||
param_filter=param_filter).check()
|
||||
|
||||
|
||||
def _assert_inverts(*a, **kw):
|
||||
d = _CDFData(*a, **kw)
|
||||
d.check()
|
||||
|
||||
|
||||
def _binomial_cdf(k, n, p):
|
||||
k, n, p = mpmath.mpf(k), mpmath.mpf(n), mpmath.mpf(p)
|
||||
if k <= 0:
|
||||
return mpmath.mpf(0)
|
||||
elif k >= n:
|
||||
return mpmath.mpf(1)
|
||||
|
||||
onemp = mpmath.fsub(1, p, exact=True)
|
||||
return mpmath.betainc(n - k, k + 1, x2=onemp, regularized=True)
|
||||
|
||||
|
||||
def _f_cdf(dfn, dfd, x):
|
||||
if x < 0:
|
||||
return mpmath.mpf(0)
|
||||
dfn, dfd, x = mpmath.mpf(dfn), mpmath.mpf(dfd), mpmath.mpf(x)
|
||||
ub = dfn*x/(dfn*x + dfd)
|
||||
res = mpmath.betainc(dfn/2, dfd/2, x2=ub, regularized=True)
|
||||
return res
|
||||
|
||||
|
||||
def _student_t_cdf(df, t, dps=None):
|
||||
if dps is None:
|
||||
dps = mpmath.mp.dps
|
||||
with mpmath.workdps(dps):
|
||||
df, t = mpmath.mpf(df), mpmath.mpf(t)
|
||||
fac = mpmath.hyp2f1(0.5, 0.5*(df + 1), 1.5, -t**2/df)
|
||||
fac *= t*mpmath.gamma(0.5*(df + 1))
|
||||
fac /= mpmath.sqrt(mpmath.pi*df)*mpmath.gamma(0.5*df)
|
||||
return 0.5 + fac
|
||||
|
||||
|
||||
def _noncentral_chi_pdf(t, df, nc):
|
||||
res = mpmath.besseli(df/2 - 1, mpmath.sqrt(nc*t))
|
||||
res *= mpmath.exp(-(t + nc)/2)*(t/nc)**(df/4 - 1/2)/2
|
||||
return res
|
||||
|
||||
|
||||
def _noncentral_chi_cdf(x, df, nc, dps=None):
|
||||
if dps is None:
|
||||
dps = mpmath.mp.dps
|
||||
x, df, nc = mpmath.mpf(x), mpmath.mpf(df), mpmath.mpf(nc)
|
||||
with mpmath.workdps(dps):
|
||||
res = mpmath.quad(lambda t: _noncentral_chi_pdf(t, df, nc), [0, x])
|
||||
return res
|
||||
|
||||
|
||||
def _tukey_lmbda_quantile(p, lmbda):
|
||||
# For lmbda != 0
|
||||
return (p**lmbda - (1 - p)**lmbda)/lmbda
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@check_version(mpmath, '0.19')
|
||||
class TestCDFlib:
|
||||
|
||||
@pytest.mark.xfail(run=False)
|
||||
def test_bdtrik(self):
|
||||
_assert_inverts(
|
||||
sp.bdtrik,
|
||||
_binomial_cdf,
|
||||
0, [ProbArg(), IntArg(1, 1000), ProbArg()],
|
||||
rtol=1e-4)
|
||||
|
||||
def test_bdtrin(self):
|
||||
_assert_inverts(
|
||||
sp.bdtrin,
|
||||
_binomial_cdf,
|
||||
1, [IntArg(1, 1000), ProbArg(), ProbArg()],
|
||||
rtol=1e-4, endpt_atol=[None, None, 1e-6])
|
||||
|
||||
def test_btdtria(self):
|
||||
_assert_inverts(
|
||||
sp.btdtria,
|
||||
lambda a, b, x: mpmath.betainc(a, b, x2=x, regularized=True),
|
||||
0, [ProbArg(), Arg(0, 1e2, inclusive_a=False),
|
||||
Arg(0, 1, inclusive_a=False, inclusive_b=False)],
|
||||
rtol=1e-6)
|
||||
|
||||
def test_btdtrib(self):
|
||||
# Use small values of a or mpmath doesn't converge
|
||||
_assert_inverts(
|
||||
sp.btdtrib,
|
||||
lambda a, b, x: mpmath.betainc(a, b, x2=x, regularized=True),
|
||||
1,
|
||||
[Arg(0, 1e2, inclusive_a=False), ProbArg(),
|
||||
Arg(0, 1, inclusive_a=False, inclusive_b=False)],
|
||||
rtol=1e-7,
|
||||
endpt_atol=[None, 1e-18, 1e-15])
|
||||
|
||||
@pytest.mark.xfail(run=False)
|
||||
def test_fdtridfd(self):
|
||||
_assert_inverts(
|
||||
sp.fdtridfd,
|
||||
_f_cdf,
|
||||
1,
|
||||
[IntArg(1, 100), ProbArg(), Arg(0, 100, inclusive_a=False)],
|
||||
rtol=1e-7)
|
||||
|
||||
def test_gdtria(self):
|
||||
_assert_inverts(
|
||||
sp.gdtria,
|
||||
lambda a, b, x: mpmath.gammainc(b, b=a*x, regularized=True),
|
||||
0,
|
||||
[ProbArg(), Arg(0, 1e3, inclusive_a=False),
|
||||
Arg(0, 1e4, inclusive_a=False)],
|
||||
rtol=1e-7,
|
||||
endpt_atol=[None, 1e-7, 1e-10])
|
||||
|
||||
def test_gdtrib(self):
|
||||
# Use small values of a and x or mpmath doesn't converge
|
||||
_assert_inverts(
|
||||
sp.gdtrib,
|
||||
lambda a, b, x: mpmath.gammainc(b, b=a*x, regularized=True),
|
||||
1,
|
||||
[Arg(0, 1e2, inclusive_a=False), ProbArg(),
|
||||
Arg(0, 1e3, inclusive_a=False)],
|
||||
rtol=1e-5)
|
||||
|
||||
def test_gdtrix(self):
|
||||
_assert_inverts(
|
||||
sp.gdtrix,
|
||||
lambda a, b, x: mpmath.gammainc(b, b=a*x, regularized=True),
|
||||
2,
|
||||
[Arg(0, 1e3, inclusive_a=False), Arg(0, 1e3, inclusive_a=False),
|
||||
ProbArg()],
|
||||
rtol=1e-7,
|
||||
endpt_atol=[None, 1e-7, 1e-10])
|
||||
|
||||
# Overall nrdtrimn and nrdtrisd are not performing well with infeasible/edge
|
||||
# combinations of sigma and x, hence restricted the domains to still use the
|
||||
# testing machinery, also see gh-20069
|
||||
|
||||
# nrdtrimn signature: p, sd, x
|
||||
# nrdtrisd signature: mn, p, x
|
||||
def test_nrdtrimn(self):
|
||||
_assert_inverts(
|
||||
sp.nrdtrimn,
|
||||
lambda x, y, z: mpmath.ncdf(z, x, y),
|
||||
0,
|
||||
[ProbArg(), # CDF value p
|
||||
Arg(0.1, np.inf, inclusive_a=False, inclusive_b=False), # sigma
|
||||
Arg(-1e10, 1e10)], # x
|
||||
rtol=1e-5)
|
||||
|
||||
def test_nrdtrisd(self):
|
||||
_assert_inverts(
|
||||
sp.nrdtrisd,
|
||||
lambda x, y, z: mpmath.ncdf(z, x, y),
|
||||
1,
|
||||
[Arg(-np.inf, 10, inclusive_a=False, inclusive_b=False), # mn
|
||||
ProbArg(), # CDF value p
|
||||
Arg(10, 1e100)], # x
|
||||
rtol=1e-5)
|
||||
|
||||
def test_stdtr(self):
|
||||
# Ideally the left endpoint for Arg() should be 0.
|
||||
assert_mpmath_equal(
|
||||
sp.stdtr,
|
||||
_student_t_cdf,
|
||||
[IntArg(1, 100), Arg(1e-10, np.inf)], rtol=1e-7)
|
||||
|
||||
@pytest.mark.xfail(run=False)
|
||||
def test_stdtridf(self):
|
||||
_assert_inverts(
|
||||
sp.stdtridf,
|
||||
_student_t_cdf,
|
||||
0, [ProbArg(), Arg()], rtol=1e-7)
|
||||
|
||||
def test_stdtrit(self):
|
||||
_assert_inverts(
|
||||
sp.stdtrit,
|
||||
_student_t_cdf,
|
||||
1, [IntArg(1, 100), ProbArg()], rtol=1e-7,
|
||||
endpt_atol=[None, 1e-10])
|
||||
|
||||
def test_chdtriv(self):
|
||||
_assert_inverts(
|
||||
sp.chdtriv,
|
||||
lambda v, x: mpmath.gammainc(v/2, b=x/2, regularized=True),
|
||||
0, [ProbArg(), IntArg(1, 100)], rtol=1e-4)
|
||||
|
||||
@pytest.mark.xfail(run=False)
|
||||
def test_chndtridf(self):
|
||||
# Use a larger atol since mpmath is doing numerical integration
|
||||
_assert_inverts(
|
||||
sp.chndtridf,
|
||||
_noncentral_chi_cdf,
|
||||
1, [Arg(0, 100, inclusive_a=False), ProbArg(),
|
||||
Arg(0, 100, inclusive_a=False)],
|
||||
n=1000, rtol=1e-4, atol=1e-15)
|
||||
|
||||
@pytest.mark.xfail(run=False)
|
||||
def test_chndtrinc(self):
|
||||
# Use a larger atol since mpmath is doing numerical integration
|
||||
_assert_inverts(
|
||||
sp.chndtrinc,
|
||||
_noncentral_chi_cdf,
|
||||
2, [Arg(0, 100, inclusive_a=False), IntArg(1, 100), ProbArg()],
|
||||
n=1000, rtol=1e-4, atol=1e-15)
|
||||
|
||||
def test_chndtrix(self):
|
||||
# Use a larger atol since mpmath is doing numerical integration
|
||||
_assert_inverts(
|
||||
sp.chndtrix,
|
||||
_noncentral_chi_cdf,
|
||||
0, [ProbArg(), IntArg(1, 100), Arg(0, 100, inclusive_a=False)],
|
||||
n=1000, rtol=1e-4, atol=1e-15,
|
||||
endpt_atol=[1e-6, None, None])
|
||||
|
||||
def test_tklmbda_zero_shape(self):
|
||||
# When lmbda = 0 the CDF has a simple closed form
|
||||
one = mpmath.mpf(1)
|
||||
assert_mpmath_equal(
|
||||
lambda x: sp.tklmbda(x, 0),
|
||||
lambda x: one/(mpmath.exp(-x) + one),
|
||||
[Arg()], rtol=1e-7)
|
||||
|
||||
def test_tklmbda_neg_shape(self):
|
||||
_assert_inverts(
|
||||
sp.tklmbda,
|
||||
_tukey_lmbda_quantile,
|
||||
0, [ProbArg(), Arg(-25, 0, inclusive_b=False)],
|
||||
spfunc_first=False, rtol=1e-5,
|
||||
endpt_atol=[1e-9, 1e-5])
|
||||
|
||||
@pytest.mark.xfail(run=False)
|
||||
def test_tklmbda_pos_shape(self):
|
||||
_assert_inverts(
|
||||
sp.tklmbda,
|
||||
_tukey_lmbda_quantile,
|
||||
0, [ProbArg(), Arg(0, 100, inclusive_a=False)],
|
||||
spfunc_first=False, rtol=1e-5)
|
||||
|
||||
# The values of lmdba are chosen so that 1/lmbda is exact.
|
||||
@pytest.mark.parametrize('lmbda', [0.5, 1.0, 8.0])
|
||||
def test_tklmbda_lmbda1(self, lmbda):
|
||||
bound = 1/lmbda
|
||||
assert_equal(sp.tklmbda([-bound, bound], lmbda), [0.0, 1.0])
|
||||
|
||||
|
||||
funcs = [
|
||||
("btdtria", 3),
|
||||
("btdtrib", 3),
|
||||
("bdtrik", 3),
|
||||
("bdtrin", 3),
|
||||
("chdtriv", 2),
|
||||
("chndtr", 3),
|
||||
("chndtrix", 3),
|
||||
("chndtridf", 3),
|
||||
("chndtrinc", 3),
|
||||
("fdtridfd", 3),
|
||||
("ncfdtr", 4),
|
||||
("ncfdtri", 4),
|
||||
("ncfdtridfn", 4),
|
||||
("ncfdtridfd", 4),
|
||||
("ncfdtrinc", 4),
|
||||
("gdtrix", 3),
|
||||
("gdtrib", 3),
|
||||
("gdtria", 3),
|
||||
("nbdtrik", 3),
|
||||
("nbdtrin", 3),
|
||||
("nrdtrimn", 3),
|
||||
("nrdtrisd", 3),
|
||||
("pdtrik", 2),
|
||||
("stdtr", 2),
|
||||
("stdtrit", 2),
|
||||
("stdtridf", 2),
|
||||
("nctdtr", 3),
|
||||
("nctdtrit", 3),
|
||||
("nctdtridf", 3),
|
||||
("nctdtrinc", 3),
|
||||
("tklmbda", 2),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func,numargs', funcs, ids=[x[0] for x in funcs])
|
||||
def test_nonfinite(func, numargs):
|
||||
|
||||
rng = np.random.default_rng(1701299355559735)
|
||||
func = getattr(sp, func)
|
||||
args_choices = [(float(x), np.nan, np.inf, -np.inf) for x in rng.random(numargs)]
|
||||
|
||||
for args in itertools.product(*args_choices):
|
||||
res = func(*args)
|
||||
|
||||
if any(np.isnan(x) for x in args):
|
||||
# Nan inputs should result to nan output
|
||||
assert_equal(res, np.nan)
|
||||
else:
|
||||
# All other inputs should return something (but not
|
||||
# raise exceptions or cause hangs)
|
||||
pass
|
||||
|
||||
|
||||
def test_chndtrix_gh2158():
|
||||
# test that gh-2158 is resolved; previously this blew up
|
||||
res = sp.chndtrix(0.999999, 2, np.arange(20.)+1e-6)
|
||||
|
||||
# Generated in R
|
||||
# options(digits=16)
|
||||
# ncp <- seq(0, 19) + 1e-6
|
||||
# print(qchisq(0.999999, df = 2, ncp = ncp))
|
||||
res_exp = [27.63103493142305, 35.25728589950540, 39.97396073236288,
|
||||
43.88033702110538, 47.35206403482798, 50.54112500166103,
|
||||
53.52720257322766, 56.35830042867810, 59.06600769498512,
|
||||
61.67243118946381, 64.19376191277179, 66.64228141346548,
|
||||
69.02756927200180, 71.35726934749408, 73.63759723904816,
|
||||
75.87368842650227, 78.06984431185720, 80.22971052389806,
|
||||
82.35640899964173, 84.45263768373256]
|
||||
assert_allclose(res, res_exp)
|
||||
|
||||
|
||||
def test_nctdtrinc_gh19896():
|
||||
# test that gh-19896 is resolved.
|
||||
# Compared to SciPy 1.11 results from Fortran code.
|
||||
dfarr = [0.001, 0.98, 9.8, 98, 980, 10000, 98, 9.8, 0.98, 0.001]
|
||||
parr = [0.001, 0.1, 0.3, 0.8, 0.999, 0.001, 0.1, 0.3, 0.8, 0.999]
|
||||
tarr = [0.0015, 0.15, 1.5, 15, 300, 0.0015, 0.15, 1.5, 15, 300]
|
||||
desired = [3.090232306168629, 1.406141304556198, 2.014225177124157,
|
||||
13.727067118283456, 278.9765683871208, 3.090232306168629,
|
||||
1.4312427877936222, 2.014225177124157, 3.712743137978295,
|
||||
-3.086951096691082]
|
||||
actual = sp.nctdtrinc(dfarr, parr, tarr)
|
||||
assert_allclose(actual, desired, rtol=5e-12, atol=0.0)
|
||||
|
||||
|
||||
def test_stdtr_stdtrit_neg_inf():
|
||||
# -inf was treated as +inf and values from the normal were returned
|
||||
assert np.all(np.isnan(sp.stdtr(-np.inf, [-np.inf, -1.0, 0.0, 1.0, np.inf])))
|
||||
assert np.all(np.isnan(sp.stdtrit(-np.inf, [0.0, 0.25, 0.5, 0.75, 1.0])))
|
||||
|
||||
|
||||
def test_bdtrik_nbdtrik_inf():
|
||||
y = np.array(
|
||||
[np.nan,-np.inf,-10.0, -1.0, 0.0, .00001, .5, 0.9999, 1.0, 10.0, np.inf])
|
||||
y = y[:,None]
|
||||
p = np.atleast_2d(
|
||||
[np.nan, -np.inf, -10.0, -1.0, 0.0, .00001, .5, 1.0, np.inf])
|
||||
assert np.all(np.isnan(sp.bdtrik(y, np.inf, p)))
|
||||
assert np.all(np.isnan(sp.nbdtrik(y, np.inf, p)))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"dfn,dfd,nc,f,expected_cdf",
|
||||
[[100.0, 0.1, 0.1, 100.0, 0.29787396410092676],
|
||||
[100.0, 100.0, 0.01, 0.1, 4.4344737598690424e-26],
|
||||
[100.0, 0.01, 0.1, 0.01, 0.002848616633080384],
|
||||
[10.0, 0.01, 1.0, 0.1, 0.012339557729057956],
|
||||
[100.0, 100.0, 0.01, 0.01, 1.8926477420964936e-72],
|
||||
[1.0, 100.0, 100.0, 0.1, 1.7925940526821304e-22],
|
||||
[1.0, 0.01, 100.0, 10.0, 0.012334711965024968],
|
||||
[1.0, 0.01, 10.0, 0.01, 0.00021944525290299],
|
||||
[10.0, 1.0, 0.1, 100.0, 0.9219345555070705],
|
||||
[0.1, 0.1, 1.0, 1.0, 0.3136335813423239],
|
||||
[100.0, 100.0, 0.1, 10.0, 1.0],
|
||||
[1.0, 0.1, 100.0, 10.0, 0.02926064279680897]]
|
||||
)
|
||||
def test_ncfdtr_ncfdtri(dfn, dfd, nc, f, expected_cdf):
|
||||
# Reference values computed with mpmath with the following script
|
||||
#
|
||||
# import numpy as np
|
||||
#
|
||||
# from mpmath import mp
|
||||
# from scipy.special import ncfdtr
|
||||
#
|
||||
# mp.dps = 100
|
||||
#
|
||||
# def mp_ncfdtr(dfn, dfd, nc, f):
|
||||
# # Uses formula 26.2.20 from Abramowitz and Stegun.
|
||||
# dfn, dfd, nc, f = map(mp.mpf, (dfn, dfd, nc, f))
|
||||
# def term(j):
|
||||
# result = mp.exp(-nc/2)*(nc/2)**j / mp.factorial(j)
|
||||
# result *= mp.betainc(
|
||||
# dfn/2 + j, dfd/2, 0, f*dfn/(f*dfn + dfd), regularized=True
|
||||
# )
|
||||
# return result
|
||||
# result = mp.nsum(term, [0, mp.inf])
|
||||
# return float(result)
|
||||
#
|
||||
# dfn = np.logspace(-2, 2, 5)
|
||||
# dfd = np.logspace(-2, 2, 5)
|
||||
# nc = np.logspace(-2, 2, 5)
|
||||
# f = np.logspace(-2, 2, 5)
|
||||
#
|
||||
# dfn, dfd, nc, f = np.meshgrid(dfn, dfd, nc, f)
|
||||
# dfn, dfd, nc, f = map(np.ravel, (dfn, dfd, nc, f))
|
||||
#
|
||||
# cases = []
|
||||
# re = []
|
||||
# for x0, x1, x2, x3 in zip(*(dfn, dfd, nc, f)):
|
||||
# observed = ncfdtr(x0, x1, x2, x3)
|
||||
# expected = mp_ncfdtr(x0, x1, x2, x3)
|
||||
# cases.append((x0, x1, x2, x3, expected))
|
||||
# re.append((abs(expected - observed)/abs(expected)))
|
||||
#
|
||||
# assert np.max(re) < 1e-13
|
||||
#
|
||||
# rng = np.random.default_rng(1234)
|
||||
# sample_idx = rng.choice(len(re), replace=False, size=12)
|
||||
# cases = np.array(cases)[sample_idx].tolist()
|
||||
assert_allclose(sp.ncfdtr(dfn, dfd, nc, f), expected_cdf, rtol=1e-13, atol=0)
|
||||
# testing tails where the CDF reaches 0 or 1 does not make sense for inverses
|
||||
# of a CDF as they are not bijective in these regions
|
||||
if 0 < expected_cdf < 1:
|
||||
assert_allclose(sp.ncfdtri(dfn, dfd, nc, expected_cdf), f, rtol=5e-11)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"args",
|
||||
[(-1.0, 0.1, 0.1, 0.5),
|
||||
(1, -1.0, 0.1, 0.5),
|
||||
(1, 1, -1.0, 0.5),
|
||||
(1, 1, 1, 100),
|
||||
(1, 1, 1, -1)]
|
||||
)
|
||||
def test_ncfdtri_domain_error(args):
|
||||
with sp.errstate(domain="raise"):
|
||||
with pytest.raises(sp.SpecialFunctionError, match="domain"):
|
||||
sp.ncfdtri(*args)
|
||||
|
||||
class TestNoncentralTFunctions:
|
||||
|
||||
# Reference values computed with mpmath with the following script
|
||||
# Formula from:
|
||||
# Lenth, Russell V (1989). "Algorithm AS 243: Cumulative Distribution Function
|
||||
# of the Non-central t Distribution". Journal of the Royal Statistical Society,
|
||||
# Series C. 38 (1): 185-189
|
||||
#
|
||||
# Warning: may take a long time to run
|
||||
#
|
||||
# from mpmath import mp
|
||||
# mp.dps = 400
|
||||
|
||||
# def nct_cdf(df, nc, x):
|
||||
# df, nc, x = map(mp.mpf, (df, nc, x))
|
||||
|
||||
# def f(df, nc, x):
|
||||
# phi = mp.ncdf(-nc)
|
||||
# y = x * x / (x * x + df)
|
||||
# constant = mp.exp(-nc * nc / 2.)
|
||||
# def term(j):
|
||||
# intermediate = constant * (nc *nc / 2.)**j
|
||||
# p = intermediate/mp.factorial(j)
|
||||
# q = nc / (mp.sqrt(2.) * mp.gamma(j + 1.5)) * intermediate
|
||||
# first_beta_term = mp.betainc(j + 0.5, df/2., x2=y,
|
||||
# regularized=True)
|
||||
# second_beta_term = mp.betainc(j + mp.one, df/2., x2=y,
|
||||
# regularized=True)
|
||||
# return p * first_beta_term + q * second_beta_term
|
||||
|
||||
# sum_term = mp.nsum(term, [0, mp.inf])
|
||||
# f = phi + 0.5 * sum_term
|
||||
# return f
|
||||
|
||||
# if x >= 0:
|
||||
# result = f(df, nc, x)
|
||||
# else:
|
||||
# result = mp.one - f(df, -nc, x)
|
||||
# return float(result)
|
||||
|
||||
@pytest.mark.parametrize("df, nc, x, expected_cdf", [
|
||||
(0.98, -3.8, 0.0015, 0.9999279987514815),
|
||||
(0.98, -3.8, 0.15, 0.9999528361700505),
|
||||
(0.98, -3.8, 1.5, 0.9999908823016942),
|
||||
(0.98, -3.8, 15, 0.9999990264591945),
|
||||
(0.98, 0.38, 0.0015, 0.35241533122693),
|
||||
(0.98, 0.38, 0.15, 0.39749697267146983),
|
||||
(0.98, 0.38, 1.5, 0.716862963488558),
|
||||
(0.98, 0.38, 15, 0.9656246449257494),
|
||||
(0.98, 3.8, 0.0015, 7.26973354942293e-05),
|
||||
(0.98, 3.8, 0.15, 0.00012416481147589105),
|
||||
(0.98, 3.8, 1.5, 0.035388035775454095),
|
||||
(0.98, 3.8, 15, 0.7954826975430583),
|
||||
(0.98, 38, 0.0015, 3.02106943e-316),
|
||||
(0.98, 38, 0.15, 6.069970616996603e-309),
|
||||
(0.98, 38, 1.5, 2.591995360483094e-97),
|
||||
(0.98, 38, 15, 0.011927265886910935),
|
||||
(9.8, -3.8, 0.0015, 0.9999280776192786),
|
||||
(9.8, -3.8, 0.15, 0.9999599410685442),
|
||||
(9.8, -3.8, 1.5, 0.9999997432394788),
|
||||
(9.8, -3.8, 15, 0.9999999999999984),
|
||||
(9.8, 0.38, 0.0015, 0.3525155979107491),
|
||||
(9.8, 0.38, 0.15, 0.40763120140379194),
|
||||
(9.8, 0.38, 1.5, 0.8476794017024651),
|
||||
(9.8, 0.38, 15, 0.9999999297116268),
|
||||
(9.8, 3.8, 0.0015, 7.277620328149153e-05),
|
||||
(9.8, 3.8, 0.15, 0.00013024802220900652),
|
||||
(9.8, 3.8, 1.5, 0.013477432800072933),
|
||||
(9.8, 3.8, 15, 0.999850151230648),
|
||||
(9.8, 38, 0.0015, 3.05066095e-316),
|
||||
(9.8, 38, 0.15, 1.79065514676e-313),
|
||||
(9.8, 38, 1.5, 2.0935940165900746e-249),
|
||||
(9.8, 38, 15, 2.252076291604796e-09),
|
||||
(98, -3.8, 0.0015, 0.9999280875149109),
|
||||
(98, -3.8, 0.15, 0.9999608250170452),
|
||||
(98, -3.8, 1.5, 0.9999999304757682),
|
||||
(98, -3.8, 15, 1.0),
|
||||
(98, 0.38, 0.0015, 0.35252817848596313),
|
||||
(98, 0.38, 0.15, 0.40890253001794846),
|
||||
(98, 0.38, 1.5, 0.8664672830006552),
|
||||
(98, 0.38, 15, 1.0),
|
||||
(98, 3.8, 0.0015, 7.278609891281275e-05),
|
||||
(98, 3.8, 0.15, 0.0001310318674827004),
|
||||
(98, 3.8, 1.5, 0.010990879189991727),
|
||||
(98, 3.8, 15, 0.9999999999999989),
|
||||
(98, 38, 0.0015, 3.05437385e-316),
|
||||
(98, 38, 0.15, 9.1668336166e-314),
|
||||
(98, 38, 1.5, 1.8085884236563926e-288),
|
||||
(98, 38, 15, 2.7740532792035907e-50),
|
||||
(980, -3.8, 0.0015, 0.9999280885188965),
|
||||
(980, -3.8, 0.15, 0.9999609144559273),
|
||||
(980, -3.8, 1.5, 0.9999999410050979),
|
||||
(980, -3.8, 15, 1.0),
|
||||
(980, 0.38, 0.0015, 0.3525294548792812),
|
||||
(980, 0.38, 0.15, 0.4090315324657382),
|
||||
(980, 0.38, 1.5, 0.8684247068517293),
|
||||
(980, 0.38, 15, 1.0),
|
||||
(980, 3.8, 0.0015, 7.278710289828983e-05),
|
||||
(980, 3.8, 0.15, 0.00013111131667906573),
|
||||
(980, 3.8, 1.5, 0.010750678886113882),
|
||||
(980, 3.8, 15, 1.0),
|
||||
(980, 38, 0.0015, 3.0547506e-316),
|
||||
(980, 38, 0.15, 8.6191646313e-314),
|
||||
pytest.param(980, 38, 1.5, 1.1824454111413493e-291,
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Bug in underlying Boost math implementation")),
|
||||
(980, 38, 15, 5.407535300713606e-105)
|
||||
])
|
||||
def test_gh19896(self, df, nc, x, expected_cdf):
|
||||
# test that gh-19896 is resolved.
|
||||
# Originally this was a regression test that used the old Fortran results
|
||||
# as a reference. The Fortran results were not accurate, so the reference
|
||||
# values were recomputed with mpmath.
|
||||
nctdtr_result = sp.nctdtr(df, nc, x)
|
||||
assert_allclose(nctdtr_result, expected_cdf, rtol=1e-13, atol=1e-303)
|
||||
|
||||
def test_nctdtr_gh8344(self):
|
||||
# test that gh-8344 is resolved.
|
||||
df, nc, x = 3000, 3, 0.1
|
||||
expected = 0.0018657780826323328
|
||||
assert_allclose(sp.nctdtr(df, nc, x), expected, rtol=1e-14)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"df, nc, x, expected, rtol",
|
||||
[[3., 5., -2., 1.5645373999149622e-09, 5e-9],
|
||||
[1000., 10., 1., 1.1493552133826623e-19, 1e-13],
|
||||
[1e-5, -6., 2., 0.9999999990135003, 1e-13],
|
||||
[10., 20., 0.15, 6.426530505957303e-88, 1e-13],
|
||||
[1., 1., np.inf, 1.0, 0.0],
|
||||
[1., 1., -np.inf, 0.0, 0.0]
|
||||
]
|
||||
)
|
||||
def test_nctdtr_accuracy(self, df, nc, x, expected, rtol):
|
||||
assert_allclose(sp.nctdtr(df, nc, x), expected, rtol=rtol)
|
||||
|
||||
@pytest.mark.parametrize("df, nc, x, expected_cdf", [
|
||||
(0.98, 38, 1.5, 2.591995360483094e-97),
|
||||
(3000, 3, 0.1, 0.0018657780826323328),
|
||||
(0.98, -3.8, 15, 0.9999990264591945),
|
||||
(9.8, 38, 15, 2.252076291604796e-09),
|
||||
|
||||
])
|
||||
def test_nctdtrit(self, df, nc, x, expected_cdf):
|
||||
assert_allclose(sp.nctdtrit(df, nc, expected_cdf), x, rtol=1e-10)
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# gh-14777 regression tests
|
||||
# Test stdtr and stdtrit with infinite df and large values of df
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
from scipy.special import stdtr, stdtrit, ndtr, ndtri
|
||||
|
||||
|
||||
def test_stdtr_vs_R_large_df():
|
||||
df = [1e10, 1e12, 1e120, np.inf]
|
||||
t = 1.
|
||||
res = stdtr(df, t)
|
||||
# R Code:
|
||||
# options(digits=20)
|
||||
# pt(1., c(1e10, 1e12, 1e120, Inf))
|
||||
res_R = [0.84134474605644460343,
|
||||
0.84134474606842180044,
|
||||
0.84134474606854281475,
|
||||
0.84134474606854292578]
|
||||
assert_allclose(res, res_R, rtol=2e-15)
|
||||
# last value should also agree with ndtr
|
||||
assert_equal(res[3], ndtr(1.))
|
||||
|
||||
|
||||
def test_stdtrit_vs_R_large_df():
|
||||
df = [1e10, 1e12, 1e120, np.inf]
|
||||
p = 0.1
|
||||
res = stdtrit(df, p)
|
||||
# R Code:
|
||||
# options(digits=20)
|
||||
# qt(0.1, c(1e10, 1e12, 1e120, Inf))
|
||||
res_R = [-1.2815515656292593150,
|
||||
-1.2815515655454472466,
|
||||
-1.2815515655446008125,
|
||||
-1.2815515655446008125]
|
||||
assert_allclose(res, res_R, rtol=1e-14, atol=1e-15)
|
||||
# last value should also agree with ndtri
|
||||
assert_equal(res[3], ndtri(0.1))
|
||||
|
||||
|
||||
def test_stdtr_stdtri_invalid():
|
||||
# a mix of large and inf df with t/p equal to nan
|
||||
df = [1e10, 1e12, 1e120, np.inf]
|
||||
x = np.nan
|
||||
res1 = stdtr(df, x)
|
||||
res2 = stdtrit(df, x)
|
||||
res_ex = 4*[np.nan]
|
||||
assert_equal(res1, res_ex)
|
||||
assert_equal(res2, res_ex)
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import pytest
|
||||
import numpy as np
|
||||
from scipy.special._ufuncs import (
|
||||
_smirnovc, _smirnovci, _smirnovp,
|
||||
_struve_asymp_large_z, _struve_bessel_series, _struve_power_series,
|
||||
bdtr, bdtrc, bdtri, expn, kn, nbdtr, nbdtrc, nbdtri, pdtri,
|
||||
smirnov, smirnovi, yn
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# For each ufunc here, verify that the default integer type, np.intp,
|
||||
# can be safely cast to the integer type found in the input type signatures.
|
||||
# For this particular set of functions, the code expects to find just one
|
||||
# integer type among the input signatures.
|
||||
#
|
||||
@pytest.mark.parametrize(
|
||||
'ufunc',
|
||||
[_smirnovc, _smirnovci, _smirnovp,
|
||||
_struve_asymp_large_z, _struve_bessel_series, _struve_power_series,
|
||||
bdtr, bdtrc, bdtri, expn, kn, nbdtr, nbdtrc, nbdtri, pdtri,
|
||||
smirnov, smirnovi, yn],
|
||||
)
|
||||
def test_intp_safe_cast(ufunc):
|
||||
int_chars = {'i', 'l', 'q'}
|
||||
int_input = [set(sig.split('->')[0]) & int_chars for sig in ufunc.types]
|
||||
int_char = ''.join(s.pop() if s else '' for s in int_input)
|
||||
assert len(int_char) == 1, "More integer types in the signatures than expected"
|
||||
assert np.can_cast(np.intp, np.dtype(int_char))
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import pytest
|
||||
from scipy.special._ufuncs import _cosine_cdf, _cosine_invcdf
|
||||
|
||||
|
||||
# These values are (x, p) where p is the expected exact value of
|
||||
# _cosine_cdf(x). These values will be tested for exact agreement.
|
||||
_coscdf_exact = [
|
||||
(-4.0, 0.0),
|
||||
(0, 0.5),
|
||||
(np.pi, 1.0),
|
||||
(4.0, 1.0),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("x, expected", _coscdf_exact)
|
||||
def test_cosine_cdf_exact(x, expected):
|
||||
assert _cosine_cdf(x) == expected
|
||||
|
||||
|
||||
# These values are (x, p), where p is the expected value of
|
||||
# _cosine_cdf(x). The expected values were computed with mpmath using
|
||||
# 50 digits of precision. These values will be tested for agreement
|
||||
# with the computed values using a very small relative tolerance.
|
||||
# The value at -np.pi is not 0, because -np.pi does not equal -π.
|
||||
_coscdf_close = [
|
||||
(3.1409, 0.999999999991185),
|
||||
(2.25, 0.9819328173287907),
|
||||
# -1.6 is the threshold below which the Pade approximant is used.
|
||||
(-1.599, 0.08641959838382553),
|
||||
(-1.601, 0.086110582992713),
|
||||
(-2.0, 0.0369709335961611),
|
||||
(-3.0, 7.522387241801384e-05),
|
||||
(-3.1415, 2.109869685443648e-14),
|
||||
(-3.14159, 4.956444476505336e-19),
|
||||
(-np.pi, 4.871934450264861e-50),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("x, expected", _coscdf_close)
|
||||
def test_cosine_cdf(x, expected):
|
||||
assert_allclose(_cosine_cdf(x), expected, rtol=5e-15)
|
||||
|
||||
|
||||
# These values are (p, x) where x is the expected exact value of
|
||||
# _cosine_invcdf(p). These values will be tested for exact agreement.
|
||||
_cosinvcdf_exact = [
|
||||
(0.0, -np.pi),
|
||||
(0.5, 0.0),
|
||||
(1.0, np.pi),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("p, expected", _cosinvcdf_exact)
|
||||
def test_cosine_invcdf_exact(p, expected):
|
||||
assert _cosine_invcdf(p) == expected
|
||||
|
||||
|
||||
def test_cosine_invcdf_invalid_p():
|
||||
# Check that p values outside of [0, 1] return nan.
|
||||
assert np.isnan(_cosine_invcdf([-0.1, 1.1])).all()
|
||||
|
||||
|
||||
# These values are (p, x), where x is the expected value of _cosine_invcdf(p).
|
||||
# The expected values were computed with mpmath using 50 digits of precision.
|
||||
_cosinvcdf_close = [
|
||||
(1e-50, -np.pi),
|
||||
(1e-14, -3.1415204137058454),
|
||||
(1e-08, -3.1343686589124524),
|
||||
(0.0018001, -2.732563923138336),
|
||||
(0.010, -2.41276589008678),
|
||||
(0.060, -1.7881244975330157),
|
||||
(0.125, -1.3752523669869274),
|
||||
(0.250, -0.831711193579736),
|
||||
(0.400, -0.3167954512395289),
|
||||
(0.419, -0.25586025626919906),
|
||||
(0.421, -0.24947570750445663),
|
||||
(0.750, 0.831711193579736),
|
||||
(0.940, 1.7881244975330153),
|
||||
(0.9999999996, 3.1391220839917167),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("p, expected", _cosinvcdf_close)
|
||||
def test_cosine_invcdf(p, expected):
|
||||
assert_allclose(_cosine_invcdf(p), expected, rtol=1e-14)
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
from collections.abc import Callable
|
||||
|
||||
import pytest
|
||||
from itertools import product
|
||||
from numpy.testing import assert_allclose, suppress_warnings
|
||||
from scipy import special
|
||||
from scipy.special import cython_special
|
||||
|
||||
|
||||
bint_points = [True, False]
|
||||
int_points = [-10, -1, 1, 10]
|
||||
real_points = [-10.0, -1.0, 1.0, 10.0]
|
||||
complex_points = [complex(*tup) for tup in product(real_points, repeat=2)]
|
||||
|
||||
|
||||
CYTHON_SIGNATURE_MAP = {
|
||||
'b': 'bint',
|
||||
'f': 'float',
|
||||
'd': 'double',
|
||||
'g': 'long double',
|
||||
'F': 'float complex',
|
||||
'D': 'double complex',
|
||||
'G': 'long double complex',
|
||||
'i': 'int',
|
||||
'l': 'long'
|
||||
}
|
||||
|
||||
|
||||
TEST_POINTS = {
|
||||
'b': bint_points,
|
||||
'f': real_points,
|
||||
'd': real_points,
|
||||
'g': real_points,
|
||||
'F': complex_points,
|
||||
'D': complex_points,
|
||||
'G': complex_points,
|
||||
'i': int_points,
|
||||
'l': int_points,
|
||||
}
|
||||
|
||||
|
||||
PARAMS: list[tuple[Callable, Callable, tuple[str, ...], str | None]] = [
|
||||
(special.agm, cython_special.agm, ('dd',), None),
|
||||
(special.airy, cython_special._airy_pywrap, ('d', 'D'), None),
|
||||
(special.airye, cython_special._airye_pywrap, ('d', 'D'), None),
|
||||
(special.bdtr, cython_special.bdtr, ('dld', 'ddd'), None),
|
||||
(special.bdtrc, cython_special.bdtrc, ('dld', 'ddd'), None),
|
||||
(special.bdtri, cython_special.bdtri, ('dld', 'ddd'), None),
|
||||
(special.bdtrik, cython_special.bdtrik, ('ddd',), None),
|
||||
(special.bdtrin, cython_special.bdtrin, ('ddd',), None),
|
||||
(special.bei, cython_special.bei, ('d',), None),
|
||||
(special.beip, cython_special.beip, ('d',), None),
|
||||
(special.ber, cython_special.ber, ('d',), None),
|
||||
(special.berp, cython_special.berp, ('d',), None),
|
||||
(special.besselpoly, cython_special.besselpoly, ('ddd',), None),
|
||||
(special.beta, cython_special.beta, ('dd',), None),
|
||||
(special.betainc, cython_special.betainc, ('ddd',), None),
|
||||
(special.betaincc, cython_special.betaincc, ('ddd',), None),
|
||||
(special.betaincinv, cython_special.betaincinv, ('ddd',), None),
|
||||
(special.betainccinv, cython_special.betainccinv, ('ddd',), None),
|
||||
(special.betaln, cython_special.betaln, ('dd',), None),
|
||||
(special.binom, cython_special.binom, ('dd',), None),
|
||||
(special.boxcox, cython_special.boxcox, ('dd',), None),
|
||||
(special.boxcox1p, cython_special.boxcox1p, ('dd',), None),
|
||||
(special.btdtria, cython_special.btdtria, ('ddd',), None),
|
||||
(special.btdtrib, cython_special.btdtrib, ('ddd',), None),
|
||||
(special.cbrt, cython_special.cbrt, ('d',), None),
|
||||
(special.chdtr, cython_special.chdtr, ('dd',), None),
|
||||
(special.chdtrc, cython_special.chdtrc, ('dd',), None),
|
||||
(special.chdtri, cython_special.chdtri, ('dd',), None),
|
||||
(special.chdtriv, cython_special.chdtriv, ('dd',), None),
|
||||
(special.chndtr, cython_special.chndtr, ('ddd',), None),
|
||||
(special.chndtridf, cython_special.chndtridf, ('ddd',), None),
|
||||
(special.chndtrinc, cython_special.chndtrinc, ('ddd',), None),
|
||||
(special.chndtrix, cython_special.chndtrix, ('ddd',), None),
|
||||
(special.cosdg, cython_special.cosdg, ('d',), None),
|
||||
(special.cosm1, cython_special.cosm1, ('d',), None),
|
||||
(special.cotdg, cython_special.cotdg, ('d',), None),
|
||||
(special.dawsn, cython_special.dawsn, ('d', 'D'), None),
|
||||
(special.ellipe, cython_special.ellipe, ('d',), None),
|
||||
(special.ellipeinc, cython_special.ellipeinc, ('dd',), None),
|
||||
(special.ellipj, cython_special._ellipj_pywrap, ('dd',), None),
|
||||
(special.ellipkinc, cython_special.ellipkinc, ('dd',), None),
|
||||
(special.ellipkm1, cython_special.ellipkm1, ('d',), None),
|
||||
(special.ellipk, cython_special.ellipk, ('d',), None),
|
||||
(special.elliprc, cython_special.elliprc, ('dd', 'DD'), None),
|
||||
(special.elliprd, cython_special.elliprd, ('ddd', 'DDD'), None),
|
||||
(special.elliprf, cython_special.elliprf, ('ddd', 'DDD'), None),
|
||||
(special.elliprg, cython_special.elliprg, ('ddd', 'DDD'), None),
|
||||
(special.elliprj, cython_special.elliprj, ('dddd', 'DDDD'), None),
|
||||
(special.entr, cython_special.entr, ('d',), None),
|
||||
(special.erf, cython_special.erf, ('d', 'D'), None),
|
||||
(special.erfc, cython_special.erfc, ('d', 'D'), None),
|
||||
(special.erfcx, cython_special.erfcx, ('d', 'D'), None),
|
||||
(special.erfi, cython_special.erfi, ('d', 'D'), None),
|
||||
(special.erfinv, cython_special.erfinv, ('d',), None),
|
||||
(special.erfcinv, cython_special.erfcinv, ('d',), None),
|
||||
(special.eval_chebyc, cython_special.eval_chebyc, ('dd', 'dD', 'ld'), None),
|
||||
(special.eval_chebys, cython_special.eval_chebys, ('dd', 'dD', 'ld'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_chebyt, cython_special.eval_chebyt, ('dd', 'dD', 'ld'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_chebyu, cython_special.eval_chebyu, ('dd', 'dD', 'ld'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_gegenbauer, cython_special.eval_gegenbauer, ('ddd', 'ddD', 'ldd'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_genlaguerre, cython_special.eval_genlaguerre, ('ddd', 'ddD', 'ldd'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_hermite, cython_special.eval_hermite, ('ld',), None),
|
||||
(special.eval_hermitenorm, cython_special.eval_hermitenorm, ('ld',), None),
|
||||
(special.eval_jacobi, cython_special.eval_jacobi, ('dddd', 'dddD', 'lddd'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_laguerre, cython_special.eval_laguerre, ('dd', 'dD', 'ld'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_legendre, cython_special.eval_legendre, ('dd', 'dD', 'ld'), None),
|
||||
(special.eval_sh_chebyt, cython_special.eval_sh_chebyt, ('dd', 'dD', 'ld'), None),
|
||||
(special.eval_sh_chebyu, cython_special.eval_sh_chebyu, ('dd', 'dD', 'ld'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_sh_jacobi, cython_special.eval_sh_jacobi, ('dddd', 'dddD', 'lddd'),
|
||||
'd and l differ for negative int'),
|
||||
(special.eval_sh_legendre, cython_special.eval_sh_legendre, ('dd', 'dD', 'ld'),
|
||||
None),
|
||||
(special.exp1, cython_special.exp1, ('d', 'D'), None),
|
||||
(special.exp10, cython_special.exp10, ('d',), None),
|
||||
(special.exp2, cython_special.exp2, ('d',), None),
|
||||
(special.expi, cython_special.expi, ('d', 'D'), None),
|
||||
(special.expit, cython_special.expit, ('f', 'd', 'g'), None),
|
||||
(special.expm1, cython_special.expm1, ('d', 'D'), None),
|
||||
(special.expn, cython_special.expn, ('ld', 'dd'), None),
|
||||
(special.exprel, cython_special.exprel, ('d',), None),
|
||||
(special.fdtr, cython_special.fdtr, ('ddd',), None),
|
||||
(special.fdtrc, cython_special.fdtrc, ('ddd',), None),
|
||||
(special.fdtri, cython_special.fdtri, ('ddd',), None),
|
||||
(special.fdtridfd, cython_special.fdtridfd, ('ddd',), None),
|
||||
(special.fresnel, cython_special._fresnel_pywrap, ('d', 'D'), None),
|
||||
(special.gamma, cython_special.gamma, ('d', 'D'), None),
|
||||
(special.gammainc, cython_special.gammainc, ('dd',), None),
|
||||
(special.gammaincc, cython_special.gammaincc, ('dd',), None),
|
||||
(special.gammainccinv, cython_special.gammainccinv, ('dd',), None),
|
||||
(special.gammaincinv, cython_special.gammaincinv, ('dd',), None),
|
||||
(special.gammaln, cython_special.gammaln, ('d',), None),
|
||||
(special.gammasgn, cython_special.gammasgn, ('d',), None),
|
||||
(special.gdtr, cython_special.gdtr, ('ddd',), None),
|
||||
(special.gdtrc, cython_special.gdtrc, ('ddd',), None),
|
||||
(special.gdtria, cython_special.gdtria, ('ddd',), None),
|
||||
(special.gdtrib, cython_special.gdtrib, ('ddd',), None),
|
||||
(special.gdtrix, cython_special.gdtrix, ('ddd',), None),
|
||||
(special.hankel1, cython_special.hankel1, ('dD',), None),
|
||||
(special.hankel1e, cython_special.hankel1e, ('dD',), None),
|
||||
(special.hankel2, cython_special.hankel2, ('dD',), None),
|
||||
(special.hankel2e, cython_special.hankel2e, ('dD',), None),
|
||||
(special.huber, cython_special.huber, ('dd',), None),
|
||||
(special.hyp0f1, cython_special.hyp0f1, ('dd', 'dD'), None),
|
||||
(special.hyp1f1, cython_special.hyp1f1, ('ddd', 'ddD'), None),
|
||||
(special.hyp2f1, cython_special.hyp2f1, ('dddd', 'dddD'), None),
|
||||
(special.hyperu, cython_special.hyperu, ('ddd',), None),
|
||||
(special.i0, cython_special.i0, ('d',), None),
|
||||
(special.i0e, cython_special.i0e, ('d',), None),
|
||||
(special.i1, cython_special.i1, ('d',), None),
|
||||
(special.i1e, cython_special.i1e, ('d',), None),
|
||||
(special.inv_boxcox, cython_special.inv_boxcox, ('dd',), None),
|
||||
(special.inv_boxcox1p, cython_special.inv_boxcox1p, ('dd',), None),
|
||||
(special.it2i0k0, cython_special._it2i0k0_pywrap, ('d',), None),
|
||||
(special.it2j0y0, cython_special._it2j0y0_pywrap, ('d',), None),
|
||||
(special.it2struve0, cython_special.it2struve0, ('d',), None),
|
||||
(special.itairy, cython_special._itairy_pywrap, ('d',), None),
|
||||
(special.iti0k0, cython_special._iti0k0_pywrap, ('d',), None),
|
||||
(special.itj0y0, cython_special._itj0y0_pywrap, ('d',), None),
|
||||
(special.itmodstruve0, cython_special.itmodstruve0, ('d',), None),
|
||||
(special.itstruve0, cython_special.itstruve0, ('d',), None),
|
||||
(special.iv, cython_special.iv, ('dd', 'dD'), None),
|
||||
(special.ive, cython_special.ive, ('dd', 'dD'), None),
|
||||
(special.j0, cython_special.j0, ('d',), None),
|
||||
(special.j1, cython_special.j1, ('d',), None),
|
||||
(special.jv, cython_special.jv, ('dd', 'dD'), None),
|
||||
(special.jve, cython_special.jve, ('dd', 'dD'), None),
|
||||
(special.k0, cython_special.k0, ('d',), None),
|
||||
(special.k0e, cython_special.k0e, ('d',), None),
|
||||
(special.k1, cython_special.k1, ('d',), None),
|
||||
(special.k1e, cython_special.k1e, ('d',), None),
|
||||
(special.kei, cython_special.kei, ('d',), None),
|
||||
(special.keip, cython_special.keip, ('d',), None),
|
||||
(special.kelvin, cython_special._kelvin_pywrap, ('d',), None),
|
||||
(special.ker, cython_special.ker, ('d',), None),
|
||||
(special.kerp, cython_special.kerp, ('d',), None),
|
||||
(special.kl_div, cython_special.kl_div, ('dd',), None),
|
||||
(special.kn, cython_special.kn, ('ld', 'dd'), None),
|
||||
(special.kolmogi, cython_special.kolmogi, ('d',), None),
|
||||
(special.kolmogorov, cython_special.kolmogorov, ('d',), None),
|
||||
(special.kv, cython_special.kv, ('dd', 'dD'), None),
|
||||
(special.kve, cython_special.kve, ('dd', 'dD'), None),
|
||||
(special.log1p, cython_special.log1p, ('d', 'D'), None),
|
||||
(special.log_expit, cython_special.log_expit, ('f', 'd', 'g'), None),
|
||||
(special.log_ndtr, cython_special.log_ndtr, ('d', 'D'), None),
|
||||
(special.log_wright_bessel, cython_special.log_wright_bessel, ('ddd',), None),
|
||||
(special.ndtri_exp, cython_special.ndtri_exp, ('d',), None),
|
||||
(special.loggamma, cython_special.loggamma, ('D',), None),
|
||||
(special.logit, cython_special.logit, ('f', 'd', 'g'), None),
|
||||
(special.lpmv, cython_special.lpmv, ('ddd',), None),
|
||||
(special.mathieu_a, cython_special.mathieu_a, ('dd',), None),
|
||||
(special.mathieu_b, cython_special.mathieu_b, ('dd',), None),
|
||||
(special.mathieu_cem, cython_special._mathieu_cem_pywrap, ('ddd',), None),
|
||||
(special.mathieu_modcem1, cython_special._mathieu_modcem1_pywrap, ('ddd',), None),
|
||||
(special.mathieu_modcem2, cython_special._mathieu_modcem2_pywrap, ('ddd',), None),
|
||||
(special.mathieu_modsem1, cython_special._mathieu_modsem1_pywrap, ('ddd',), None),
|
||||
(special.mathieu_modsem2, cython_special._mathieu_modsem2_pywrap, ('ddd',), None),
|
||||
(special.mathieu_sem, cython_special._mathieu_sem_pywrap, ('ddd',), None),
|
||||
(special.modfresnelm, cython_special._modfresnelm_pywrap, ('d',), None),
|
||||
(special.modfresnelp, cython_special._modfresnelp_pywrap, ('d',), None),
|
||||
(special.modstruve, cython_special.modstruve, ('dd',), None),
|
||||
(special.nbdtr, cython_special.nbdtr, ('lld', 'ddd'), None),
|
||||
(special.nbdtrc, cython_special.nbdtrc, ('lld', 'ddd'), None),
|
||||
(special.nbdtri, cython_special.nbdtri, ('lld', 'ddd'), None),
|
||||
(special.nbdtrik, cython_special.nbdtrik, ('ddd',), None),
|
||||
(special.nbdtrin, cython_special.nbdtrin, ('ddd',), None),
|
||||
(special.ncfdtr, cython_special.ncfdtr, ('dddd',), None),
|
||||
(special.ncfdtri, cython_special.ncfdtri, ('dddd',), None),
|
||||
(special.ncfdtridfd, cython_special.ncfdtridfd, ('dddd',), None),
|
||||
(special.ncfdtridfn, cython_special.ncfdtridfn, ('dddd',), None),
|
||||
(special.ncfdtrinc, cython_special.ncfdtrinc, ('dddd',), None),
|
||||
(special.nctdtr, cython_special.nctdtr, ('ddd',), None),
|
||||
(special.nctdtridf, cython_special.nctdtridf, ('ddd',), None),
|
||||
(special.nctdtrinc, cython_special.nctdtrinc, ('ddd',), None),
|
||||
(special.nctdtrit, cython_special.nctdtrit, ('ddd',), None),
|
||||
(special.ndtr, cython_special.ndtr, ('d', 'D'), None),
|
||||
(special.ndtri, cython_special.ndtri, ('d',), None),
|
||||
(special.nrdtrimn, cython_special.nrdtrimn, ('ddd',), None),
|
||||
(special.nrdtrisd, cython_special.nrdtrisd, ('ddd',), None),
|
||||
(special.obl_ang1, cython_special._obl_ang1_pywrap, ('dddd',), None),
|
||||
(special.obl_ang1_cv, cython_special._obl_ang1_cv_pywrap, ('ddddd',), None),
|
||||
(special.obl_cv, cython_special.obl_cv, ('ddd',), None),
|
||||
(special.obl_rad1, cython_special._obl_rad1_pywrap, ('dddd',), "see gh-6211"),
|
||||
(special.obl_rad1_cv, cython_special._obl_rad1_cv_pywrap, ('ddddd',),
|
||||
"see gh-6211"),
|
||||
(special.obl_rad2, cython_special._obl_rad2_pywrap, ('dddd',), "see gh-6211"),
|
||||
(special.obl_rad2_cv, cython_special._obl_rad2_cv_pywrap, ('ddddd',),
|
||||
"see gh-6211"),
|
||||
(special.pbdv, cython_special._pbdv_pywrap, ('dd',), None),
|
||||
(special.pbvv, cython_special._pbvv_pywrap, ('dd',), None),
|
||||
(special.pbwa, cython_special._pbwa_pywrap, ('dd',), None),
|
||||
(special.pdtr, cython_special.pdtr, ('dd', 'dd'), None),
|
||||
(special.pdtrc, cython_special.pdtrc, ('dd', 'dd'), None),
|
||||
(special.pdtri, cython_special.pdtri, ('ld', 'dd'), None),
|
||||
(special.pdtrik, cython_special.pdtrik, ('dd',), None),
|
||||
(special.poch, cython_special.poch, ('dd',), None),
|
||||
(special.powm1, cython_special.powm1, ('dd',), None),
|
||||
(special.pro_ang1, cython_special._pro_ang1_pywrap, ('dddd',), None),
|
||||
(special.pro_ang1_cv, cython_special._pro_ang1_cv_pywrap, ('ddddd',), None),
|
||||
(special.pro_cv, cython_special.pro_cv, ('ddd',), None),
|
||||
(special.pro_rad1, cython_special._pro_rad1_pywrap, ('dddd',), "see gh-6211"),
|
||||
(special.pro_rad1_cv, cython_special._pro_rad1_cv_pywrap, ('ddddd',),
|
||||
"see gh-6211"),
|
||||
(special.pro_rad2, cython_special._pro_rad2_pywrap, ('dddd',), "see gh-6211"),
|
||||
(special.pro_rad2_cv, cython_special._pro_rad2_cv_pywrap, ('ddddd',),
|
||||
"see gh-6211"),
|
||||
(special.pseudo_huber, cython_special.pseudo_huber, ('dd',), None),
|
||||
(special.psi, cython_special.psi, ('d', 'D'), None),
|
||||
(special.radian, cython_special.radian, ('ddd',), None),
|
||||
(special.rel_entr, cython_special.rel_entr, ('dd',), None),
|
||||
(special.rgamma, cython_special.rgamma, ('d', 'D'), None),
|
||||
(special.round, cython_special.round, ('d',), None),
|
||||
(special.spherical_jn, cython_special.spherical_jn, ('ld', 'ldb', 'lD', 'lDb'),
|
||||
"Python version supports negative reals; Cython version doesn't - see gh-21629"),
|
||||
(special.spherical_yn, cython_special.spherical_yn, ('ld', 'ldb', 'lD', 'lDb'),
|
||||
"Python version supports negative reals; Cython version doesn't - see gh-21629"),
|
||||
(special.spherical_in, cython_special.spherical_in, ('ld', 'ldb', 'lD', 'lDb'),
|
||||
"Python version supports negative reals; Cython version doesn't - see gh-21629"),
|
||||
(special.spherical_kn, cython_special.spherical_kn, ('ld', 'ldb', 'lD', 'lDb'),
|
||||
"Python version supports negative reals; Cython version doesn't - see gh-21629"),
|
||||
(special.shichi, cython_special._shichi_pywrap, ('d', 'D'), None),
|
||||
(special.sici, cython_special._sici_pywrap, ('d', 'D'), None),
|
||||
(special.sindg, cython_special.sindg, ('d',), None),
|
||||
(special.smirnov, cython_special.smirnov, ('ld', 'dd'), None),
|
||||
(special.smirnovi, cython_special.smirnovi, ('ld', 'dd'), None),
|
||||
(special.spence, cython_special.spence, ('d', 'D'), None),
|
||||
(special.sph_harm, cython_special.sph_harm, ('lldd', 'dddd'), None),
|
||||
(special.stdtr, cython_special.stdtr, ('dd',), None),
|
||||
(special.stdtridf, cython_special.stdtridf, ('dd',), None),
|
||||
(special.stdtrit, cython_special.stdtrit, ('dd',), None),
|
||||
(special.struve, cython_special.struve, ('dd',), None),
|
||||
(special.tandg, cython_special.tandg, ('d',), None),
|
||||
(special.tklmbda, cython_special.tklmbda, ('dd',), None),
|
||||
(special.voigt_profile, cython_special.voigt_profile, ('ddd',), None),
|
||||
(special.wofz, cython_special.wofz, ('D',), None),
|
||||
(special.wright_bessel, cython_special.wright_bessel, ('ddd',), None),
|
||||
(special.wrightomega, cython_special.wrightomega, ('D',), None),
|
||||
(special.xlog1py, cython_special.xlog1py, ('dd', 'DD'), None),
|
||||
(special.xlogy, cython_special.xlogy, ('dd', 'DD'), None),
|
||||
(special.y0, cython_special.y0, ('d',), None),
|
||||
(special.y1, cython_special.y1, ('d',), None),
|
||||
(special.yn, cython_special.yn, ('ld', 'dd'), None),
|
||||
(special.yv, cython_special.yv, ('dd', 'dD'), None),
|
||||
(special.yve, cython_special.yve, ('dd', 'dD'), None),
|
||||
(special.zetac, cython_special.zetac, ('d',), None),
|
||||
(special.owens_t, cython_special.owens_t, ('dd',), None)
|
||||
]
|
||||
|
||||
|
||||
IDS = [x[0].__name__ for x in PARAMS]
|
||||
|
||||
|
||||
def _generate_test_points(typecodes):
|
||||
axes = tuple(TEST_POINTS[x] for x in typecodes)
|
||||
pts = list(product(*axes))
|
||||
return pts
|
||||
|
||||
|
||||
def test_cython_api_completeness():
|
||||
# Check that everything is tested
|
||||
for name in dir(cython_special):
|
||||
func = getattr(cython_special, name)
|
||||
if callable(func) and not name.startswith('_'):
|
||||
for _, cyfun, _, _ in PARAMS:
|
||||
if cyfun is func:
|
||||
break
|
||||
else:
|
||||
raise RuntimeError(f"{name} missing from tests!")
|
||||
|
||||
|
||||
@pytest.mark.thread_unsafe
|
||||
@pytest.mark.fail_slow(20)
|
||||
@pytest.mark.parametrize("param", PARAMS, ids=IDS)
|
||||
def test_cython_api(param):
|
||||
pyfunc, cyfunc, specializations, knownfailure = param
|
||||
if knownfailure:
|
||||
pytest.xfail(reason=knownfailure)
|
||||
|
||||
# Check which parameters are expected to be fused types
|
||||
max_params = max(len(spec) for spec in specializations)
|
||||
values = [set() for _ in range(max_params)]
|
||||
for typecodes in specializations:
|
||||
for j, v in enumerate(typecodes):
|
||||
values[j].add(v)
|
||||
seen = set()
|
||||
is_fused_code = [False] * len(values)
|
||||
for j, v in enumerate(values):
|
||||
vv = tuple(sorted(v))
|
||||
if vv in seen:
|
||||
continue
|
||||
is_fused_code[j] = (len(v) > 1)
|
||||
seen.add(vv)
|
||||
|
||||
# Check results
|
||||
for typecodes in specializations:
|
||||
# Pick the correct specialized function
|
||||
signature = [CYTHON_SIGNATURE_MAP[code]
|
||||
for j, code in enumerate(typecodes)
|
||||
if is_fused_code[j]]
|
||||
|
||||
if signature:
|
||||
cy_spec_func = cyfunc[tuple(signature)]
|
||||
else:
|
||||
signature = None
|
||||
cy_spec_func = cyfunc
|
||||
|
||||
# Test it
|
||||
pts = _generate_test_points(typecodes)
|
||||
for pt in pts:
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(DeprecationWarning)
|
||||
pyval = pyfunc(*pt)
|
||||
cyval = cy_spec_func(*pt)
|
||||
assert_allclose(cyval, pyval, err_msg=f"{pt} {typecodes} {signature}")
|
||||
|
|
@ -0,0 +1,719 @@
|
|||
import importlib.resources
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import suppress_warnings
|
||||
import pytest
|
||||
|
||||
from scipy.special import (
|
||||
lpn, lpmn, lpmv, lqn, lqmn, sph_harm, eval_legendre, eval_hermite,
|
||||
eval_laguerre, eval_genlaguerre, binom, cbrt, expm1, log1p, zeta,
|
||||
jn, jv, jvp, yn, yv, yvp, iv, ivp, kn, kv, kvp,
|
||||
gamma, gammaln, gammainc, gammaincc, gammaincinv, gammainccinv, digamma,
|
||||
beta, betainc, betaincinv, poch,
|
||||
ellipe, ellipeinc, ellipk, ellipkm1, ellipkinc,
|
||||
elliprc, elliprd, elliprf, elliprg, elliprj,
|
||||
erf, erfc, erfinv, erfcinv, exp1, expi, expn,
|
||||
bdtrik, btdtria, btdtrib, chndtr, gdtr, gdtrc, gdtrix, gdtrib,
|
||||
nbdtrik, pdtrik, owens_t,
|
||||
mathieu_a, mathieu_b, mathieu_cem, mathieu_sem, mathieu_modcem1,
|
||||
mathieu_modsem1, mathieu_modcem2, mathieu_modsem2,
|
||||
ellip_harm, ellip_harm_2, spherical_jn, spherical_yn, wright_bessel
|
||||
)
|
||||
from scipy.integrate import IntegrationWarning
|
||||
|
||||
from scipy.special._testutils import FuncData
|
||||
|
||||
|
||||
# The npz files are generated, and hence may live in the build dir. We can only
|
||||
# access them through `importlib.resources`, not an explicit path from `__file__`
|
||||
_datadir = importlib.resources.files('scipy.special.tests.data')
|
||||
|
||||
_boost_npz = _datadir.joinpath('boost.npz')
|
||||
with importlib.resources.as_file(_boost_npz) as f:
|
||||
DATASETS_BOOST = np.load(f)
|
||||
|
||||
_gsl_npz = _datadir.joinpath('gsl.npz')
|
||||
with importlib.resources.as_file(_gsl_npz) as f:
|
||||
DATASETS_GSL = np.load(f)
|
||||
|
||||
_local_npz = _datadir.joinpath('local.npz')
|
||||
with importlib.resources.as_file(_local_npz) as f:
|
||||
DATASETS_LOCAL = np.load(f)
|
||||
|
||||
|
||||
def data(func, dataname, *a, **kw):
|
||||
kw.setdefault('dataname', dataname)
|
||||
return FuncData(func, DATASETS_BOOST[dataname], *a, **kw)
|
||||
|
||||
|
||||
def data_gsl(func, dataname, *a, **kw):
|
||||
kw.setdefault('dataname', dataname)
|
||||
return FuncData(func, DATASETS_GSL[dataname], *a, **kw)
|
||||
|
||||
|
||||
def data_local(func, dataname, *a, **kw):
|
||||
kw.setdefault('dataname', dataname)
|
||||
return FuncData(func, DATASETS_LOCAL[dataname], *a, **kw)
|
||||
|
||||
|
||||
# The functions lpn, lpmn, clpmn, and sph_harm appearing below are
|
||||
# deprecated in favor of legendre_p_all, assoc_legendre_p_all,
|
||||
# assoc_legendre_p_all (assoc_legendre_p_all covers lpmn and clpmn),
|
||||
# and sph_harm_y respectively. The deprecated functions listed above are
|
||||
# implemented as shims around their respective replacements. The replacements
|
||||
# are tested separately, but tests for the deprecated functions remain to
|
||||
# verify the correctness of the shims.
|
||||
|
||||
|
||||
def ellipk_(k):
|
||||
return ellipk(k*k)
|
||||
|
||||
|
||||
def ellipkinc_(f, k):
|
||||
return ellipkinc(f, k*k)
|
||||
|
||||
|
||||
def ellipe_(k):
|
||||
return ellipe(k*k)
|
||||
|
||||
|
||||
def ellipeinc_(f, k):
|
||||
return ellipeinc(f, k*k)
|
||||
|
||||
|
||||
def zeta_(x):
|
||||
return zeta(x, 1.)
|
||||
|
||||
|
||||
def assoc_legendre_p_boost_(nu, mu, x):
|
||||
# the boost test data is for integer orders only
|
||||
return lpmv(mu, nu.astype(int), x)
|
||||
|
||||
def legendre_p_via_assoc_(nu, x):
|
||||
return lpmv(0, nu, x)
|
||||
|
||||
def lpn_(n, x):
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(category=DeprecationWarning)
|
||||
return lpn(n.astype('l'), x)[0][-1]
|
||||
|
||||
def lqn_(n, x):
|
||||
return lqn(n.astype('l'), x)[0][-1]
|
||||
|
||||
def legendre_p_via_lpmn(n, x):
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(category=DeprecationWarning)
|
||||
return lpmn(0, n, x)[0][0,-1]
|
||||
|
||||
def legendre_q_via_lqmn(n, x):
|
||||
return lqmn(0, n, x)[0][0,-1]
|
||||
|
||||
def mathieu_ce_rad(m, q, x):
|
||||
return mathieu_cem(m, q, x*180/np.pi)[0]
|
||||
|
||||
|
||||
def mathieu_se_rad(m, q, x):
|
||||
return mathieu_sem(m, q, x*180/np.pi)[0]
|
||||
|
||||
|
||||
def mathieu_mc1_scaled(m, q, x):
|
||||
# GSL follows a different normalization.
|
||||
# We follow Abramowitz & Stegun, they apparently something else.
|
||||
return mathieu_modcem1(m, q, x)[0] * np.sqrt(np.pi/2)
|
||||
|
||||
|
||||
def mathieu_ms1_scaled(m, q, x):
|
||||
return mathieu_modsem1(m, q, x)[0] * np.sqrt(np.pi/2)
|
||||
|
||||
|
||||
def mathieu_mc2_scaled(m, q, x):
|
||||
return mathieu_modcem2(m, q, x)[0] * np.sqrt(np.pi/2)
|
||||
|
||||
|
||||
def mathieu_ms2_scaled(m, q, x):
|
||||
return mathieu_modsem2(m, q, x)[0] * np.sqrt(np.pi/2)
|
||||
|
||||
def eval_legendre_ld(n, x):
|
||||
return eval_legendre(n.astype('l'), x)
|
||||
|
||||
def eval_legendre_dd(n, x):
|
||||
return eval_legendre(n.astype('d'), x)
|
||||
|
||||
def eval_hermite_ld(n, x):
|
||||
return eval_hermite(n.astype('l'), x)
|
||||
|
||||
def eval_laguerre_ld(n, x):
|
||||
return eval_laguerre(n.astype('l'), x)
|
||||
|
||||
def eval_laguerre_dd(n, x):
|
||||
return eval_laguerre(n.astype('d'), x)
|
||||
|
||||
def eval_genlaguerre_ldd(n, a, x):
|
||||
return eval_genlaguerre(n.astype('l'), a, x)
|
||||
|
||||
def eval_genlaguerre_ddd(n, a, x):
|
||||
return eval_genlaguerre(n.astype('d'), a, x)
|
||||
|
||||
def bdtrik_comp(y, n, p):
|
||||
return bdtrik(1-y, n, p)
|
||||
|
||||
def btdtria_comp(p, b, x):
|
||||
return btdtria(1-p, b, x)
|
||||
|
||||
def btdtrib_comp(a, p, x):
|
||||
return btdtrib(a, 1-p, x)
|
||||
|
||||
def gdtr_(p, x):
|
||||
return gdtr(1.0, p, x)
|
||||
|
||||
def gdtrc_(p, x):
|
||||
return gdtrc(1.0, p, x)
|
||||
|
||||
def gdtrix_(b, p):
|
||||
return gdtrix(1.0, b, p)
|
||||
|
||||
def gdtrix_comp(b, p):
|
||||
return gdtrix(1.0, b, 1-p)
|
||||
|
||||
def gdtrib_(p, x):
|
||||
return gdtrib(1.0, p, x)
|
||||
|
||||
def gdtrib_comp(p, x):
|
||||
return gdtrib(1.0, 1-p, x)
|
||||
|
||||
def nbdtrik_comp(y, n, p):
|
||||
return nbdtrik(1-y, n, p)
|
||||
|
||||
def pdtrik_comp(p, m):
|
||||
return pdtrik(1-p, m)
|
||||
|
||||
def poch_(z, m):
|
||||
return 1.0 / poch(z, m)
|
||||
|
||||
def poch_minus(z, m):
|
||||
return 1.0 / poch(z, -m)
|
||||
|
||||
def spherical_jn_(n, x):
|
||||
return spherical_jn(n.astype('l'), x)
|
||||
|
||||
def spherical_yn_(n, x):
|
||||
return spherical_yn(n.astype('l'), x)
|
||||
|
||||
def sph_harm_(m, n, theta, phi):
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(category=DeprecationWarning)
|
||||
y = sph_harm(m, n, theta, phi)
|
||||
return (y.real, y.imag)
|
||||
|
||||
def cexpm1(x, y):
|
||||
z = expm1(x + 1j*y)
|
||||
return z.real, z.imag
|
||||
|
||||
def clog1p(x, y):
|
||||
z = log1p(x + 1j*y)
|
||||
return z.real, z.imag
|
||||
|
||||
|
||||
BOOST_TESTS = [
|
||||
data(assoc_legendre_p_boost_, 'assoc_legendre_p_ipp-assoc_legendre_p',
|
||||
(0,1,2), 3, rtol=1e-11),
|
||||
|
||||
data(legendre_p_via_assoc_, 'legendre_p_ipp-legendre_p',
|
||||
(0,1), 2, rtol=1e-11),
|
||||
data(legendre_p_via_assoc_, 'legendre_p_large_ipp-legendre_p_large',
|
||||
(0,1), 2, rtol=9.6e-14),
|
||||
data(legendre_p_via_lpmn, 'legendre_p_ipp-legendre_p',
|
||||
(0,1), 2, rtol=5e-14, vectorized=False),
|
||||
data(legendre_p_via_lpmn, 'legendre_p_large_ipp-legendre_p_large',
|
||||
(0,1), 2, rtol=3e-13, vectorized=False),
|
||||
data(lpn_, 'legendre_p_ipp-legendre_p',
|
||||
(0,1), 2, rtol=5e-14, vectorized=False),
|
||||
data(lpn_, 'legendre_p_large_ipp-legendre_p_large',
|
||||
(0,1), 2, rtol=3e-13, vectorized=False),
|
||||
data(eval_legendre_ld, 'legendre_p_ipp-legendre_p',
|
||||
(0,1), 2, rtol=6e-14),
|
||||
data(eval_legendre_ld, 'legendre_p_large_ipp-legendre_p_large',
|
||||
(0,1), 2, rtol=2e-13),
|
||||
data(eval_legendre_dd, 'legendre_p_ipp-legendre_p',
|
||||
(0,1), 2, rtol=2e-14),
|
||||
data(eval_legendre_dd, 'legendre_p_large_ipp-legendre_p_large',
|
||||
(0,1), 2, rtol=2e-13),
|
||||
|
||||
data(lqn_, 'legendre_p_ipp-legendre_p',
|
||||
(0,1), 3, rtol=2e-14, vectorized=False),
|
||||
data(lqn_, 'legendre_p_large_ipp-legendre_p_large',
|
||||
(0,1), 3, rtol=2e-12, vectorized=False),
|
||||
data(legendre_q_via_lqmn, 'legendre_p_ipp-legendre_p',
|
||||
(0,1), 3, rtol=2e-14, vectorized=False),
|
||||
data(legendre_q_via_lqmn, 'legendre_p_large_ipp-legendre_p_large',
|
||||
(0,1), 3, rtol=2e-12, vectorized=False),
|
||||
|
||||
data(beta, 'beta_exp_data_ipp-beta_exp_data',
|
||||
(0,1), 2, rtol=1e-13),
|
||||
data(beta, 'beta_exp_data_ipp-beta_exp_data',
|
||||
(0,1), 2, rtol=1e-13),
|
||||
data(beta, 'beta_med_data_ipp-beta_med_data',
|
||||
(0,1), 2, rtol=5e-13),
|
||||
|
||||
data(betainc, 'ibeta_small_data_ipp-ibeta_small_data',
|
||||
(0,1,2), 5, rtol=6e-15),
|
||||
data(betainc, 'ibeta_data_ipp-ibeta_data',
|
||||
(0,1,2), 5, rtol=5e-13),
|
||||
data(betainc, 'ibeta_int_data_ipp-ibeta_int_data',
|
||||
(0,1,2), 5, rtol=2e-14),
|
||||
data(betainc, 'ibeta_large_data_ipp-ibeta_large_data',
|
||||
(0,1,2), 5, rtol=4e-10),
|
||||
|
||||
data(betaincinv, 'ibeta_inv_data_ipp-ibeta_inv_data',
|
||||
(0,1,2), 3, rtol=1e-5),
|
||||
|
||||
data(btdtria, 'ibeta_inva_data_ipp-ibeta_inva_data',
|
||||
(2,0,1), 3, rtol=5e-9),
|
||||
data(btdtria_comp, 'ibeta_inva_data_ipp-ibeta_inva_data',
|
||||
(2,0,1), 4, rtol=5e-9),
|
||||
|
||||
data(btdtrib, 'ibeta_inva_data_ipp-ibeta_inva_data',
|
||||
(0,2,1), 5, rtol=5e-9),
|
||||
data(btdtrib_comp, 'ibeta_inva_data_ipp-ibeta_inva_data',
|
||||
(0,2,1), 6, rtol=5e-9),
|
||||
|
||||
data(binom, 'binomial_data_ipp-binomial_data',
|
||||
(0,1), 2, rtol=1e-13),
|
||||
data(binom, 'binomial_large_data_ipp-binomial_large_data',
|
||||
(0,1), 2, rtol=5e-13),
|
||||
|
||||
data(bdtrik, 'binomial_quantile_ipp-binomial_quantile_data',
|
||||
(2,0,1), 3, rtol=5e-9),
|
||||
data(bdtrik_comp, 'binomial_quantile_ipp-binomial_quantile_data',
|
||||
(2,0,1), 4, rtol=5e-9),
|
||||
|
||||
data(nbdtrik, 'negative_binomial_quantile_ipp-negative_binomial_quantile_data',
|
||||
(2,0,1), 3, rtol=4e-9),
|
||||
data(nbdtrik_comp,
|
||||
'negative_binomial_quantile_ipp-negative_binomial_quantile_data',
|
||||
(2,0,1), 4, rtol=4e-9),
|
||||
|
||||
data(pdtrik, 'poisson_quantile_ipp-poisson_quantile_data',
|
||||
(1,0), 2, rtol=3e-9),
|
||||
data(pdtrik_comp, 'poisson_quantile_ipp-poisson_quantile_data',
|
||||
(1,0), 3, rtol=4e-9),
|
||||
|
||||
data(cbrt, 'cbrt_data_ipp-cbrt_data', 1, 0),
|
||||
|
||||
data(digamma, 'digamma_data_ipp-digamma_data', 0, 1),
|
||||
data(digamma, 'digamma_data_ipp-digamma_data', 0j, 1),
|
||||
data(digamma, 'digamma_neg_data_ipp-digamma_neg_data', 0, 1, rtol=2e-13),
|
||||
data(digamma, 'digamma_neg_data_ipp-digamma_neg_data', 0j, 1, rtol=1e-13),
|
||||
data(digamma, 'digamma_root_data_ipp-digamma_root_data', 0, 1, rtol=1e-15),
|
||||
data(digamma, 'digamma_root_data_ipp-digamma_root_data', 0j, 1, rtol=1e-15),
|
||||
data(digamma, 'digamma_small_data_ipp-digamma_small_data', 0, 1, rtol=1e-15),
|
||||
data(digamma, 'digamma_small_data_ipp-digamma_small_data', 0j, 1, rtol=1e-14),
|
||||
|
||||
data(ellipk_, 'ellint_k_data_ipp-ellint_k_data', 0, 1),
|
||||
data(ellipkinc_, 'ellint_f_data_ipp-ellint_f_data', (0,1), 2, rtol=1e-14),
|
||||
data(ellipe_, 'ellint_e_data_ipp-ellint_e_data', 0, 1),
|
||||
data(ellipeinc_, 'ellint_e2_data_ipp-ellint_e2_data', (0,1), 2, rtol=1e-14),
|
||||
|
||||
data(erf, 'erf_data_ipp-erf_data', 0, 1),
|
||||
data(erf, 'erf_data_ipp-erf_data', 0j, 1, rtol=1e-13),
|
||||
data(erfc, 'erf_data_ipp-erf_data', 0, 2, rtol=6e-15),
|
||||
data(erf, 'erf_large_data_ipp-erf_large_data', 0, 1),
|
||||
data(erf, 'erf_large_data_ipp-erf_large_data', 0j, 1),
|
||||
data(erfc, 'erf_large_data_ipp-erf_large_data', 0, 2, rtol=4e-14),
|
||||
data(erf, 'erf_small_data_ipp-erf_small_data', 0, 1),
|
||||
data(erf, 'erf_small_data_ipp-erf_small_data', 0j, 1, rtol=1e-13),
|
||||
data(erfc, 'erf_small_data_ipp-erf_small_data', 0, 2),
|
||||
|
||||
data(erfinv, 'erf_inv_data_ipp-erf_inv_data', 0, 1),
|
||||
data(erfcinv, 'erfc_inv_data_ipp-erfc_inv_data', 0, 1),
|
||||
data(erfcinv, 'erfc_inv_big_data_ipp-erfc_inv_big_data', 0, 1,
|
||||
param_filter=(lambda s: s > 0)),
|
||||
|
||||
data(exp1, 'expint_1_data_ipp-expint_1_data', 1, 2, rtol=1e-13),
|
||||
data(exp1, 'expint_1_data_ipp-expint_1_data', 1j, 2, rtol=5e-9),
|
||||
data(expi, 'expinti_data_ipp-expinti_data', 0, 1, rtol=1e-13),
|
||||
data(expi, 'expinti_data_double_ipp-expinti_data_double', 0, 1, rtol=1e-13),
|
||||
data(expi, 'expinti_data_long_ipp-expinti_data_long', 0, 1),
|
||||
|
||||
data(expn, 'expint_small_data_ipp-expint_small_data', (0,1), 2),
|
||||
data(expn, 'expint_data_ipp-expint_data', (0,1), 2, rtol=1e-14),
|
||||
|
||||
data(gamma, 'test_gamma_data_ipp-near_0', 0, 1),
|
||||
data(gamma, 'test_gamma_data_ipp-near_1', 0, 1),
|
||||
data(gamma, 'test_gamma_data_ipp-near_2', 0, 1),
|
||||
data(gamma, 'test_gamma_data_ipp-near_m10', 0, 1),
|
||||
data(gamma, 'test_gamma_data_ipp-near_m55', 0, 1, rtol=7e-12),
|
||||
data(gamma, 'test_gamma_data_ipp-factorials', 0, 1, rtol=4e-14),
|
||||
data(gamma, 'test_gamma_data_ipp-near_0', 0j, 1, rtol=2e-9),
|
||||
data(gamma, 'test_gamma_data_ipp-near_1', 0j, 1, rtol=2e-9),
|
||||
data(gamma, 'test_gamma_data_ipp-near_2', 0j, 1, rtol=2e-9),
|
||||
data(gamma, 'test_gamma_data_ipp-near_m10', 0j, 1, rtol=2e-9),
|
||||
data(gamma, 'test_gamma_data_ipp-near_m55', 0j, 1, rtol=2e-9),
|
||||
data(gamma, 'test_gamma_data_ipp-factorials', 0j, 1, rtol=2e-13),
|
||||
data(gammaln, 'test_gamma_data_ipp-near_0', 0, 2, rtol=5e-11),
|
||||
data(gammaln, 'test_gamma_data_ipp-near_1', 0, 2, rtol=5e-11),
|
||||
data(gammaln, 'test_gamma_data_ipp-near_2', 0, 2, rtol=2e-10),
|
||||
data(gammaln, 'test_gamma_data_ipp-near_m10', 0, 2, rtol=5e-11),
|
||||
data(gammaln, 'test_gamma_data_ipp-near_m55', 0, 2, rtol=5e-11),
|
||||
data(gammaln, 'test_gamma_data_ipp-factorials', 0, 2),
|
||||
|
||||
data(gammainc, 'igamma_small_data_ipp-igamma_small_data', (0,1), 5, rtol=5e-15),
|
||||
data(gammainc, 'igamma_med_data_ipp-igamma_med_data', (0,1), 5, rtol=2e-13),
|
||||
data(gammainc, 'igamma_int_data_ipp-igamma_int_data', (0,1), 5, rtol=2e-13),
|
||||
data(gammainc, 'igamma_big_data_ipp-igamma_big_data', (0,1), 5, rtol=1e-12),
|
||||
|
||||
data(gdtr_, 'igamma_small_data_ipp-igamma_small_data', (0,1), 5, rtol=1e-13),
|
||||
data(gdtr_, 'igamma_med_data_ipp-igamma_med_data', (0,1), 5, rtol=2e-13),
|
||||
data(gdtr_, 'igamma_int_data_ipp-igamma_int_data', (0,1), 5, rtol=2e-13),
|
||||
data(gdtr_, 'igamma_big_data_ipp-igamma_big_data', (0,1), 5, rtol=2e-9),
|
||||
|
||||
data(gammaincc, 'igamma_small_data_ipp-igamma_small_data',
|
||||
(0,1), 3, rtol=1e-13),
|
||||
data(gammaincc, 'igamma_med_data_ipp-igamma_med_data',
|
||||
(0,1), 3, rtol=2e-13),
|
||||
data(gammaincc, 'igamma_int_data_ipp-igamma_int_data',
|
||||
(0,1), 3, rtol=4e-14),
|
||||
data(gammaincc, 'igamma_big_data_ipp-igamma_big_data',
|
||||
(0,1), 3, rtol=1e-11),
|
||||
|
||||
data(gdtrc_, 'igamma_small_data_ipp-igamma_small_data', (0,1), 3, rtol=1e-13),
|
||||
data(gdtrc_, 'igamma_med_data_ipp-igamma_med_data', (0,1), 3, rtol=2e-13),
|
||||
data(gdtrc_, 'igamma_int_data_ipp-igamma_int_data', (0,1), 3, rtol=4e-14),
|
||||
data(gdtrc_, 'igamma_big_data_ipp-igamma_big_data', (0,1), 3, rtol=1e-11),
|
||||
|
||||
data(gdtrib_, 'igamma_inva_data_ipp-igamma_inva_data', (1,0), 2, rtol=5e-9),
|
||||
data(gdtrib_comp, 'igamma_inva_data_ipp-igamma_inva_data', (1,0), 3, rtol=5e-9),
|
||||
|
||||
data(poch_, 'tgamma_delta_ratio_data_ipp-tgamma_delta_ratio_data',
|
||||
(0,1), 2, rtol=2e-13),
|
||||
data(poch_, 'tgamma_delta_ratio_int_ipp-tgamma_delta_ratio_int',
|
||||
(0,1), 2,),
|
||||
data(poch_, 'tgamma_delta_ratio_int2_ipp-tgamma_delta_ratio_int2',
|
||||
(0,1), 2,),
|
||||
data(poch_minus, 'tgamma_delta_ratio_data_ipp-tgamma_delta_ratio_data',
|
||||
(0,1), 3, rtol=2e-13),
|
||||
data(poch_minus, 'tgamma_delta_ratio_int_ipp-tgamma_delta_ratio_int',
|
||||
(0,1), 3),
|
||||
data(poch_minus, 'tgamma_delta_ratio_int2_ipp-tgamma_delta_ratio_int2',
|
||||
(0,1), 3),
|
||||
|
||||
data(eval_hermite_ld, 'hermite_ipp-hermite',
|
||||
(0,1), 2, rtol=2e-14),
|
||||
|
||||
data(eval_laguerre_ld, 'laguerre2_ipp-laguerre2',
|
||||
(0,1), 2, rtol=7e-12),
|
||||
data(eval_laguerre_dd, 'laguerre2_ipp-laguerre2',
|
||||
(0,1), 2, knownfailure='hyp2f1 insufficiently accurate.'),
|
||||
data(eval_genlaguerre_ldd, 'laguerre3_ipp-laguerre3',
|
||||
(0,1,2), 3, rtol=2e-13),
|
||||
data(eval_genlaguerre_ddd, 'laguerre3_ipp-laguerre3',
|
||||
(0,1,2), 3, knownfailure='hyp2f1 insufficiently accurate.'),
|
||||
|
||||
data(log1p, 'log1p_expm1_data_ipp-log1p_expm1_data', 0, 1),
|
||||
data(expm1, 'log1p_expm1_data_ipp-log1p_expm1_data', 0, 2),
|
||||
|
||||
data(iv, 'bessel_i_data_ipp-bessel_i_data',
|
||||
(0,1), 2, rtol=1e-12),
|
||||
data(iv, 'bessel_i_data_ipp-bessel_i_data',
|
||||
(0,1j), 2, rtol=2e-10, atol=1e-306),
|
||||
data(iv, 'bessel_i_int_data_ipp-bessel_i_int_data',
|
||||
(0,1), 2, rtol=1e-9),
|
||||
data(iv, 'bessel_i_int_data_ipp-bessel_i_int_data',
|
||||
(0,1j), 2, rtol=2e-10),
|
||||
|
||||
data(ivp, 'bessel_i_prime_int_data_ipp-bessel_i_prime_int_data',
|
||||
(0,1), 2, rtol=1.2e-13),
|
||||
data(ivp, 'bessel_i_prime_int_data_ipp-bessel_i_prime_int_data',
|
||||
(0,1j), 2, rtol=1.2e-13, atol=1e-300),
|
||||
|
||||
data(jn, 'bessel_j_int_data_ipp-bessel_j_int_data', (0,1), 2, rtol=1e-12),
|
||||
data(jn, 'bessel_j_int_data_ipp-bessel_j_int_data', (0,1j), 2, rtol=1e-12),
|
||||
data(jn, 'bessel_j_large_data_ipp-bessel_j_large_data', (0,1), 2, rtol=6e-11),
|
||||
data(jn, 'bessel_j_large_data_ipp-bessel_j_large_data', (0,1j), 2, rtol=6e-11),
|
||||
|
||||
data(jv, 'bessel_j_int_data_ipp-bessel_j_int_data', (0,1), 2, rtol=1e-12),
|
||||
data(jv, 'bessel_j_int_data_ipp-bessel_j_int_data', (0,1j), 2, rtol=1e-12),
|
||||
data(jv, 'bessel_j_data_ipp-bessel_j_data', (0,1), 2, rtol=1e-12),
|
||||
data(jv, 'bessel_j_data_ipp-bessel_j_data', (0,1j), 2, rtol=1e-12),
|
||||
|
||||
data(jvp, 'bessel_j_prime_int_data_ipp-bessel_j_prime_int_data',
|
||||
(0,1), 2, rtol=1e-13),
|
||||
data(jvp, 'bessel_j_prime_int_data_ipp-bessel_j_prime_int_data',
|
||||
(0,1j), 2, rtol=1e-13),
|
||||
data(jvp, 'bessel_j_prime_large_data_ipp-bessel_j_prime_large_data',
|
||||
(0,1), 2, rtol=1e-11),
|
||||
data(jvp, 'bessel_j_prime_large_data_ipp-bessel_j_prime_large_data',
|
||||
(0,1j), 2, rtol=2e-11),
|
||||
|
||||
data(kn, 'bessel_k_int_data_ipp-bessel_k_int_data', (0,1), 2, rtol=1e-12),
|
||||
|
||||
data(kv, 'bessel_k_int_data_ipp-bessel_k_int_data', (0,1), 2, rtol=1e-12),
|
||||
data(kv, 'bessel_k_int_data_ipp-bessel_k_int_data', (0,1j), 2, rtol=1e-12),
|
||||
data(kv, 'bessel_k_data_ipp-bessel_k_data', (0,1), 2, rtol=1e-12),
|
||||
data(kv, 'bessel_k_data_ipp-bessel_k_data', (0,1j), 2, rtol=1e-12),
|
||||
|
||||
data(kvp, 'bessel_k_prime_int_data_ipp-bessel_k_prime_int_data',
|
||||
(0,1), 2, rtol=3e-14),
|
||||
data(kvp, 'bessel_k_prime_int_data_ipp-bessel_k_prime_int_data',
|
||||
(0,1j), 2, rtol=3e-14),
|
||||
data(kvp, 'bessel_k_prime_data_ipp-bessel_k_prime_data', (0,1), 2, rtol=7e-14),
|
||||
data(kvp, 'bessel_k_prime_data_ipp-bessel_k_prime_data', (0,1j), 2, rtol=7e-14),
|
||||
|
||||
data(yn, 'bessel_y01_data_ipp-bessel_y01_data', (0,1), 2, rtol=1e-12),
|
||||
data(yn, 'bessel_yn_data_ipp-bessel_yn_data', (0,1), 2, rtol=1e-12),
|
||||
|
||||
data(yv, 'bessel_yn_data_ipp-bessel_yn_data', (0,1), 2, rtol=1e-12),
|
||||
data(yv, 'bessel_yn_data_ipp-bessel_yn_data', (0,1j), 2, rtol=1e-12),
|
||||
data(yv, 'bessel_yv_data_ipp-bessel_yv_data', (0,1), 2, rtol=1e-10),
|
||||
data(yv, 'bessel_yv_data_ipp-bessel_yv_data', (0,1j), 2, rtol=1e-10),
|
||||
|
||||
data(yvp, 'bessel_yv_prime_data_ipp-bessel_yv_prime_data',
|
||||
(0, 1), 2, rtol=4e-9),
|
||||
data(yvp, 'bessel_yv_prime_data_ipp-bessel_yv_prime_data',
|
||||
(0, 1j), 2, rtol=4e-9),
|
||||
|
||||
data(zeta_, 'zeta_data_ipp-zeta_data', 0, 1,
|
||||
param_filter=(lambda s: s > 1)),
|
||||
data(zeta_, 'zeta_neg_data_ipp-zeta_neg_data', 0, 1,
|
||||
param_filter=(lambda s: s > 1)),
|
||||
data(zeta_, 'zeta_1_up_data_ipp-zeta_1_up_data', 0, 1,
|
||||
param_filter=(lambda s: s > 1)),
|
||||
data(zeta_, 'zeta_1_below_data_ipp-zeta_1_below_data', 0, 1,
|
||||
param_filter=(lambda s: s > 1)),
|
||||
|
||||
data(gammaincinv, 'gamma_inv_small_data_ipp-gamma_inv_small_data',
|
||||
(0,1), 2, rtol=1e-11),
|
||||
data(gammaincinv, 'gamma_inv_data_ipp-gamma_inv_data',
|
||||
(0,1), 2, rtol=1e-14),
|
||||
data(gammaincinv, 'gamma_inv_big_data_ipp-gamma_inv_big_data',
|
||||
(0,1), 2, rtol=1e-11),
|
||||
|
||||
data(gammainccinv, 'gamma_inv_small_data_ipp-gamma_inv_small_data',
|
||||
(0,1), 3, rtol=1e-12),
|
||||
data(gammainccinv, 'gamma_inv_data_ipp-gamma_inv_data',
|
||||
(0,1), 3, rtol=1e-14),
|
||||
data(gammainccinv, 'gamma_inv_big_data_ipp-gamma_inv_big_data',
|
||||
(0,1), 3, rtol=1e-14),
|
||||
|
||||
data(gdtrix_, 'gamma_inv_small_data_ipp-gamma_inv_small_data',
|
||||
(0,1), 2, rtol=3e-13, knownfailure='gdtrix unflow some points'),
|
||||
data(gdtrix_, 'gamma_inv_data_ipp-gamma_inv_data',
|
||||
(0,1), 2, rtol=3e-15),
|
||||
data(gdtrix_, 'gamma_inv_big_data_ipp-gamma_inv_big_data',
|
||||
(0,1), 2),
|
||||
data(gdtrix_comp, 'gamma_inv_small_data_ipp-gamma_inv_small_data',
|
||||
(0,1), 2, knownfailure='gdtrix bad some points'),
|
||||
data(gdtrix_comp, 'gamma_inv_data_ipp-gamma_inv_data',
|
||||
(0,1), 3, rtol=6e-15),
|
||||
data(gdtrix_comp, 'gamma_inv_big_data_ipp-gamma_inv_big_data',
|
||||
(0,1), 3),
|
||||
|
||||
data(chndtr, 'nccs_ipp-nccs',
|
||||
(2,0,1), 3, rtol=3e-5),
|
||||
data(chndtr, 'nccs_big_ipp-nccs_big',
|
||||
(2,0,1), 3, rtol=5e-4, knownfailure='chndtr inaccurate some points'),
|
||||
|
||||
data(sph_harm_, 'spherical_harmonic_ipp-spherical_harmonic',
|
||||
(1,0,3,2), (4,5), rtol=5e-11,
|
||||
param_filter=(lambda p: np.ones(p.shape, '?'),
|
||||
lambda p: np.ones(p.shape, '?'),
|
||||
lambda p: np.logical_and(p < 2*np.pi, p >= 0),
|
||||
lambda p: np.logical_and(p < np.pi, p >= 0))),
|
||||
|
||||
data(spherical_jn_, 'sph_bessel_data_ipp-sph_bessel_data',
|
||||
(0,1), 2, rtol=1e-13),
|
||||
data(spherical_yn_, 'sph_neumann_data_ipp-sph_neumann_data',
|
||||
(0,1), 2, rtol=8e-15),
|
||||
|
||||
data(owens_t, 'owens_t_ipp-owens_t',
|
||||
(0, 1), 2, rtol=5e-14),
|
||||
data(owens_t, 'owens_t_large_data_ipp-owens_t_large_data',
|
||||
(0, 1), 2, rtol=8e-12),
|
||||
|
||||
# -- test data exists in boost but is not used in scipy --
|
||||
|
||||
# ibeta_derivative_data_ipp/ibeta_derivative_data.txt
|
||||
# ibeta_derivative_int_data_ipp/ibeta_derivative_int_data.txt
|
||||
# ibeta_derivative_large_data_ipp/ibeta_derivative_large_data.txt
|
||||
# ibeta_derivative_small_data_ipp/ibeta_derivative_small_data.txt
|
||||
|
||||
# bessel_y01_prime_data_ipp/bessel_y01_prime_data.txt
|
||||
# bessel_yn_prime_data_ipp/bessel_yn_prime_data.txt
|
||||
# sph_bessel_prime_data_ipp/sph_bessel_prime_data.txt
|
||||
# sph_neumann_prime_data_ipp/sph_neumann_prime_data.txt
|
||||
|
||||
# ellint_d2_data_ipp/ellint_d2_data.txt
|
||||
# ellint_d_data_ipp/ellint_d_data.txt
|
||||
# ellint_pi2_data_ipp/ellint_pi2_data.txt
|
||||
# ellint_pi3_data_ipp/ellint_pi3_data.txt
|
||||
# ellint_pi3_large_data_ipp/ellint_pi3_large_data.txt
|
||||
data(elliprc, 'ellint_rc_data_ipp-ellint_rc_data', (0, 1), 2,
|
||||
rtol=5e-16),
|
||||
data(elliprd, 'ellint_rd_data_ipp-ellint_rd_data', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprd, 'ellint_rd_0xy_ipp-ellint_rd_0xy', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprd, 'ellint_rd_0yy_ipp-ellint_rd_0yy', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprd, 'ellint_rd_xxx_ipp-ellint_rd_xxx', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
# Some of the following rtol for elliprd may be larger than 5e-16 to
|
||||
# work around some hard cases in the Boost test where we get slightly
|
||||
# larger error than the ideal bound when the x (==y) input is close to
|
||||
# zero.
|
||||
# Also the accuracy on 32-bit builds with g++ may suffer from excess
|
||||
# loss of precision; see GCC bugzilla 323
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
|
||||
data(elliprd, 'ellint_rd_xxz_ipp-ellint_rd_xxz', (0, 1, 2), 3,
|
||||
rtol=6.5e-16),
|
||||
data(elliprd, 'ellint_rd_xyy_ipp-ellint_rd_xyy', (0, 1, 2), 3,
|
||||
rtol=6e-16),
|
||||
data(elliprf, 'ellint_rf_data_ipp-ellint_rf_data', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprf, 'ellint_rf_xxx_ipp-ellint_rf_xxx', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprf, 'ellint_rf_xyy_ipp-ellint_rf_xyy', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprf, 'ellint_rf_xy0_ipp-ellint_rf_xy0', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprf, 'ellint_rf_0yy_ipp-ellint_rf_0yy', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
# The accuracy of R_G is primarily limited by R_D that is used
|
||||
# internally. It is generally worse than R_D. Notice that we increased
|
||||
# the rtol for R_G here. The cases with duplicate arguments are
|
||||
# slightly less likely to be unbalanced (at least two arguments are
|
||||
# already balanced) so the error bound is slightly better. Again,
|
||||
# precision with g++ 32-bit is even worse.
|
||||
data(elliprg, 'ellint_rg_ipp-ellint_rg', (0, 1, 2), 3,
|
||||
rtol=8.0e-16),
|
||||
data(elliprg, 'ellint_rg_xxx_ipp-ellint_rg_xxx', (0, 1, 2), 3,
|
||||
rtol=6e-16),
|
||||
data(elliprg, 'ellint_rg_xyy_ipp-ellint_rg_xyy', (0, 1, 2), 3,
|
||||
rtol=7.5e-16),
|
||||
data(elliprg, 'ellint_rg_xy0_ipp-ellint_rg_xy0', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprg, 'ellint_rg_00x_ipp-ellint_rg_00x', (0, 1, 2), 3,
|
||||
rtol=5e-16),
|
||||
data(elliprj, 'ellint_rj_data_ipp-ellint_rj_data', (0, 1, 2, 3), 4,
|
||||
rtol=5e-16, atol=1e-25,
|
||||
param_filter=(lambda s: s <= 5e-26,)),
|
||||
# ellint_rc_data_ipp/ellint_rc_data.txt
|
||||
# ellint_rd_0xy_ipp/ellint_rd_0xy.txt
|
||||
# ellint_rd_0yy_ipp/ellint_rd_0yy.txt
|
||||
# ellint_rd_data_ipp/ellint_rd_data.txt
|
||||
# ellint_rd_xxx_ipp/ellint_rd_xxx.txt
|
||||
# ellint_rd_xxz_ipp/ellint_rd_xxz.txt
|
||||
# ellint_rd_xyy_ipp/ellint_rd_xyy.txt
|
||||
# ellint_rf_0yy_ipp/ellint_rf_0yy.txt
|
||||
# ellint_rf_data_ipp/ellint_rf_data.txt
|
||||
# ellint_rf_xxx_ipp/ellint_rf_xxx.txt
|
||||
# ellint_rf_xy0_ipp/ellint_rf_xy0.txt
|
||||
# ellint_rf_xyy_ipp/ellint_rf_xyy.txt
|
||||
# ellint_rg_00x_ipp/ellint_rg_00x.txt
|
||||
# ellint_rg_ipp/ellint_rg.txt
|
||||
# ellint_rg_xxx_ipp/ellint_rg_xxx.txt
|
||||
# ellint_rg_xy0_ipp/ellint_rg_xy0.txt
|
||||
# ellint_rg_xyy_ipp/ellint_rg_xyy.txt
|
||||
# ellint_rj_data_ipp/ellint_rj_data.txt
|
||||
# ellint_rj_e2_ipp/ellint_rj_e2.txt
|
||||
# ellint_rj_e3_ipp/ellint_rj_e3.txt
|
||||
# ellint_rj_e4_ipp/ellint_rj_e4.txt
|
||||
# ellint_rj_zp_ipp/ellint_rj_zp.txt
|
||||
|
||||
# jacobi_elliptic_ipp/jacobi_elliptic.txt
|
||||
# jacobi_elliptic_small_ipp/jacobi_elliptic_small.txt
|
||||
# jacobi_large_phi_ipp/jacobi_large_phi.txt
|
||||
# jacobi_near_1_ipp/jacobi_near_1.txt
|
||||
# jacobi_zeta_big_phi_ipp/jacobi_zeta_big_phi.txt
|
||||
# jacobi_zeta_data_ipp/jacobi_zeta_data.txt
|
||||
|
||||
# heuman_lambda_data_ipp/heuman_lambda_data.txt
|
||||
|
||||
# hypergeometric_0F2_ipp/hypergeometric_0F2.txt
|
||||
# hypergeometric_1F1_big_ipp/hypergeometric_1F1_big.txt
|
||||
# hypergeometric_1F1_ipp/hypergeometric_1F1.txt
|
||||
# hypergeometric_1F1_small_random_ipp/hypergeometric_1F1_small_random.txt
|
||||
# hypergeometric_1F2_ipp/hypergeometric_1F2.txt
|
||||
# hypergeometric_1f1_large_regularized_ipp/hypergeometric_1f1_large_regularized.txt # noqa: E501
|
||||
# hypergeometric_1f1_log_large_unsolved_ipp/hypergeometric_1f1_log_large_unsolved.txt # noqa: E501
|
||||
# hypergeometric_2F0_half_ipp/hypergeometric_2F0_half.txt
|
||||
# hypergeometric_2F0_integer_a2_ipp/hypergeometric_2F0_integer_a2.txt
|
||||
# hypergeometric_2F0_ipp/hypergeometric_2F0.txt
|
||||
# hypergeometric_2F0_large_z_ipp/hypergeometric_2F0_large_z.txt
|
||||
# hypergeometric_2F1_ipp/hypergeometric_2F1.txt
|
||||
# hypergeometric_2F2_ipp/hypergeometric_2F2.txt
|
||||
|
||||
# ncbeta_big_ipp/ncbeta_big.txt
|
||||
# nct_small_delta_ipp/nct_small_delta.txt
|
||||
# nct_asym_ipp/nct_asym.txt
|
||||
# ncbeta_ipp/ncbeta.txt
|
||||
|
||||
# powm1_data_ipp/powm1_big_data.txt
|
||||
# powm1_sqrtp1m1_test_hpp/sqrtp1m1_data.txt
|
||||
|
||||
# sinc_data_ipp/sinc_data.txt
|
||||
|
||||
# test_gamma_data_ipp/gammap1m1_data.txt
|
||||
# tgamma_ratio_data_ipp/tgamma_ratio_data.txt
|
||||
|
||||
# trig_data_ipp/trig_data.txt
|
||||
# trig_data2_ipp/trig_data2.txt
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.thread_unsafe
|
||||
@pytest.mark.parametrize('test', BOOST_TESTS, ids=repr)
|
||||
def test_boost(test):
|
||||
_test_factory(test)
|
||||
|
||||
|
||||
GSL_TESTS = [
|
||||
data_gsl(mathieu_a, 'mathieu_ab', (0, 1), 2, rtol=1e-13, atol=1e-13),
|
||||
data_gsl(mathieu_b, 'mathieu_ab', (0, 1), 3, rtol=1e-13, atol=1e-13),
|
||||
|
||||
# Also the GSL output has limited accuracy...
|
||||
data_gsl(mathieu_ce_rad, 'mathieu_ce_se', (0, 1, 2), 3, rtol=1e-7, atol=1e-13),
|
||||
data_gsl(mathieu_se_rad, 'mathieu_ce_se', (0, 1, 2), 4, rtol=1e-7, atol=1e-13),
|
||||
|
||||
data_gsl(mathieu_mc1_scaled, 'mathieu_mc_ms',
|
||||
(0, 1, 2), 3, rtol=1e-7, atol=1e-13),
|
||||
data_gsl(mathieu_ms1_scaled, 'mathieu_mc_ms',
|
||||
(0, 1, 2), 4, rtol=1e-7, atol=1e-13),
|
||||
|
||||
data_gsl(mathieu_mc2_scaled, 'mathieu_mc_ms',
|
||||
(0, 1, 2), 5, rtol=1e-7, atol=1e-13),
|
||||
data_gsl(mathieu_ms2_scaled, 'mathieu_mc_ms',
|
||||
(0, 1, 2), 6, rtol=1e-7, atol=1e-13),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test', GSL_TESTS, ids=repr)
|
||||
def test_gsl(test):
|
||||
_test_factory(test)
|
||||
|
||||
|
||||
LOCAL_TESTS = [
|
||||
data_local(ellipkinc, 'ellipkinc_neg_m', (0, 1), 2),
|
||||
data_local(ellipkm1, 'ellipkm1', 0, 1),
|
||||
data_local(ellipeinc, 'ellipeinc_neg_m', (0, 1), 2),
|
||||
data_local(clog1p, 'log1p_expm1_complex', (0,1), (2,3), rtol=1e-14),
|
||||
data_local(cexpm1, 'log1p_expm1_complex', (0,1), (4,5), rtol=1e-14),
|
||||
data_local(gammainc, 'gammainc', (0, 1), 2, rtol=1e-12),
|
||||
data_local(gammaincc, 'gammaincc', (0, 1), 2, rtol=1e-11),
|
||||
data_local(ellip_harm_2, 'ellip',(0, 1, 2, 3, 4), 6, rtol=1e-10, atol=1e-13),
|
||||
data_local(ellip_harm, 'ellip',(0, 1, 2, 3, 4), 5, rtol=1e-10, atol=1e-13),
|
||||
data_local(wright_bessel, 'wright_bessel', (0, 1, 2), 3, rtol=1e-11),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test', LOCAL_TESTS, ids=repr)
|
||||
def test_local(test):
|
||||
_test_factory(test)
|
||||
|
||||
|
||||
def _test_factory(test, dtype=np.float64):
|
||||
"""Boost test"""
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(IntegrationWarning, "The occurrence of roundoff error is detected")
|
||||
with np.errstate(all='ignore'):
|
||||
test.check(dtype=dtype)
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# Tests for a few of the "double-double" C++ functions defined in
|
||||
# special/cephes/dd_real.h. Prior to gh-20390 which translated these
|
||||
# functions from C to C++, there were test cases for _dd_expm1. It
|
||||
# was determined that this function is not used anywhere internally
|
||||
# in SciPy, so this function was not translated.
|
||||
|
||||
|
||||
import pytest
|
||||
from numpy.testing import assert_allclose
|
||||
from scipy.special._test_internal import _dd_exp, _dd_log
|
||||
|
||||
|
||||
# Each tuple in test_data contains:
|
||||
# (dd_func, xhi, xlo, expected_yhi, expected_ylo)
|
||||
# The expected values were computed with mpmath, e.g.
|
||||
#
|
||||
# import mpmath
|
||||
# mpmath.mp.dps = 100
|
||||
# xhi = 10.0
|
||||
# xlo = 0.0
|
||||
# x = mpmath.mpf(xhi) + mpmath.mpf(xlo)
|
||||
# y = mpmath.log(x)
|
||||
# expected_yhi = float(y)
|
||||
# expected_ylo = float(y - expected_yhi)
|
||||
#
|
||||
test_data = [
|
||||
(_dd_exp, -0.3333333333333333, -1.850371707708594e-17,
|
||||
0.7165313105737893, -2.0286948382455594e-17),
|
||||
(_dd_exp, 0.0, 0.0, 1.0, 0.0),
|
||||
(_dd_exp, 10.0, 0.0, 22026.465794806718, -1.3780134700517372e-12),
|
||||
(_dd_log, 0.03125, 0.0, -3.4657359027997265, -4.930038229799327e-18),
|
||||
(_dd_log, 10.0, 0.0, 2.302585092994046, -2.1707562233822494e-16),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dd_func, xhi, xlo, expected_yhi, expected_ylo',
|
||||
test_data)
|
||||
def test_dd(dd_func, xhi, xlo, expected_yhi, expected_ylo):
|
||||
yhi, ylo = dd_func(xhi, xlo)
|
||||
assert yhi == expected_yhi, (f"high double ({yhi}) does not equal the "
|
||||
f"expected value {expected_yhi}")
|
||||
assert_allclose(ylo, expected_ylo, rtol=5e-15)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import numpy as np
|
||||
from numpy import pi, log, sqrt
|
||||
from numpy.testing import assert_, assert_equal
|
||||
|
||||
from scipy.special._testutils import FuncData
|
||||
import scipy.special as sc
|
||||
|
||||
# Euler-Mascheroni constant
|
||||
euler = 0.57721566490153286
|
||||
|
||||
|
||||
def test_consistency():
|
||||
# Make sure the implementation of digamma for real arguments
|
||||
# agrees with the implementation of digamma for complex arguments.
|
||||
|
||||
# It's all poles after -1e16
|
||||
x = np.r_[-np.logspace(15, -30, 200), np.logspace(-30, 300, 200)]
|
||||
dataset = np.vstack((x + 0j, sc.digamma(x))).T
|
||||
FuncData(sc.digamma, dataset, 0, 1, rtol=5e-14, nan_ok=True).check()
|
||||
|
||||
|
||||
def test_special_values():
|
||||
# Test special values from Gauss's digamma theorem. See
|
||||
#
|
||||
# https://en.wikipedia.org/wiki/Digamma_function
|
||||
|
||||
dataset = [
|
||||
(1, -euler),
|
||||
(0.5, -2*log(2) - euler),
|
||||
(1/3, -pi/(2*sqrt(3)) - 3*log(3)/2 - euler),
|
||||
(1/4, -pi/2 - 3*log(2) - euler),
|
||||
(1/6, -pi*sqrt(3)/2 - 2*log(2) - 3*log(3)/2 - euler),
|
||||
(1/8,
|
||||
-pi/2 - 4*log(2) - (pi + log(2 + sqrt(2)) - log(2 - sqrt(2)))/sqrt(2) - euler)
|
||||
]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(sc.digamma, dataset, 0, 1, rtol=1e-14).check()
|
||||
|
||||
|
||||
def test_nonfinite():
|
||||
pts = [0.0, -0.0, np.inf]
|
||||
std = [-np.inf, np.inf, np.inf]
|
||||
assert_equal(sc.digamma(pts), std)
|
||||
assert_(all(np.isnan(sc.digamma([-np.inf, -1]))))
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
#
|
||||
# Tests for the Ellipsoidal Harmonic Function,
|
||||
# Distributed under the same license as SciPy itself.
|
||||
#
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import (assert_equal, assert_almost_equal, assert_allclose,
|
||||
assert_, suppress_warnings)
|
||||
from scipy.special._testutils import assert_func_equal
|
||||
from scipy.special import ellip_harm, ellip_harm_2, ellip_normal
|
||||
from scipy.integrate import IntegrationWarning
|
||||
from numpy import sqrt, pi
|
||||
|
||||
|
||||
def test_ellip_potential():
|
||||
def change_coefficient(lambda1, mu, nu, h2, k2):
|
||||
x = sqrt(lambda1**2*mu**2*nu**2/(h2*k2))
|
||||
y = sqrt((lambda1**2 - h2)*(mu**2 - h2)*(h2 - nu**2)/(h2*(k2 - h2)))
|
||||
z = sqrt((lambda1**2 - k2)*(k2 - mu**2)*(k2 - nu**2)/(k2*(k2 - h2)))
|
||||
return x, y, z
|
||||
|
||||
def solid_int_ellip(lambda1, mu, nu, n, p, h2, k2):
|
||||
return (ellip_harm(h2, k2, n, p, lambda1)*ellip_harm(h2, k2, n, p, mu)
|
||||
* ellip_harm(h2, k2, n, p, nu))
|
||||
|
||||
def solid_int_ellip2(lambda1, mu, nu, n, p, h2, k2):
|
||||
return (ellip_harm_2(h2, k2, n, p, lambda1)
|
||||
* ellip_harm(h2, k2, n, p, mu)*ellip_harm(h2, k2, n, p, nu))
|
||||
|
||||
def summation(lambda1, mu1, nu1, lambda2, mu2, nu2, h2, k2):
|
||||
tol = 1e-8
|
||||
sum1 = 0
|
||||
for n in range(20):
|
||||
xsum = 0
|
||||
for p in range(1, 2*n+2):
|
||||
xsum += (4*pi*(solid_int_ellip(lambda2, mu2, nu2, n, p, h2, k2)
|
||||
* solid_int_ellip2(lambda1, mu1, nu1, n, p, h2, k2)) /
|
||||
(ellip_normal(h2, k2, n, p)*(2*n + 1)))
|
||||
if abs(xsum) < 0.1*tol*abs(sum1):
|
||||
break
|
||||
sum1 += xsum
|
||||
return sum1, xsum
|
||||
|
||||
def potential(lambda1, mu1, nu1, lambda2, mu2, nu2, h2, k2):
|
||||
x1, y1, z1 = change_coefficient(lambda1, mu1, nu1, h2, k2)
|
||||
x2, y2, z2 = change_coefficient(lambda2, mu2, nu2, h2, k2)
|
||||
res = sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2)
|
||||
return 1/res
|
||||
|
||||
pts = [
|
||||
(120, sqrt(19), 2, 41, sqrt(17), 2, 15, 25),
|
||||
(120, sqrt(16), 3.2, 21, sqrt(11), 2.9, 11, 20),
|
||||
]
|
||||
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(IntegrationWarning, "The occurrence of roundoff error")
|
||||
sup.filter(IntegrationWarning, "The maximum number of subdivisions")
|
||||
|
||||
for p in pts:
|
||||
err_msg = repr(p)
|
||||
exact = potential(*p)
|
||||
result, last_term = summation(*p)
|
||||
assert_allclose(exact, result, atol=0, rtol=1e-8, err_msg=err_msg)
|
||||
assert_(abs(result - exact) < 10*abs(last_term), err_msg)
|
||||
|
||||
|
||||
def test_ellip_norm():
|
||||
|
||||
def G01(h2, k2):
|
||||
return 4*pi
|
||||
|
||||
def G11(h2, k2):
|
||||
return 4*pi*h2*k2/3
|
||||
|
||||
def G12(h2, k2):
|
||||
return 4*pi*h2*(k2 - h2)/3
|
||||
|
||||
def G13(h2, k2):
|
||||
return 4*pi*k2*(k2 - h2)/3
|
||||
|
||||
def G22(h2, k2):
|
||||
res = (2*(h2**4 + k2**4) - 4*h2*k2*(h2**2 + k2**2) + 6*h2**2*k2**2 +
|
||||
sqrt(h2**2 + k2**2 - h2*k2)*(-2*(h2**3 + k2**3) + 3*h2*k2*(h2 + k2)))
|
||||
return 16*pi/405*res
|
||||
|
||||
def G21(h2, k2):
|
||||
res = (2*(h2**4 + k2**4) - 4*h2*k2*(h2**2 + k2**2) + 6*h2**2*k2**2
|
||||
+ sqrt(h2**2 + k2**2 - h2*k2)*(2*(h2**3 + k2**3) - 3*h2*k2*(h2 + k2)))
|
||||
return 16*pi/405*res
|
||||
|
||||
def G23(h2, k2):
|
||||
return 4*pi*h2**2*k2*(k2 - h2)/15
|
||||
|
||||
def G24(h2, k2):
|
||||
return 4*pi*h2*k2**2*(k2 - h2)/15
|
||||
|
||||
def G25(h2, k2):
|
||||
return 4*pi*h2*k2*(k2 - h2)**2/15
|
||||
|
||||
def G32(h2, k2):
|
||||
res = (16*(h2**4 + k2**4) - 36*h2*k2*(h2**2 + k2**2) + 46*h2**2*k2**2
|
||||
+ sqrt(4*(h2**2 + k2**2) - 7*h2*k2)*(-8*(h2**3 + k2**3) +
|
||||
11*h2*k2*(h2 + k2)))
|
||||
return 16*pi/13125*k2*h2*res
|
||||
|
||||
def G31(h2, k2):
|
||||
res = (16*(h2**4 + k2**4) - 36*h2*k2*(h2**2 + k2**2) + 46*h2**2*k2**2
|
||||
+ sqrt(4*(h2**2 + k2**2) - 7*h2*k2)*(8*(h2**3 + k2**3) -
|
||||
11*h2*k2*(h2 + k2)))
|
||||
return 16*pi/13125*h2*k2*res
|
||||
|
||||
def G34(h2, k2):
|
||||
res = (6*h2**4 + 16*k2**4 - 12*h2**3*k2 - 28*h2*k2**3 + 34*h2**2*k2**2
|
||||
+ sqrt(h2**2 + 4*k2**2 - h2*k2)*(-6*h2**3 - 8*k2**3 + 9*h2**2*k2 +
|
||||
13*h2*k2**2))
|
||||
return 16*pi/13125*h2*(k2 - h2)*res
|
||||
|
||||
def G33(h2, k2):
|
||||
res = (6*h2**4 + 16*k2**4 - 12*h2**3*k2 - 28*h2*k2**3 + 34*h2**2*k2**2
|
||||
+ sqrt(h2**2 + 4*k2**2 - h2*k2)*(6*h2**3 + 8*k2**3 - 9*h2**2*k2 -
|
||||
13*h2*k2**2))
|
||||
return 16*pi/13125*h2*(k2 - h2)*res
|
||||
|
||||
def G36(h2, k2):
|
||||
res = (16*h2**4 + 6*k2**4 - 28*h2**3*k2 - 12*h2*k2**3 + 34*h2**2*k2**2
|
||||
+ sqrt(4*h2**2 + k2**2 - h2*k2)*(-8*h2**3 - 6*k2**3 + 13*h2**2*k2 +
|
||||
9*h2*k2**2))
|
||||
return 16*pi/13125*k2*(k2 - h2)*res
|
||||
|
||||
def G35(h2, k2):
|
||||
res = (16*h2**4 + 6*k2**4 - 28*h2**3*k2 - 12*h2*k2**3 + 34*h2**2*k2**2
|
||||
+ sqrt(4*h2**2 + k2**2 - h2*k2)*(8*h2**3 + 6*k2**3 - 13*h2**2*k2 -
|
||||
9*h2*k2**2))
|
||||
return 16*pi/13125*k2*(k2 - h2)*res
|
||||
|
||||
def G37(h2, k2):
|
||||
return 4*pi*h2**2*k2**2*(k2 - h2)**2/105
|
||||
|
||||
known_funcs = {(0, 1): G01, (1, 1): G11, (1, 2): G12, (1, 3): G13,
|
||||
(2, 1): G21, (2, 2): G22, (2, 3): G23, (2, 4): G24,
|
||||
(2, 5): G25, (3, 1): G31, (3, 2): G32, (3, 3): G33,
|
||||
(3, 4): G34, (3, 5): G35, (3, 6): G36, (3, 7): G37}
|
||||
|
||||
def _ellip_norm(n, p, h2, k2):
|
||||
func = known_funcs[n, p]
|
||||
return func(h2, k2)
|
||||
_ellip_norm = np.vectorize(_ellip_norm)
|
||||
|
||||
def ellip_normal_known(h2, k2, n, p):
|
||||
return _ellip_norm(n, p, h2, k2)
|
||||
|
||||
# generate both large and small h2 < k2 pairs
|
||||
np.random.seed(1234)
|
||||
h2 = np.random.pareto(0.5, size=1)
|
||||
k2 = h2 * (1 + np.random.pareto(0.5, size=h2.size))
|
||||
|
||||
points = []
|
||||
for n in range(4):
|
||||
for p in range(1, 2*n+2):
|
||||
points.append((h2, k2, np.full(h2.size, n), np.full(h2.size, p)))
|
||||
points = np.array(points)
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(IntegrationWarning, "The occurrence of roundoff error")
|
||||
assert_func_equal(ellip_normal, ellip_normal_known, points, rtol=1e-12)
|
||||
|
||||
|
||||
def test_ellip_harm_2():
|
||||
|
||||
def I1(h2, k2, s):
|
||||
res = (ellip_harm_2(h2, k2, 1, 1, s)/(3 * ellip_harm(h2, k2, 1, 1, s))
|
||||
+ ellip_harm_2(h2, k2, 1, 2, s)/(3 * ellip_harm(h2, k2, 1, 2, s)) +
|
||||
ellip_harm_2(h2, k2, 1, 3, s)/(3 * ellip_harm(h2, k2, 1, 3, s)))
|
||||
return res
|
||||
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(IntegrationWarning, "The occurrence of roundoff error")
|
||||
assert_almost_equal(I1(5, 8, 10), 1/(10*sqrt((100-5)*(100-8))))
|
||||
|
||||
# Values produced by code from arXiv:1204.0267
|
||||
assert_almost_equal(ellip_harm_2(5, 8, 2, 1, 10), 0.00108056853382)
|
||||
assert_almost_equal(ellip_harm_2(5, 8, 2, 2, 10), 0.00105820513809)
|
||||
assert_almost_equal(ellip_harm_2(5, 8, 2, 3, 10), 0.00106058384743)
|
||||
assert_almost_equal(ellip_harm_2(5, 8, 2, 4, 10), 0.00106774492306)
|
||||
assert_almost_equal(ellip_harm_2(5, 8, 2, 5, 10), 0.00107976356454)
|
||||
|
||||
|
||||
def test_ellip_harm():
|
||||
|
||||
def E01(h2, k2, s):
|
||||
return 1
|
||||
|
||||
def E11(h2, k2, s):
|
||||
return s
|
||||
|
||||
def E12(h2, k2, s):
|
||||
return sqrt(abs(s*s - h2))
|
||||
|
||||
def E13(h2, k2, s):
|
||||
return sqrt(abs(s*s - k2))
|
||||
|
||||
def E21(h2, k2, s):
|
||||
return s*s - 1/3*((h2 + k2) + sqrt(abs((h2 + k2)*(h2 + k2)-3*h2*k2)))
|
||||
|
||||
def E22(h2, k2, s):
|
||||
return s*s - 1/3*((h2 + k2) - sqrt(abs((h2 + k2)*(h2 + k2)-3*h2*k2)))
|
||||
|
||||
def E23(h2, k2, s):
|
||||
return s * sqrt(abs(s*s - h2))
|
||||
|
||||
def E24(h2, k2, s):
|
||||
return s * sqrt(abs(s*s - k2))
|
||||
|
||||
def E25(h2, k2, s):
|
||||
return sqrt(abs((s*s - h2)*(s*s - k2)))
|
||||
|
||||
def E31(h2, k2, s):
|
||||
return s*s*s - (s/5)*(2*(h2 + k2) + sqrt(4*(h2 + k2)*(h2 + k2) -
|
||||
15*h2*k2))
|
||||
|
||||
def E32(h2, k2, s):
|
||||
return s*s*s - (s/5)*(2*(h2 + k2) - sqrt(4*(h2 + k2)*(h2 + k2) -
|
||||
15*h2*k2))
|
||||
|
||||
def E33(h2, k2, s):
|
||||
return sqrt(abs(s*s - h2))*(s*s - 1/5*((h2 + 2*k2) + sqrt(abs((h2 +
|
||||
2*k2)*(h2 + 2*k2) - 5*h2*k2))))
|
||||
|
||||
def E34(h2, k2, s):
|
||||
return sqrt(abs(s*s - h2))*(s*s - 1/5*((h2 + 2*k2) - sqrt(abs((h2 +
|
||||
2*k2)*(h2 + 2*k2) - 5*h2*k2))))
|
||||
|
||||
def E35(h2, k2, s):
|
||||
return sqrt(abs(s*s - k2))*(s*s - 1/5*((2*h2 + k2) + sqrt(abs((2*h2
|
||||
+ k2)*(2*h2 + k2) - 5*h2*k2))))
|
||||
|
||||
def E36(h2, k2, s):
|
||||
return sqrt(abs(s*s - k2))*(s*s - 1/5*((2*h2 + k2) - sqrt(abs((2*h2
|
||||
+ k2)*(2*h2 + k2) - 5*h2*k2))))
|
||||
|
||||
def E37(h2, k2, s):
|
||||
return s * sqrt(abs((s*s - h2)*(s*s - k2)))
|
||||
|
||||
assert_equal(ellip_harm(5, 8, 1, 2, 2.5, 1, 1),
|
||||
ellip_harm(5, 8, 1, 2, 2.5))
|
||||
|
||||
known_funcs = {(0, 1): E01, (1, 1): E11, (1, 2): E12, (1, 3): E13,
|
||||
(2, 1): E21, (2, 2): E22, (2, 3): E23, (2, 4): E24,
|
||||
(2, 5): E25, (3, 1): E31, (3, 2): E32, (3, 3): E33,
|
||||
(3, 4): E34, (3, 5): E35, (3, 6): E36, (3, 7): E37}
|
||||
|
||||
point_ref = []
|
||||
|
||||
def ellip_harm_known(h2, k2, n, p, s):
|
||||
for i in range(h2.size):
|
||||
func = known_funcs[(int(n[i]), int(p[i]))]
|
||||
point_ref.append(func(h2[i], k2[i], s[i]))
|
||||
return point_ref
|
||||
|
||||
rng = np.random.RandomState(1234)
|
||||
h2 = rng.pareto(0.5, size=30)
|
||||
k2 = h2*(1 + rng.pareto(0.5, size=h2.size))
|
||||
s = rng.pareto(0.5, size=h2.size)
|
||||
points = []
|
||||
for i in range(h2.size):
|
||||
for n in range(4):
|
||||
for p in range(1, 2*n+2):
|
||||
points.append((h2[i], k2[i], n, p, s[i]))
|
||||
points = np.array(points)
|
||||
assert_func_equal(ellip_harm, ellip_harm_known, points, rtol=1e-12)
|
||||
|
||||
|
||||
def test_ellip_harm_invalid_p():
|
||||
# Regression test. This should return nan.
|
||||
n = 4
|
||||
# Make p > 2*n + 1.
|
||||
p = 2*n + 2
|
||||
result = ellip_harm(0.5, 2.0, n, p, 0.2)
|
||||
assert np.isnan(result)
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
import pytest
|
||||
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
class TestInverseErrorFunction:
|
||||
def test_compliment(self):
|
||||
# Test erfcinv(1 - x) == erfinv(x)
|
||||
x = np.linspace(-1, 1, 101)
|
||||
assert_allclose(sc.erfcinv(1 - x), sc.erfinv(x), rtol=0, atol=1e-15)
|
||||
|
||||
def test_literal_values(self):
|
||||
# The expected values were calculated with mpmath:
|
||||
#
|
||||
# import mpmath
|
||||
# mpmath.mp.dps = 200
|
||||
# for y in [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]:
|
||||
# x = mpmath.erfinv(y)
|
||||
# print(x)
|
||||
#
|
||||
y = np.array([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
|
||||
actual = sc.erfinv(y)
|
||||
expected = [
|
||||
0.0,
|
||||
0.08885599049425769,
|
||||
0.1791434546212917,
|
||||
0.2724627147267543,
|
||||
0.37080715859355795,
|
||||
0.4769362762044699,
|
||||
0.5951160814499948,
|
||||
0.7328690779592167,
|
||||
0.9061938024368233,
|
||||
1.1630871536766743,
|
||||
]
|
||||
assert_allclose(actual, expected, rtol=0, atol=1e-15)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'f, x, y',
|
||||
[
|
||||
(sc.erfinv, -1, -np.inf),
|
||||
(sc.erfinv, 0, 0),
|
||||
(sc.erfinv, 1, np.inf),
|
||||
(sc.erfinv, -100, np.nan),
|
||||
(sc.erfinv, 100, np.nan),
|
||||
(sc.erfcinv, 0, np.inf),
|
||||
(sc.erfcinv, 1, -0.0),
|
||||
(sc.erfcinv, 2, -np.inf),
|
||||
(sc.erfcinv, -100, np.nan),
|
||||
(sc.erfcinv, 100, np.nan),
|
||||
],
|
||||
ids=[
|
||||
'erfinv at lower bound',
|
||||
'erfinv at midpoint',
|
||||
'erfinv at upper bound',
|
||||
'erfinv below lower bound',
|
||||
'erfinv above upper bound',
|
||||
'erfcinv at lower bound',
|
||||
'erfcinv at midpoint',
|
||||
'erfcinv at upper bound',
|
||||
'erfcinv below lower bound',
|
||||
'erfcinv above upper bound',
|
||||
]
|
||||
)
|
||||
def test_domain_bounds(self, f, x, y):
|
||||
assert_equal(f(x), y)
|
||||
|
||||
def test_erfinv_asympt(self):
|
||||
# regression test for gh-12758: erfinv(x) loses precision at small x
|
||||
# expected values precomputed with mpmath:
|
||||
# >>> mpmath.mp.dps = 100
|
||||
# >>> expected = [float(mpmath.erfinv(t)) for t in x]
|
||||
x = np.array([1e-20, 1e-15, 1e-14, 1e-10, 1e-8, 0.9e-7, 1.1e-7, 1e-6])
|
||||
expected = np.array([8.86226925452758e-21,
|
||||
8.862269254527581e-16,
|
||||
8.86226925452758e-15,
|
||||
8.862269254527581e-11,
|
||||
8.86226925452758e-09,
|
||||
7.97604232907484e-08,
|
||||
9.74849617998037e-08,
|
||||
8.8622692545299e-07])
|
||||
assert_allclose(sc.erfinv(x), expected,
|
||||
rtol=1e-15)
|
||||
|
||||
# also test the roundtrip consistency
|
||||
assert_allclose(sc.erf(sc.erfinv(x)),
|
||||
x,
|
||||
rtol=5e-15)
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
import pytest
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
class TestExp1:
|
||||
|
||||
def test_branch_cut(self):
|
||||
assert np.isnan(sc.exp1(-1))
|
||||
assert sc.exp1(complex(-1, 0)).imag == (
|
||||
-sc.exp1(complex(-1, -0.0)).imag
|
||||
)
|
||||
|
||||
assert_allclose(
|
||||
sc.exp1(complex(-1, 0)),
|
||||
sc.exp1(-1 + 1e-20j),
|
||||
atol=0,
|
||||
rtol=1e-15
|
||||
)
|
||||
assert_allclose(
|
||||
sc.exp1(complex(-1, -0.0)),
|
||||
sc.exp1(-1 - 1e-20j),
|
||||
atol=0,
|
||||
rtol=1e-15
|
||||
)
|
||||
|
||||
def test_834(self):
|
||||
# Regression test for #834
|
||||
a = sc.exp1(-complex(19.9999990))
|
||||
b = sc.exp1(-complex(19.9999991))
|
||||
assert_allclose(a.imag, b.imag, atol=0, rtol=1e-15)
|
||||
|
||||
|
||||
class TestScaledExp1:
|
||||
|
||||
@pytest.mark.parametrize('x, expected', [(0, 0), (np.inf, 1)])
|
||||
def test_limits(self, x, expected):
|
||||
y = sc._ufuncs._scaled_exp1(x)
|
||||
assert y == expected
|
||||
|
||||
# The expected values were computed with mpmath, e.g.:
|
||||
#
|
||||
# from mpmath import mp
|
||||
# mp.dps = 80
|
||||
# x = 1e-25
|
||||
# print(float(x*mp.exp(x)*np.expint(1, x)))
|
||||
#
|
||||
# prints 5.698741165994961e-24
|
||||
#
|
||||
# The method used to compute _scaled_exp1 changes at x=1
|
||||
# and x=1250, so values at those inputs, and values just
|
||||
# above and below them, are included in the test data.
|
||||
@pytest.mark.parametrize('x, expected',
|
||||
[(1e-25, 5.698741165994961e-24),
|
||||
(0.1, 0.20146425447084518),
|
||||
(0.9995, 0.5962509885831002),
|
||||
(1.0, 0.5963473623231941),
|
||||
(1.0005, 0.5964436833238044),
|
||||
(2.5, 0.7588145912149602),
|
||||
(10.0, 0.9156333393978808),
|
||||
(100.0, 0.9901942286733019),
|
||||
(500.0, 0.9980079523802055),
|
||||
(1000.0, 0.9990019940238807),
|
||||
(1249.5, 0.9992009578306811),
|
||||
(1250.0, 0.9992012769377913),
|
||||
(1250.25, 0.9992014363957858),
|
||||
(2000.0, 0.9995004992514963),
|
||||
(1e4, 0.9999000199940024),
|
||||
(1e10, 0.9999999999),
|
||||
(1e15, 0.999999999999999),
|
||||
])
|
||||
def test_scaled_exp1(self, x, expected):
|
||||
y = sc._ufuncs._scaled_exp1(x)
|
||||
assert_allclose(y, expected, rtol=2e-15)
|
||||
|
||||
|
||||
class TestExpi:
|
||||
|
||||
@pytest.mark.parametrize('result', [
|
||||
sc.expi(complex(-1, 0)),
|
||||
sc.expi(complex(-1, -0.0)),
|
||||
sc.expi(-1)
|
||||
])
|
||||
def test_branch_cut(self, result):
|
||||
desired = -0.21938393439552027368 # Computed using Mpmath
|
||||
assert_allclose(result, desired, atol=0, rtol=1e-14)
|
||||
|
||||
def test_near_branch_cut(self):
|
||||
lim_from_above = sc.expi(-1 + 1e-20j)
|
||||
lim_from_below = sc.expi(-1 - 1e-20j)
|
||||
assert_allclose(
|
||||
lim_from_above.real,
|
||||
lim_from_below.real,
|
||||
atol=0,
|
||||
rtol=1e-15
|
||||
)
|
||||
assert_allclose(
|
||||
lim_from_above.imag,
|
||||
-lim_from_below.imag,
|
||||
atol=0,
|
||||
rtol=1e-15
|
||||
)
|
||||
|
||||
def test_continuity_on_positive_real_axis(self):
|
||||
assert_allclose(
|
||||
sc.expi(complex(1, 0)),
|
||||
sc.expi(complex(1, -0.0)),
|
||||
atol=0,
|
||||
rtol=1e-15
|
||||
)
|
||||
|
||||
|
||||
class TestExpn:
|
||||
|
||||
def test_out_of_domain(self):
|
||||
assert all(np.isnan([sc.expn(-1, 1.0), sc.expn(1, -1.0)]))
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import os
|
||||
import platform
|
||||
import sysconfig
|
||||
|
||||
import pytest
|
||||
|
||||
from scipy._lib._testutils import IS_EDITABLE,_test_cython_extension, cython
|
||||
from scipy.special import beta, gamma
|
||||
|
||||
|
||||
@pytest.mark.fail_slow(40)
|
||||
# essential per https://github.com/scipy/scipy/pull/20487#discussion_r1567057247
|
||||
@pytest.mark.skipif(IS_EDITABLE,
|
||||
reason='Editable install cannot find .pxd headers.')
|
||||
@pytest.mark.skipif((platform.system() == 'Windows' and
|
||||
sysconfig.get_config_var('Py_GIL_DISABLED')),
|
||||
reason='gh-22039')
|
||||
@pytest.mark.skipif(platform.machine() in ["wasm32", "wasm64"],
|
||||
reason="Can't start subprocess")
|
||||
@pytest.mark.skipif(cython is None, reason="requires cython")
|
||||
def test_cython(tmp_path):
|
||||
srcdir = os.path.dirname(os.path.dirname(__file__))
|
||||
extensions, extensions_cpp = _test_cython_extension(tmp_path, srcdir)
|
||||
# actually test the cython c-extensions
|
||||
assert extensions.cy_beta(0.5, 0.1) == beta(0.5, 0.1)
|
||||
assert extensions.cy_gamma(0.5 + 1.0j) == gamma(0.5 + 1.0j)
|
||||
assert extensions_cpp.cy_beta(0.5, 0.1) == beta(0.5, 0.1)
|
||||
assert extensions_cpp.cy_gamma(0.5 + 1.0j) == gamma(0.5 + 1.0j)
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
import pytest
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import scipy.special as sc
|
||||
from scipy.special._testutils import FuncData
|
||||
|
||||
|
||||
class TestVoigtProfile:
|
||||
|
||||
@pytest.mark.parametrize('x, sigma, gamma', [
|
||||
(np.nan, 1, 1),
|
||||
(0, np.nan, 1),
|
||||
(0, 1, np.nan),
|
||||
(1, np.nan, 0),
|
||||
(np.nan, 1, 0),
|
||||
(1, 0, np.nan),
|
||||
(np.nan, 0, 1),
|
||||
(np.nan, 0, 0)
|
||||
])
|
||||
def test_nan(self, x, sigma, gamma):
|
||||
assert np.isnan(sc.voigt_profile(x, sigma, gamma))
|
||||
|
||||
@pytest.mark.parametrize('x, desired', [
|
||||
(-np.inf, 0),
|
||||
(np.inf, 0)
|
||||
])
|
||||
def test_inf(self, x, desired):
|
||||
assert sc.voigt_profile(x, 1, 1) == desired
|
||||
|
||||
def test_against_mathematica(self):
|
||||
# Results obtained from Mathematica by computing
|
||||
#
|
||||
# PDF[VoigtDistribution[gamma, sigma], x]
|
||||
#
|
||||
points = np.array([
|
||||
[-7.89, 45.06, 6.66, 0.0077921073660388806401],
|
||||
[-0.05, 7.98, 24.13, 0.012068223646769913478],
|
||||
[-13.98, 16.83, 42.37, 0.0062442236362132357833],
|
||||
[-12.66, 0.21, 6.32, 0.010052516161087379402],
|
||||
[11.34, 4.25, 21.96, 0.0113698923627278917805],
|
||||
[-11.56, 20.40, 30.53, 0.0076332760432097464987],
|
||||
[-9.17, 25.61, 8.32, 0.011646345779083005429],
|
||||
[16.59, 18.05, 2.50, 0.013637768837526809181],
|
||||
[9.11, 2.12, 39.33, 0.0076644040807277677585],
|
||||
[-43.33, 0.30, 45.68, 0.0036680463875330150996]
|
||||
])
|
||||
FuncData(
|
||||
sc.voigt_profile,
|
||||
points,
|
||||
(0, 1, 2),
|
||||
3,
|
||||
atol=0,
|
||||
rtol=1e-15
|
||||
).check()
|
||||
|
||||
def test_symmetry(self):
|
||||
x = np.linspace(0, 10, 20)
|
||||
assert_allclose(
|
||||
sc.voigt_profile(x, 1, 1),
|
||||
sc.voigt_profile(-x, 1, 1),
|
||||
rtol=1e-15,
|
||||
atol=0
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize('x, sigma, gamma, desired', [
|
||||
(0, 0, 0, np.inf),
|
||||
(1, 0, 0, 0)
|
||||
])
|
||||
def test_corner_cases(self, x, sigma, gamma, desired):
|
||||
assert sc.voigt_profile(x, sigma, gamma) == desired
|
||||
|
||||
@pytest.mark.parametrize('sigma1, gamma1, sigma2, gamma2', [
|
||||
(0, 1, 1e-16, 1),
|
||||
(1, 0, 1, 1e-16),
|
||||
(0, 0, 1e-16, 1e-16)
|
||||
])
|
||||
def test_continuity(self, sigma1, gamma1, sigma2, gamma2):
|
||||
x = np.linspace(1, 10, 20)
|
||||
assert_allclose(
|
||||
sc.voigt_profile(x, sigma1, gamma1),
|
||||
sc.voigt_profile(x, sigma2, gamma2),
|
||||
rtol=1e-16,
|
||||
atol=1e-16
|
||||
)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import numpy as np
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
class TestRgamma:
|
||||
|
||||
def test_gh_11315(self):
|
||||
assert sc.rgamma(-35) == 0
|
||||
|
||||
def test_rgamma_zeros(self):
|
||||
x = np.array([0, -10, -100, -1000, -10000])
|
||||
assert np.all(sc.rgamma(x) == 0)
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import pytest
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_array_equal
|
||||
|
||||
import scipy.special as sc
|
||||
from scipy.special._testutils import FuncData
|
||||
|
||||
|
||||
INVALID_POINTS = [
|
||||
(1, -1),
|
||||
(0, 0),
|
||||
(-1, 1),
|
||||
(np.nan, 1),
|
||||
(1, np.nan)
|
||||
]
|
||||
|
||||
|
||||
class TestGammainc:
|
||||
|
||||
@pytest.mark.parametrize('a, x', INVALID_POINTS)
|
||||
def test_domain(self, a, x):
|
||||
assert np.isnan(sc.gammainc(a, x))
|
||||
|
||||
def test_a_eq_0_x_gt_0(self):
|
||||
assert sc.gammainc(0, 1) == 1
|
||||
|
||||
@pytest.mark.parametrize('a, x, desired', [
|
||||
(np.inf, 1, 0),
|
||||
(np.inf, 0, 0),
|
||||
(np.inf, np.inf, np.nan),
|
||||
(1, np.inf, 1)
|
||||
])
|
||||
def test_infinite_arguments(self, a, x, desired):
|
||||
result = sc.gammainc(a, x)
|
||||
if np.isnan(desired):
|
||||
assert np.isnan(result)
|
||||
else:
|
||||
assert result == desired
|
||||
|
||||
@pytest.mark.parametrize("x", [-np.inf, -1.0, -0.0, 0.0, np.inf, np.nan])
|
||||
def test_a_nan(self, x):
|
||||
assert np.isnan(sc.gammainc(np.nan, x))
|
||||
|
||||
@pytest.mark.parametrize("a", [-np.inf, -1.0, -0.0, 0.0, np.inf, np.nan])
|
||||
def test_x_nan(self, a):
|
||||
assert np.isnan(sc.gammainc(a, np.nan))
|
||||
|
||||
def test_infinite_limits(self):
|
||||
# Test that large arguments converge to the hard-coded limits
|
||||
# at infinity.
|
||||
assert_allclose(
|
||||
sc.gammainc(1000, 100),
|
||||
sc.gammainc(np.inf, 100),
|
||||
atol=1e-200, # Use `atol` since the function converges to 0.
|
||||
rtol=0
|
||||
)
|
||||
assert sc.gammainc(100, 1000) == sc.gammainc(100, np.inf)
|
||||
|
||||
def test_x_zero(self):
|
||||
a = np.arange(1, 10)
|
||||
assert_array_equal(sc.gammainc(a, 0), 0)
|
||||
|
||||
def test_limit_check(self):
|
||||
result = sc.gammainc(1e-10, 1)
|
||||
limit = sc.gammainc(0, 1)
|
||||
assert np.isclose(result, limit)
|
||||
|
||||
def gammainc_line(self, x):
|
||||
# The line a = x where a simpler asymptotic expansion (analog
|
||||
# of DLMF 8.12.15) is available.
|
||||
c = np.array([-1/3, -1/540, 25/6048, 101/155520,
|
||||
-3184811/3695155200, -2745493/8151736420])
|
||||
res = 0
|
||||
xfac = 1
|
||||
for ck in c:
|
||||
res -= ck*xfac
|
||||
xfac /= x
|
||||
res /= np.sqrt(2*np.pi*x)
|
||||
res += 0.5
|
||||
return res
|
||||
|
||||
def test_line(self):
|
||||
x = np.logspace(np.log10(25), 300, 500)
|
||||
a = x
|
||||
dataset = np.vstack((a, x, self.gammainc_line(x))).T
|
||||
FuncData(sc.gammainc, dataset, (0, 1), 2, rtol=1e-11).check()
|
||||
|
||||
def test_roundtrip(self):
|
||||
a = np.logspace(-5, 10, 100)
|
||||
x = np.logspace(-5, 10, 100)
|
||||
|
||||
y = sc.gammaincinv(a, sc.gammainc(a, x))
|
||||
assert_allclose(x, y, rtol=1e-10)
|
||||
|
||||
|
||||
class TestGammaincc:
|
||||
|
||||
@pytest.mark.parametrize('a, x', INVALID_POINTS)
|
||||
def test_domain(self, a, x):
|
||||
assert np.isnan(sc.gammaincc(a, x))
|
||||
|
||||
def test_a_eq_0_x_gt_0(self):
|
||||
assert sc.gammaincc(0, 1) == 0
|
||||
|
||||
@pytest.mark.parametrize('a, x, desired', [
|
||||
(np.inf, 1, 1),
|
||||
(np.inf, 0, 1),
|
||||
(np.inf, np.inf, np.nan),
|
||||
(1, np.inf, 0)
|
||||
])
|
||||
def test_infinite_arguments(self, a, x, desired):
|
||||
result = sc.gammaincc(a, x)
|
||||
if np.isnan(desired):
|
||||
assert np.isnan(result)
|
||||
else:
|
||||
assert result == desired
|
||||
|
||||
@pytest.mark.parametrize("x", [-np.inf, -1.0, -0.0, 0.0, np.inf, np.nan])
|
||||
def test_a_nan(self, x):
|
||||
assert np.isnan(sc.gammaincc(np.nan, x))
|
||||
|
||||
@pytest.mark.parametrize("a", [-np.inf, -1.0, -0.0, 0.0, np.inf, np.nan])
|
||||
def test_x_nan(self, a):
|
||||
assert np.isnan(sc.gammaincc(a, np.nan))
|
||||
|
||||
def test_infinite_limits(self):
|
||||
# Test that large arguments converge to the hard-coded limits
|
||||
# at infinity.
|
||||
assert sc.gammaincc(1000, 100) == sc.gammaincc(np.inf, 100)
|
||||
assert_allclose(
|
||||
sc.gammaincc(100, 1000),
|
||||
sc.gammaincc(100, np.inf),
|
||||
atol=1e-200, # Use `atol` since the function converges to 0.
|
||||
rtol=0
|
||||
)
|
||||
|
||||
def test_limit_check(self):
|
||||
result = sc.gammaincc(1e-10,1)
|
||||
limit = sc.gammaincc(0,1)
|
||||
assert np.isclose(result, limit)
|
||||
|
||||
def test_x_zero(self):
|
||||
a = np.arange(1, 10)
|
||||
assert_array_equal(sc.gammaincc(a, 0), 1)
|
||||
|
||||
def test_roundtrip(self):
|
||||
a = np.logspace(-5, 10, 100)
|
||||
x = np.logspace(-5, 10, 100)
|
||||
|
||||
y = sc.gammainccinv(a, sc.gammaincc(a, x))
|
||||
assert_allclose(x, y, rtol=1e-14)
|
||||
2566
venv/lib/python3.13/site-packages/scipy/special/tests/test_hyp2f1.py
Normal file
2566
venv/lib/python3.13/site-packages/scipy/special/tests/test_hyp2f1.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,234 @@
|
|||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
class TestHyperu:
|
||||
|
||||
def test_negative_x(self):
|
||||
a, b, x = np.meshgrid(
|
||||
[-1, -0.5, 0, 0.5, 1],
|
||||
[-1, -0.5, 0, 0.5, 1],
|
||||
np.linspace(-100, -1, 10),
|
||||
)
|
||||
assert np.all(np.isnan(sc.hyperu(a, b, x)))
|
||||
|
||||
def test_special_cases(self):
|
||||
assert sc.hyperu(0, 1, 1) == 1.0
|
||||
|
||||
@pytest.mark.parametrize('a', [0.5, 1, np.nan])
|
||||
@pytest.mark.parametrize('b', [1, 2, np.nan])
|
||||
@pytest.mark.parametrize('x', [0.25, 3, np.nan])
|
||||
def test_nan_inputs(self, a, b, x):
|
||||
assert np.isnan(sc.hyperu(a, b, x)) == np.any(np.isnan([a, b, x]))
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'a,b,x,expected',
|
||||
[(0.21581740448533887, 1.0, 1e-05, 3.6030558839391325),
|
||||
(0.21581740448533887, 1.0, 0.00021544346900318823, 2.8783254988948976),
|
||||
(0.21581740448533887, 1.0, 0.004641588833612777, 2.154928216691109),
|
||||
(0.21581740448533887, 1.0, 0.1, 1.446546638718792),
|
||||
(0.0030949064301273865, 1.0, 1e-05, 1.0356696454116199),
|
||||
(0.0030949064301273865, 1.0, 0.00021544346900318823, 1.0261510362481985),
|
||||
(0.0030949064301273865, 1.0, 0.004641588833612777, 1.0166326903402296),
|
||||
(0.0030949064301273865, 1.0, 0.1, 1.0071174207698674),
|
||||
(0.1509924314279033, 1.0, 1e-05, 2.806173846998948),
|
||||
(0.1509924314279033, 1.0, 0.00021544346900318823, 2.3092158526816124),
|
||||
(0.1509924314279033, 1.0, 0.004641588833612777, 1.812905980588048),
|
||||
(0.1509924314279033, 1.0, 0.1, 1.3239738117634872),
|
||||
(-0.010678995342969011, 1.0, 1e-05, 0.8775194903781114),
|
||||
(-0.010678995342969011, 1.0, 0.00021544346900318823, 0.9101008998540128),
|
||||
(-0.010678995342969011, 1.0, 0.004641588833612777, 0.9426854294058609),
|
||||
(-0.010678995342969011, 1.0, 0.1, 0.9753065150174902),
|
||||
(-0.06556622211831487, 1.0, 1e-05, 0.26435429752668904),
|
||||
(-0.06556622211831487, 1.0, 0.00021544346900318823, 0.4574756033875781),
|
||||
(-0.06556622211831487, 1.0, 0.004641588833612777, 0.6507121093358457),
|
||||
(-0.06556622211831487, 1.0, 0.1, 0.8453129788602187),
|
||||
(-0.21628242470175185, 1.0, 1e-05, -1.2318314201114489),
|
||||
(-0.21628242470175185, 1.0, 0.00021544346900318823, -0.6704694233529538),
|
||||
(-0.21628242470175185, 1.0, 0.004641588833612777, -0.10795098653682857),
|
||||
(-0.21628242470175185, 1.0, 0.1, 0.4687227684115524)]
|
||||
)
|
||||
def test_gh_15650_mp(self, a, b, x, expected):
|
||||
# See https://github.com/scipy/scipy/issues/15650
|
||||
# b == 1, |a| < 0.25, 0 < x < 1
|
||||
#
|
||||
# This purpose of this test is to check the accuracy of results
|
||||
# in the region that was impacted by gh-15650.
|
||||
#
|
||||
# Reference values computed with mpmath using the script:
|
||||
#
|
||||
# import itertools as it
|
||||
# import numpy as np
|
||||
#
|
||||
# from mpmath import mp
|
||||
#
|
||||
# rng = np.random.default_rng(1234)
|
||||
#
|
||||
# cases = []
|
||||
# for a, x in it.product(
|
||||
# np.random.uniform(-0.25, 0.25, size=6),
|
||||
# np.logspace(-5, -1, 4),
|
||||
# ):
|
||||
# with mp.workdps(100):
|
||||
# cases.append((float(a), 1.0, float(x), float(mp.hyperu(a, 1.0, x))))
|
||||
assert_allclose(sc.hyperu(a, b, x), expected, rtol=1e-13)
|
||||
|
||||
def test_gh_15650_sanity(self):
|
||||
# The purpose of this test is to sanity check hyperu in the region that
|
||||
# was impacted by gh-15650 by making sure there are no excessively large
|
||||
# results, as were reported there.
|
||||
a = np.linspace(-0.5, 0.5, 500)
|
||||
x = np.linspace(1e-6, 1e-1, 500)
|
||||
a, x = np.meshgrid(a, x)
|
||||
results = sc.hyperu(a, 1.0, x)
|
||||
assert np.all(np.abs(results) < 1e3)
|
||||
|
||||
|
||||
class TestHyp1f1:
|
||||
|
||||
@pytest.mark.parametrize('a, b, x', [
|
||||
(np.nan, 1, 1),
|
||||
(1, np.nan, 1),
|
||||
(1, 1, np.nan)
|
||||
])
|
||||
def test_nan_inputs(self, a, b, x):
|
||||
assert np.isnan(sc.hyp1f1(a, b, x))
|
||||
|
||||
def test_poles(self):
|
||||
assert_equal(sc.hyp1f1(1, [0, -1, -2, -3, -4], 0.5), np.inf)
|
||||
|
||||
@pytest.mark.parametrize('a, b, x, result', [
|
||||
(-1, 1, 0.5, 0.5),
|
||||
(1, 1, 0.5, 1.6487212707001281468),
|
||||
(2, 1, 0.5, 2.4730819060501922203),
|
||||
(1, 2, 0.5, 1.2974425414002562937),
|
||||
(-10, 1, 0.5, -0.38937441413785204475)
|
||||
])
|
||||
def test_special_cases(self, a, b, x, result):
|
||||
# Hit all the special case branches at the beginning of the
|
||||
# function. Desired answers computed using Mpmath.
|
||||
assert_allclose(sc.hyp1f1(a, b, x), result, atol=0, rtol=1e-15)
|
||||
|
||||
@pytest.mark.parametrize('a, b, x, result', [
|
||||
(1, 1, 0.44, 1.5527072185113360455),
|
||||
(-1, 1, 0.44, 0.55999999999999999778),
|
||||
(100, 100, 0.89, 2.4351296512898745592),
|
||||
(-100, 100, 0.89, 0.40739062490768104667),
|
||||
(1.5, 100, 59.99, 3.8073513625965598107),
|
||||
(-1.5, 100, 59.99, 0.25099240047125826943)
|
||||
])
|
||||
def test_geometric_convergence(self, a, b, x, result):
|
||||
# Test the region where we are relying on the ratio of
|
||||
#
|
||||
# (|a| + 1) * |x| / |b|
|
||||
#
|
||||
# being small. Desired answers computed using Mpmath
|
||||
assert_allclose(sc.hyp1f1(a, b, x), result, atol=0, rtol=1e-15)
|
||||
|
||||
@pytest.mark.parametrize('a, b, x, result', [
|
||||
(-1, 1, 1.5, -0.5),
|
||||
(-10, 1, 1.5, 0.41801777430943080357),
|
||||
(-25, 1, 1.5, 0.25114491646037839809),
|
||||
(-50, 1, 1.5, -0.25683643975194756115),
|
||||
(-80, 1, 1.5, -0.24554329325751503601),
|
||||
(-150, 1, 1.5, -0.173364795515420454496),
|
||||
])
|
||||
def test_a_negative_integer(self, a, b, x, result):
|
||||
# Desired answers computed using Mpmath.
|
||||
assert_allclose(sc.hyp1f1(a, b, x), result, atol=0, rtol=2e-14)
|
||||
|
||||
@pytest.mark.parametrize('a, b, x, expected', [
|
||||
(0.01, 150, -4, 0.99973683897677527773), # gh-3492
|
||||
(1, 5, 0.01, 1.0020033381011970966), # gh-3593
|
||||
(50, 100, 0.01, 1.0050126452421463411), # gh-3593
|
||||
(1, 0.3, -1e3, -7.011932249442947651455e-04), # gh-14149
|
||||
(1, 0.3, -1e4, -7.001190321418937164734e-05), # gh-14149
|
||||
(9, 8.5, -350, -5.224090831922378361082e-20), # gh-17120
|
||||
(9, 8.5, -355, -4.595407159813368193322e-20), # gh-17120
|
||||
(75, -123.5, 15, 3.425753920814889017493e+06),
|
||||
])
|
||||
def test_assorted_cases(self, a, b, x, expected):
|
||||
# Expected values were computed with mpmath.hyp1f1(a, b, x).
|
||||
assert_allclose(sc.hyp1f1(a, b, x), expected, atol=0, rtol=1e-14)
|
||||
|
||||
def test_a_neg_int_and_b_equal_x(self):
|
||||
# This is a case where the Boost wrapper will call hypergeometric_pFq
|
||||
# instead of hypergeometric_1F1. When we use a version of Boost in
|
||||
# which https://github.com/boostorg/math/issues/833 is fixed, this
|
||||
# test case can probably be moved into test_assorted_cases.
|
||||
# The expected value was computed with mpmath.hyp1f1(a, b, x).
|
||||
a = -10.0
|
||||
b = 2.5
|
||||
x = 2.5
|
||||
expected = 0.0365323664364104338721
|
||||
computed = sc.hyp1f1(a, b, x)
|
||||
assert_allclose(computed, expected, atol=0, rtol=1e-13)
|
||||
|
||||
@pytest.mark.parametrize('a, b, x, desired', [
|
||||
(-1, -2, 2, 2),
|
||||
(-1, -4, 10, 3.5),
|
||||
(-2, -2, 1, 2.5)
|
||||
])
|
||||
def test_gh_11099(self, a, b, x, desired):
|
||||
# All desired results computed using Mpmath
|
||||
assert sc.hyp1f1(a, b, x) == desired
|
||||
|
||||
@pytest.mark.parametrize('a', [-3, -2])
|
||||
def test_x_zero_a_and_b_neg_ints_and_a_ge_b(self, a):
|
||||
assert sc.hyp1f1(a, -3, 0) == 1
|
||||
|
||||
# In the following tests with complex z, the reference values
|
||||
# were computed with mpmath.hyp1f1(a, b, z), and verified with
|
||||
# Wolfram Alpha Hypergeometric1F1(a, b, z), except for the
|
||||
# case a=0.1, b=1, z=7-24j, where Wolfram Alpha reported
|
||||
# "Standard computation time exceeded". That reference value
|
||||
# was confirmed in an online Matlab session, with the commands
|
||||
#
|
||||
# > format long
|
||||
# > hypergeom(0.1, 1, 7-24i)
|
||||
# ans =
|
||||
# -3.712349651834209 + 4.554636556672912i
|
||||
#
|
||||
@pytest.mark.parametrize(
|
||||
'a, b, z, ref',
|
||||
[(-0.25, 0.5, 1+2j, 1.1814553180903435-1.2792130661292984j),
|
||||
(0.25, 0.5, 1+2j, 0.24636797405707597+1.293434354945675j),
|
||||
(25, 1.5, -2j, -516.1771262822523+407.04142751922024j),
|
||||
(12, -1.5, -10+20j, -5098507.422706547-1341962.8043508842j),
|
||||
pytest.param(
|
||||
10, 250, 10-15j, 1.1985998416598884-0.8613474402403436j,
|
||||
marks=pytest.mark.xfail,
|
||||
),
|
||||
pytest.param(
|
||||
0.1, 1, 7-24j, -3.712349651834209+4.554636556672913j,
|
||||
marks=pytest.mark.xfail,
|
||||
)
|
||||
],
|
||||
)
|
||||
def test_complex_z(self, a, b, z, ref):
|
||||
h = sc.hyp1f1(a, b, z)
|
||||
assert_allclose(h, ref, rtol=4e-15)
|
||||
|
||||
# The "legacy edge cases" mentioned in the comments in the following
|
||||
# tests refers to the behavior of hyp1f1(a, b, x) when b is a nonpositive
|
||||
# integer. In some subcases, the behavior of SciPy does not match that
|
||||
# of Boost (1.81+), mpmath and Mathematica (via Wolfram Alpha online).
|
||||
# If the handling of these edges cases is changed to agree with those
|
||||
# libraries, these test will have to be updated.
|
||||
|
||||
@pytest.mark.parametrize('b', [0, -1, -5])
|
||||
def test_legacy_case1(self, b):
|
||||
# Test results of hyp1f1(0, n, x) for n <= 0.
|
||||
# This is a legacy edge case.
|
||||
# Boost (versions greater than 1.80), Mathematica (via Wolfram Alpha
|
||||
# online) and mpmath all return 1 in this case, but SciPy's hyp1f1
|
||||
# returns inf.
|
||||
assert_equal(sc.hyp1f1(0, b, [-1.5, 0, 1.5]), [np.inf, np.inf, np.inf])
|
||||
|
||||
def test_legacy_case2(self):
|
||||
# This is a legacy edge case.
|
||||
# In software such as boost (1.81+), mpmath and Mathematica,
|
||||
# the value is 1.
|
||||
assert sc.hyp1f1(-4, -3, 0) == np.inf
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
# This file contains unit tests for iv_ratio() and related functions.
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_allclose
|
||||
from scipy.special._ufuncs import ( # type: ignore[attr-defined]
|
||||
_iv_ratio as iv_ratio,
|
||||
_iv_ratio_c as iv_ratio_c,
|
||||
)
|
||||
|
||||
|
||||
class TestIvRatio:
|
||||
|
||||
@pytest.mark.parametrize('v,x,r', [
|
||||
(0.5, 0.16666666666666666, 0.16514041292462933),
|
||||
(0.5, 0.3333333333333333, 0.32151273753163434),
|
||||
(0.5, 0.5, 0.46211715726000974),
|
||||
(0.5, 0.6666666666666666, 0.5827829453479101),
|
||||
(0.5, 0.8333333333333335, 0.6822617902381698),
|
||||
(1, 0.3380952380952381, 0.1666773049170313),
|
||||
(1, 0.7083333333333333, 0.33366443586989925),
|
||||
(1, 1.1666666666666667, 0.5023355231537423),
|
||||
(1, 1.8666666666666665, 0.674616572252164),
|
||||
(1, 3.560606060606061, 0.844207659503163),
|
||||
(2.34, 0.7975238095238094, 0.16704903081553285),
|
||||
(2.34, 1.7133333333333334, 0.3360215931268845),
|
||||
(2.34, 2.953333333333333, 0.50681909317803),
|
||||
(2.34, 5.0826666666666656, 0.6755252698800679),
|
||||
(2.34, 10.869696969696973, 0.8379351104498762),
|
||||
(56.789, 19.46575238095238, 0.1667020505391409),
|
||||
(56.789, 42.55008333333333, 0.33353809996933026),
|
||||
(56.789, 75.552, 0.5003932381177826),
|
||||
(56.789, 135.76026666666667, 0.6670528221946127),
|
||||
(56.789, 307.8642424242425, 0.8334999441460798),
|
||||
])
|
||||
def test_against_reference_values(self, v, x, r):
|
||||
"""The reference values are computed using mpmath as follows.
|
||||
|
||||
from mpmath import mp
|
||||
mp.dps = 100
|
||||
|
||||
def iv_ratio_mp(v, x):
|
||||
return mp.besseli(v, x) / mp.besseli(v - 1, x)
|
||||
|
||||
def _sample(n, *, v):
|
||||
'''Return n positive real numbers x such that iv_ratio(v, x) are
|
||||
roughly evenly spaced over (0, 1). The formula is taken from [1].
|
||||
|
||||
[1] Banerjee A., Dhillon, I. S., Ghosh, J., Sra, S. (2005).
|
||||
"Clustering on the Unit Hypersphere using von Mises-Fisher
|
||||
Distributions." Journal of Machine Learning Research,
|
||||
6(46):1345-1382.
|
||||
'''
|
||||
r = np.arange(1, n+1) / (n+1)
|
||||
return r * (2*v-r*r) / (1-r*r)
|
||||
|
||||
for v in (0.5, 1, 2.34, 56.789):
|
||||
xs = _sample(5, v=v)
|
||||
for x in xs:
|
||||
print(f"({v}, {x}, {float(iv_ratio_mp(v,x))}),")
|
||||
"""
|
||||
assert_allclose(iv_ratio(v, x), r, rtol=4e-16, atol=0)
|
||||
|
||||
@pytest.mark.parametrize('v,x,r', [
|
||||
(1, np.inf, 1),
|
||||
(np.inf, 1, 0),
|
||||
])
|
||||
def test_inf(self, v, x, r):
|
||||
"""If exactly one of v or x is inf and the other is within domain,
|
||||
should return 0 or 1 accordingly."""
|
||||
assert_equal(iv_ratio(v, x), r)
|
||||
|
||||
@pytest.mark.parametrize('v', [0.49, -np.inf, np.nan, np.inf])
|
||||
@pytest.mark.parametrize('x', [-np.finfo(float).smallest_normal,
|
||||
-np.finfo(float).smallest_subnormal,
|
||||
-np.inf, np.nan, np.inf])
|
||||
def test_nan(self, v, x):
|
||||
"""If at least one argument is out of domain, or if v = x = inf,
|
||||
the function should return nan."""
|
||||
assert_equal(iv_ratio(v, x), np.nan)
|
||||
|
||||
@pytest.mark.parametrize('v', [0.5, 1, np.finfo(float).max, np.inf])
|
||||
def test_zero_x(self, v):
|
||||
"""If x is +/-0.0, return x to ensure iv_ratio is an odd function."""
|
||||
assert_equal(iv_ratio(v, 0.0), 0.0)
|
||||
assert_equal(iv_ratio(v, -0.0), -0.0)
|
||||
|
||||
@pytest.mark.parametrize('v,x', [
|
||||
(1, np.finfo(float).smallest_normal),
|
||||
(1, np.finfo(float).smallest_subnormal),
|
||||
(1, np.finfo(float).smallest_subnormal*2),
|
||||
(1e20, 123),
|
||||
(np.finfo(float).max, 1),
|
||||
(np.finfo(float).max, np.sqrt(np.finfo(float).max)),
|
||||
])
|
||||
def test_tiny_x(self, v, x):
|
||||
"""If x is much less than v, the bounds
|
||||
|
||||
x x
|
||||
--------------------------- <= R <= -----------------------
|
||||
v-0.5+sqrt(x**2+(v+0.5)**2) v-1+sqrt(x**2+(v+1)**2)
|
||||
|
||||
collapses to R ~= x/2v. Test against this asymptotic expression.
|
||||
"""
|
||||
assert_equal(iv_ratio(v, x), (0.5*x)/v)
|
||||
|
||||
@pytest.mark.parametrize('v,x', [
|
||||
(1, 1e16),
|
||||
(1e20, 1e40),
|
||||
(np.sqrt(np.finfo(float).max), np.finfo(float).max),
|
||||
])
|
||||
def test_huge_x(self, v, x):
|
||||
"""If x is much greater than v, the bounds
|
||||
|
||||
x x
|
||||
--------------------------- <= R <= ---------------------------
|
||||
v-0.5+sqrt(x**2+(v+0.5)**2) v-0.5+sqrt(x**2+(v-0.5)**2)
|
||||
|
||||
collapses to R ~= 1. Test against this asymptotic expression.
|
||||
"""
|
||||
assert_equal(iv_ratio(v, x), 1.0)
|
||||
|
||||
@pytest.mark.parametrize('v,x', [
|
||||
(np.finfo(float).max, np.finfo(float).max),
|
||||
(np.finfo(float).max / 3, np.finfo(float).max),
|
||||
(np.finfo(float).max, np.finfo(float).max / 3),
|
||||
])
|
||||
def test_huge_v_x(self, v, x):
|
||||
"""If both x and v are very large, the bounds
|
||||
|
||||
x x
|
||||
--------------------------- <= R <= -----------------------
|
||||
v-0.5+sqrt(x**2+(v+0.5)**2) v-1+sqrt(x**2+(v+1)**2)
|
||||
|
||||
collapses to R ~= x/(v+sqrt(x**2+v**2). Test against this asymptotic
|
||||
expression, and in particular that no numerical overflow occurs during
|
||||
intermediate calculations.
|
||||
"""
|
||||
t = x / v
|
||||
expected = t / (1 + np.hypot(1, t))
|
||||
assert_allclose(iv_ratio(v, x), expected, rtol=4e-16, atol=0)
|
||||
|
||||
|
||||
class TestIvRatioC:
|
||||
|
||||
@pytest.mark.parametrize('v,x,r', [
|
||||
(0.5, 0.16666666666666666, 0.8348595870753707),
|
||||
(0.5, 0.3333333333333333, 0.6784872624683657),
|
||||
(0.5, 0.5, 0.5378828427399902),
|
||||
(0.5, 0.6666666666666666, 0.4172170546520899),
|
||||
(0.5, 0.8333333333333335, 0.3177382097618302),
|
||||
(1, 0.3380952380952381, 0.8333226950829686),
|
||||
(1, 0.7083333333333333, 0.6663355641301008),
|
||||
(1, 1.1666666666666667, 0.4976644768462577),
|
||||
(1, 1.8666666666666665, 0.325383427747836),
|
||||
(1, 3.560606060606061, 0.155792340496837),
|
||||
(2.34, 0.7975238095238094, 0.8329509691844672),
|
||||
(2.34, 1.7133333333333334, 0.6639784068731155),
|
||||
(2.34, 2.953333333333333, 0.49318090682197),
|
||||
(2.34, 5.0826666666666656, 0.3244747301199321),
|
||||
(2.34, 10.869696969696973, 0.16206488955012377),
|
||||
(56.789, 19.46575238095238, 0.8332979494608591),
|
||||
(56.789, 42.55008333333333, 0.6664619000306697),
|
||||
(56.789, 75.552, 0.4996067618822174),
|
||||
(56.789, 135.76026666666667, 0.3329471778053873),
|
||||
(56.789, 307.8642424242425, 0.16650005585392025),
|
||||
])
|
||||
def test_against_reference_values(self, v, x, r):
|
||||
"""The reference values are one minus those of TestIvRatio."""
|
||||
assert_allclose(iv_ratio_c(v, x), r, rtol=1e-15, atol=0)
|
||||
|
||||
@pytest.mark.parametrize('v,x,r', [
|
||||
(1, np.inf, 0),
|
||||
(np.inf, 1, 1),
|
||||
])
|
||||
def test_inf(self, v, x, r):
|
||||
"""If exactly one of v or x is inf and the other is within domain,
|
||||
should return 0 or 1 accordingly."""
|
||||
assert_equal(iv_ratio_c(v, x), r)
|
||||
|
||||
@pytest.mark.parametrize('v', [0.49, -np.inf, np.nan, np.inf])
|
||||
@pytest.mark.parametrize('x', [-np.finfo(float).smallest_normal,
|
||||
-np.finfo(float).smallest_subnormal,
|
||||
-np.inf, np.nan, np.inf])
|
||||
def test_nan(self, v, x):
|
||||
"""If at least one argument is out of domain, or if v = x = inf,
|
||||
the function should return nan."""
|
||||
assert_equal(iv_ratio_c(v, x), np.nan)
|
||||
|
||||
@pytest.mark.parametrize('v', [0.5, 1, np.finfo(float).max, np.inf])
|
||||
def test_zero_x(self, v):
|
||||
"""If x is +/-0.0, return 1."""
|
||||
assert_equal(iv_ratio_c(v, 0.0), 1.0)
|
||||
assert_equal(iv_ratio_c(v, -0.0), 1.0)
|
||||
|
||||
@pytest.mark.parametrize('v,x', [
|
||||
(1, np.finfo(float).smallest_normal),
|
||||
(1, np.finfo(float).smallest_subnormal),
|
||||
(1, np.finfo(float).smallest_subnormal*2),
|
||||
(1e20, 123),
|
||||
(np.finfo(float).max, 1),
|
||||
(np.finfo(float).max, np.sqrt(np.finfo(float).max)),
|
||||
])
|
||||
def test_tiny_x(self, v, x):
|
||||
"""If x is much less than v, the bounds
|
||||
|
||||
x x
|
||||
--------------------------- <= R <= -----------------------
|
||||
v-0.5+sqrt(x**2+(v+0.5)**2) v-1+sqrt(x**2+(v+1)**2)
|
||||
|
||||
collapses to 1-R ~= 1-x/2v. Test against this asymptotic expression.
|
||||
"""
|
||||
assert_equal(iv_ratio_c(v, x), 1.0-(0.5*x)/v)
|
||||
|
||||
@pytest.mark.parametrize('v,x', [
|
||||
(1, 1e16),
|
||||
(1e20, 1e40),
|
||||
(np.sqrt(np.finfo(float).max), np.finfo(float).max),
|
||||
])
|
||||
def test_huge_x(self, v, x):
|
||||
"""If x is much greater than v, the bounds
|
||||
|
||||
x x
|
||||
--------------------------- <= R <= ---------------------------
|
||||
v-0.5+sqrt(x**2+(v+0.5)**2) v-0.5+sqrt(x**2+(v-0.5)**2)
|
||||
|
||||
collapses to 1-R ~= (v-0.5)/x. Test against this asymptotic expression.
|
||||
"""
|
||||
assert_allclose(iv_ratio_c(v, x), (v-0.5)/x, rtol=1e-15, atol=0)
|
||||
|
||||
@pytest.mark.parametrize('v,x', [
|
||||
(np.finfo(float).max, np.finfo(float).max),
|
||||
(np.finfo(float).max / 3, np.finfo(float).max),
|
||||
(np.finfo(float).max, np.finfo(float).max / 3),
|
||||
])
|
||||
def test_huge_v_x(self, v, x):
|
||||
"""If both x and v are very large, the bounds
|
||||
|
||||
x x
|
||||
--------------------------- <= R <= -----------------------
|
||||
v-0.5+sqrt(x**2+(v+0.5)**2) v-1+sqrt(x**2+(v+1)**2)
|
||||
|
||||
collapses to 1 - R ~= 1 - x/(v+sqrt(x**2+v**2). Test against this
|
||||
asymptotic expression, and in particular that no numerical overflow
|
||||
occurs during intermediate calculations.
|
||||
"""
|
||||
t = x / v
|
||||
expected = 1 - t / (1 + np.hypot(1, t))
|
||||
assert_allclose(iv_ratio_c(v, x), expected, rtol=4e-16, atol=0)
|
||||
|
|
@ -0,0 +1,491 @@
|
|||
import itertools
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_
|
||||
from scipy.special._testutils import FuncData
|
||||
|
||||
from scipy.special import kolmogorov, kolmogi, smirnov, smirnovi
|
||||
from scipy.special._ufuncs import (_kolmogc, _kolmogci, _kolmogp,
|
||||
_smirnovc, _smirnovci, _smirnovp)
|
||||
|
||||
_rtol = 1e-10
|
||||
|
||||
class TestSmirnov:
|
||||
def test_nan(self):
|
||||
assert_(np.isnan(smirnov(1, np.nan)))
|
||||
|
||||
def test_basic(self):
|
||||
dataset = [(1, 0.1, 0.9),
|
||||
(1, 0.875, 0.125),
|
||||
(2, 0.875, 0.125 * 0.125),
|
||||
(3, 0.875, 0.125 * 0.125 * 0.125)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnov, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, -1] = 1 - dataset[:, -1]
|
||||
FuncData(
|
||||
_smirnovc, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_x_equals_0(self):
|
||||
dataset = [(n, 0, 1) for n in itertools.chain(range(2, 20), range(1010, 1020))]
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnov, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, -1] = 1 - dataset[:, -1]
|
||||
FuncData(
|
||||
_smirnovc, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_x_equals_1(self):
|
||||
dataset = [(n, 1, 0) for n in itertools.chain(range(2, 20), range(1010, 1020))]
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnov, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, -1] = 1 - dataset[:, -1]
|
||||
FuncData(
|
||||
_smirnovc, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_x_equals_0point5(self):
|
||||
dataset = [(1, 0.5, 0.5),
|
||||
(2, 0.5, 0.25),
|
||||
(3, 0.5, 0.166666666667),
|
||||
(4, 0.5, 0.09375),
|
||||
(5, 0.5, 0.056),
|
||||
(6, 0.5, 0.0327932098765),
|
||||
(7, 0.5, 0.0191958707681),
|
||||
(8, 0.5, 0.0112953186035),
|
||||
(9, 0.5, 0.00661933257355),
|
||||
(10, 0.5, 0.003888705)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnov, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, -1] = 1 - dataset[:, -1]
|
||||
FuncData(
|
||||
_smirnovc, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_n_equals_1(self):
|
||||
x = np.linspace(0, 1, 101, endpoint=True)
|
||||
dataset = np.column_stack([[1]*len(x), x, 1-x])
|
||||
FuncData(
|
||||
smirnov, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, -1] = 1 - dataset[:, -1]
|
||||
FuncData(
|
||||
_smirnovc, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_n_equals_2(self):
|
||||
x = np.linspace(0.5, 1, 101, endpoint=True)
|
||||
p = np.power(1-x, 2)
|
||||
n = np.array([2] * len(x))
|
||||
dataset = np.column_stack([n, x, p])
|
||||
FuncData(
|
||||
smirnov, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, -1] = 1 - dataset[:, -1]
|
||||
FuncData(
|
||||
_smirnovc, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_n_equals_3(self):
|
||||
x = np.linspace(0.7, 1, 31, endpoint=True)
|
||||
p = np.power(1-x, 3)
|
||||
n = np.array([3] * len(x))
|
||||
dataset = np.column_stack([n, x, p])
|
||||
FuncData(
|
||||
smirnov, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, -1] = 1 - dataset[:, -1]
|
||||
FuncData(
|
||||
_smirnovc, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_n_large(self):
|
||||
# test for large values of n
|
||||
# Probabilities should go down as n goes up
|
||||
x = 0.4
|
||||
pvals = np.array([smirnov(n, x) for n in range(400, 1100, 20)])
|
||||
dfs = np.diff(pvals)
|
||||
assert_(np.all(dfs <= 0), msg=f'Not all diffs negative {dfs}')
|
||||
|
||||
|
||||
class TestSmirnovi:
|
||||
def test_nan(self):
|
||||
assert_(np.isnan(smirnovi(1, np.nan)))
|
||||
|
||||
def test_basic(self):
|
||||
dataset = [(1, 0.4, 0.6),
|
||||
(1, 0.6, 0.4),
|
||||
(1, 0.99, 0.01),
|
||||
(1, 0.01, 0.99),
|
||||
(2, 0.125 * 0.125, 0.875),
|
||||
(3, 0.125 * 0.125 * 0.125, 0.875),
|
||||
(10, 1.0 / 16 ** 10, 1 - 1.0 / 16)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnovi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, 1] = 1 - dataset[:, 1]
|
||||
FuncData(
|
||||
_smirnovci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_x_equals_0(self):
|
||||
dataset = [(n, 0, 1) for n in itertools.chain(range(2, 20), range(1010, 1020))]
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnovi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, 1] = 1 - dataset[:, 1]
|
||||
FuncData(
|
||||
_smirnovci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_x_equals_1(self):
|
||||
dataset = [(n, 1, 0) for n in itertools.chain(range(2, 20), range(1010, 1020))]
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnovi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, 1] = 1 - dataset[:, 1]
|
||||
FuncData(
|
||||
_smirnovci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_n_equals_1(self):
|
||||
pp = np.linspace(0, 1, 101, endpoint=True)
|
||||
# dataset = np.array([(1, p, 1-p) for p in pp])
|
||||
dataset = np.column_stack([[1]*len(pp), pp, 1-pp])
|
||||
FuncData(
|
||||
smirnovi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, 1] = 1 - dataset[:, 1]
|
||||
FuncData(
|
||||
_smirnovci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_n_equals_2(self):
|
||||
x = np.linspace(0.5, 1, 101, endpoint=True)
|
||||
p = np.power(1-x, 2)
|
||||
n = np.array([2] * len(x))
|
||||
dataset = np.column_stack([n, p, x])
|
||||
FuncData(
|
||||
smirnovi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, 1] = 1 - dataset[:, 1]
|
||||
FuncData(
|
||||
_smirnovci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_n_equals_3(self):
|
||||
x = np.linspace(0.7, 1, 31, endpoint=True)
|
||||
p = np.power(1-x, 3)
|
||||
n = np.array([3] * len(x))
|
||||
dataset = np.column_stack([n, p, x])
|
||||
FuncData(
|
||||
smirnovi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, 1] = 1 - dataset[:, 1]
|
||||
FuncData(
|
||||
_smirnovci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_round_trip(self):
|
||||
def _sm_smi(n, p):
|
||||
return smirnov(n, smirnovi(n, p))
|
||||
|
||||
def _smc_smci(n, p):
|
||||
return _smirnovc(n, _smirnovci(n, p))
|
||||
|
||||
dataset = [(1, 0.4, 0.4),
|
||||
(1, 0.6, 0.6),
|
||||
(2, 0.875, 0.875),
|
||||
(3, 0.875, 0.875),
|
||||
(3, 0.125, 0.125),
|
||||
(10, 0.999, 0.999),
|
||||
(10, 0.0001, 0.0001)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
_sm_smi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
FuncData(
|
||||
_smc_smci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_x_equals_0point5(self):
|
||||
dataset = [(1, 0.5, 0.5),
|
||||
(2, 0.5, 0.366025403784),
|
||||
(2, 0.25, 0.5),
|
||||
(3, 0.5, 0.297156508177),
|
||||
(4, 0.5, 0.255520481121),
|
||||
(5, 0.5, 0.234559536069),
|
||||
(6, 0.5, 0.21715965898),
|
||||
(7, 0.5, 0.202722580034),
|
||||
(8, 0.5, 0.190621765256),
|
||||
(9, 0.5, 0.180363501362),
|
||||
(10, 0.5, 0.17157867006)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(
|
||||
smirnovi, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
dataset[:, 1] = 1 - dataset[:, 1]
|
||||
FuncData(
|
||||
_smirnovci, dataset, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
|
||||
class TestSmirnovp:
|
||||
def test_nan(self):
|
||||
assert_(np.isnan(_smirnovp(1, np.nan)))
|
||||
|
||||
def test_basic(self):
|
||||
# Check derivative at endpoints
|
||||
n1_10 = np.arange(1, 10)
|
||||
dataset0 = np.column_stack([n1_10,
|
||||
np.full_like(n1_10, 0),
|
||||
np.full_like(n1_10, -1)])
|
||||
FuncData(
|
||||
_smirnovp, dataset0, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
n2_10 = np.arange(2, 10)
|
||||
dataset1 = np.column_stack([n2_10,
|
||||
np.full_like(n2_10, 1.0),
|
||||
np.full_like(n2_10, 0)])
|
||||
FuncData(
|
||||
_smirnovp, dataset1, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_oneminusoneovern(self):
|
||||
# Check derivative at x=1-1/n
|
||||
n = np.arange(1, 20)
|
||||
x = 1.0/n
|
||||
xm1 = 1-1.0/n
|
||||
pp1 = -n * x**(n-1)
|
||||
pp1 -= (1-np.sign(n-2)**2) * 0.5 # n=2, x=0.5, 1-1/n = 0.5, need to adjust
|
||||
dataset1 = np.column_stack([n, xm1, pp1])
|
||||
FuncData(
|
||||
_smirnovp, dataset1, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_oneovertwon(self):
|
||||
# Check derivative at x=1/2n (Discontinuous at x=1/n, so check at x=1/2n)
|
||||
n = np.arange(1, 20)
|
||||
x = 1.0/2/n
|
||||
pp = -(n*x+1) * (1+x)**(n-2)
|
||||
dataset0 = np.column_stack([n, x, pp])
|
||||
FuncData(
|
||||
_smirnovp, dataset0, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_oneovern(self):
|
||||
# Check derivative at x=1/n
|
||||
# (Discontinuous at x=1/n, hard to tell if x==1/n, only use n=power of 2)
|
||||
n = 2**np.arange(1, 10)
|
||||
x = 1.0/n
|
||||
pp = -(n*x+1) * (1+x)**(n-2) + 0.5
|
||||
dataset0 = np.column_stack([n, x, pp])
|
||||
FuncData(
|
||||
_smirnovp, dataset0, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
def test_oneovernclose(self):
|
||||
# Check derivative at x=1/n
|
||||
# (Discontinuous at x=1/n, test on either side: x=1/n +/- 2epsilon)
|
||||
n = np.arange(3, 20)
|
||||
|
||||
x = 1.0/n - 2*np.finfo(float).eps
|
||||
pp = -(n*x+1) * (1+x)**(n-2)
|
||||
dataset0 = np.column_stack([n, x, pp])
|
||||
FuncData(
|
||||
_smirnovp, dataset0, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
x = 1.0/n + 2*np.finfo(float).eps
|
||||
pp = -(n*x+1) * (1+x)**(n-2) + 1
|
||||
dataset1 = np.column_stack([n, x, pp])
|
||||
FuncData(
|
||||
_smirnovp, dataset1, (0, 1), 2, rtol=_rtol
|
||||
).check(dtypes=[int, float, float])
|
||||
|
||||
|
||||
class TestKolmogorov:
|
||||
def test_nan(self):
|
||||
assert_(np.isnan(kolmogorov(np.nan)))
|
||||
|
||||
def test_basic(self):
|
||||
dataset = [(0, 1.0),
|
||||
(0.5, 0.96394524366487511),
|
||||
(0.8275735551899077, 0.5000000000000000),
|
||||
(1, 0.26999967167735456),
|
||||
(2, 0.00067092525577969533)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(kolmogorov, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
def test_linspace(self):
|
||||
x = np.linspace(0, 2.0, 21)
|
||||
dataset = [1.0000000000000000, 1.0000000000000000, 0.9999999999994950,
|
||||
0.9999906941986655, 0.9971923267772983, 0.9639452436648751,
|
||||
0.8642827790506042, 0.7112351950296890, 0.5441424115741981,
|
||||
0.3927307079406543, 0.2699996716773546, 0.1777181926064012,
|
||||
0.1122496666707249, 0.0680922218447664, 0.0396818795381144,
|
||||
0.0222179626165251, 0.0119520432391966, 0.0061774306344441,
|
||||
0.0030676213475797, 0.0014636048371873, 0.0006709252557797]
|
||||
|
||||
dataset_c = [0.0000000000000000, 6.609305242245699e-53, 5.050407338670114e-13,
|
||||
9.305801334566668e-06, 0.0028076732227017, 0.0360547563351249,
|
||||
0.1357172209493958, 0.2887648049703110, 0.4558575884258019,
|
||||
0.6072692920593457, 0.7300003283226455, 0.8222818073935988,
|
||||
0.8877503333292751, 0.9319077781552336, 0.9603181204618857,
|
||||
0.9777820373834749, 0.9880479567608034, 0.9938225693655559,
|
||||
0.9969323786524203, 0.9985363951628127, 0.9993290747442203]
|
||||
|
||||
dataset = np.column_stack([x, dataset])
|
||||
FuncData(kolmogorov, dataset, (0,), 1, rtol=_rtol).check()
|
||||
dataset_c = np.column_stack([x, dataset_c])
|
||||
FuncData(_kolmogc, dataset_c, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
def test_linspacei(self):
|
||||
p = np.linspace(0, 1.0, 21, endpoint=True)
|
||||
dataset = [np.inf, 1.3580986393225507, 1.2238478702170823,
|
||||
1.1379465424937751, 1.0727491749396481, 1.0191847202536859,
|
||||
0.9730633753323726, 0.9320695842357622, 0.8947644549851197,
|
||||
0.8601710725555463, 0.8275735551899077, 0.7964065373291559,
|
||||
0.7661855555617682, 0.7364542888171910, 0.7067326523068980,
|
||||
0.6764476915028201, 0.6448126061663567, 0.6105590999244391,
|
||||
0.5711732651063401, 0.5196103791686224, 0.0000000000000000]
|
||||
|
||||
dataset_c = [0.0000000000000000, 0.5196103791686225, 0.5711732651063401,
|
||||
0.6105590999244391, 0.6448126061663567, 0.6764476915028201,
|
||||
0.7067326523068980, 0.7364542888171910, 0.7661855555617682,
|
||||
0.7964065373291559, 0.8275735551899077, 0.8601710725555463,
|
||||
0.8947644549851196, 0.9320695842357622, 0.9730633753323727,
|
||||
1.0191847202536859, 1.0727491749396481, 1.1379465424937754,
|
||||
1.2238478702170825, 1.3580986393225509, np.inf]
|
||||
|
||||
dataset = np.column_stack([p[1:], dataset[1:]])
|
||||
FuncData(kolmogi, dataset, (0,), 1, rtol=_rtol).check()
|
||||
dataset_c = np.column_stack([p[:-1], dataset_c[:-1]])
|
||||
FuncData(_kolmogci, dataset_c, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
def test_smallx(self):
|
||||
epsilon = 0.1 ** np.arange(1, 14)
|
||||
x = np.array([0.571173265106, 0.441027698518, 0.374219690278, 0.331392659217,
|
||||
0.300820537459, 0.277539353999, 0.259023494805, 0.243829561254,
|
||||
0.231063086389, 0.220135543236, 0.210641372041, 0.202290283658,
|
||||
0.19487060742])
|
||||
|
||||
dataset = np.column_stack([x, 1-epsilon])
|
||||
FuncData(kolmogorov, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
def test_round_trip(self):
|
||||
def _ki_k(_x):
|
||||
return kolmogi(kolmogorov(_x))
|
||||
|
||||
def _kci_kc(_x):
|
||||
return _kolmogci(_kolmogc(_x))
|
||||
|
||||
x = np.linspace(0.0, 2.0, 21, endpoint=True)
|
||||
# Exclude 0.1, 0.2. 0.2 almost makes succeeds, but 0.1 has no chance.
|
||||
x02 = x[(x == 0) | (x > 0.21)]
|
||||
dataset02 = np.column_stack([x02, x02])
|
||||
FuncData(_ki_k, dataset02, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
dataset = np.column_stack([x, x])
|
||||
FuncData(_kci_kc, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
|
||||
class TestKolmogi:
|
||||
def test_nan(self):
|
||||
assert_(np.isnan(kolmogi(np.nan)))
|
||||
|
||||
def test_basic(self):
|
||||
dataset = [(1.0, 0),
|
||||
(0.96394524366487511, 0.5),
|
||||
(0.9, 0.571173265106),
|
||||
(0.5000000000000000, 0.8275735551899077),
|
||||
(0.26999967167735456, 1),
|
||||
(0.00067092525577969533, 2)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(kolmogi, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
def test_smallpcdf(self):
|
||||
epsilon = 0.5 ** np.arange(1, 55, 3)
|
||||
# kolmogi(1-p) == _kolmogci(p) if 1-(1-p) == p, but not necessarily otherwise
|
||||
# Use epsilon s.t. 1-(1-epsilon)) == epsilon,
|
||||
# so can use same x-array for both results
|
||||
|
||||
x = np.array([0.8275735551899077, 0.5345255069097583, 0.4320114038786941,
|
||||
0.3736868442620478, 0.3345161714909591, 0.3057833329315859,
|
||||
0.2835052890528936, 0.2655578150208676, 0.2506869966107999,
|
||||
0.2380971058736669, 0.2272549289962079, 0.2177876361600040,
|
||||
0.2094254686862041, 0.2019676748836232, 0.1952612948137504,
|
||||
0.1891874239646641, 0.1836520225050326, 0.1785795904846466])
|
||||
|
||||
dataset = np.column_stack([1-epsilon, x])
|
||||
FuncData(kolmogi, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
dataset = np.column_stack([epsilon, x])
|
||||
FuncData(_kolmogci, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
def test_smallpsf(self):
|
||||
epsilon = 0.5 ** np.arange(1, 55, 3)
|
||||
# kolmogi(p) == _kolmogci(1-p) if 1-(1-p) == p, but not necessarily otherwise
|
||||
# Use epsilon s.t. 1-(1-epsilon)) == epsilon,
|
||||
# so can use same x-array for both results
|
||||
|
||||
x = np.array([0.8275735551899077, 1.3163786275161036, 1.6651092133663343,
|
||||
1.9525136345289607, 2.2027324540033235, 2.4272929437460848,
|
||||
2.6327688477341593, 2.8233300509220260, 3.0018183401530627,
|
||||
3.1702735084088891, 3.3302184446307912, 3.4828258153113318,
|
||||
3.6290214150152051, 3.7695513262825959, 3.9050272690877326,
|
||||
4.0359582187082550, 4.1627730557884890, 4.2858371743264527])
|
||||
|
||||
dataset = np.column_stack([epsilon, x])
|
||||
FuncData(kolmogi, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
dataset = np.column_stack([1-epsilon, x])
|
||||
FuncData(_kolmogci, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
def test_round_trip(self):
|
||||
def _k_ki(_p):
|
||||
return kolmogorov(kolmogi(_p))
|
||||
|
||||
p = np.linspace(0.1, 1.0, 10, endpoint=True)
|
||||
dataset = np.column_stack([p, p])
|
||||
FuncData(_k_ki, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
||||
|
||||
class TestKolmogp:
|
||||
def test_nan(self):
|
||||
assert_(np.isnan(_kolmogp(np.nan)))
|
||||
|
||||
def test_basic(self):
|
||||
dataset = [(0.000000, -0.0),
|
||||
(0.200000, -1.532420541338916e-10),
|
||||
(0.400000, -0.1012254419260496),
|
||||
(0.600000, -1.324123244249925),
|
||||
(0.800000, -1.627024345636592),
|
||||
(1.000000, -1.071948558356941),
|
||||
(1.200000, -0.538512430720529),
|
||||
(1.400000, -0.2222133182429472),
|
||||
(1.600000, -0.07649302775520538),
|
||||
(1.800000, -0.02208687346347873),
|
||||
(2.000000, -0.005367402045629683)]
|
||||
|
||||
dataset = np.asarray(dataset)
|
||||
FuncData(_kolmogp, dataset, (0,), 1, rtol=_rtol).check()
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
#
|
||||
# Tests for the lambertw function,
|
||||
# Adapted from the MPMath tests [1] by Yosef Meller, mellerf@netvision.net.il
|
||||
# Distributed under the same license as SciPy itself.
|
||||
#
|
||||
# [1] mpmath source code, Subversion revision 992
|
||||
# http://code.google.com/p/mpmath/source/browse/trunk/mpmath/tests/test_functions2.py?spec=svn994&r=992
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_, assert_equal, assert_array_almost_equal
|
||||
from scipy.special import lambertw
|
||||
from numpy import nan, inf, pi, e, isnan, log, r_, array, complex128
|
||||
|
||||
from scipy.special._testutils import FuncData
|
||||
|
||||
|
||||
def test_values():
|
||||
assert_(isnan(lambertw(nan)))
|
||||
assert_equal(lambertw(inf,1).real, inf)
|
||||
assert_equal(lambertw(inf,1).imag, 2*pi)
|
||||
assert_equal(lambertw(-inf,1).real, inf)
|
||||
assert_equal(lambertw(-inf,1).imag, 3*pi)
|
||||
|
||||
assert_equal(lambertw(1.), lambertw(1., 0))
|
||||
|
||||
data = [
|
||||
(0,0, 0),
|
||||
(0+0j,0, 0),
|
||||
(inf,0, inf),
|
||||
(0,-1, -inf),
|
||||
(0,1, -inf),
|
||||
(0,3, -inf),
|
||||
(e,0, 1),
|
||||
(1,0, 0.567143290409783873),
|
||||
(-pi/2,0, 1j*pi/2),
|
||||
(-log(2)/2,0, -log(2)),
|
||||
(0.25,0, 0.203888354702240164),
|
||||
(-0.25,0, -0.357402956181388903),
|
||||
(-1./10000,0, -0.000100010001500266719),
|
||||
(-0.25,-1, -2.15329236411034965),
|
||||
(0.25,-1, -3.00899800997004620-4.07652978899159763j),
|
||||
(-0.25,-1, -2.15329236411034965),
|
||||
(0.25,1, -3.00899800997004620+4.07652978899159763j),
|
||||
(-0.25,1, -3.48973228422959210+7.41405453009603664j),
|
||||
(-4,0, 0.67881197132094523+1.91195078174339937j),
|
||||
(-4,1, -0.66743107129800988+7.76827456802783084j),
|
||||
(-4,-1, 0.67881197132094523-1.91195078174339937j),
|
||||
(1000,0, 5.24960285240159623),
|
||||
(1000,1, 4.91492239981054535+5.44652615979447070j),
|
||||
(1000,-1, 4.91492239981054535-5.44652615979447070j),
|
||||
(1000,5, 3.5010625305312892+29.9614548941181328j),
|
||||
(3+4j,0, 1.281561806123775878+0.533095222020971071j),
|
||||
(-0.4+0.4j,0, -0.10396515323290657+0.61899273315171632j),
|
||||
(3+4j,1, -0.11691092896595324+5.61888039871282334j),
|
||||
(3+4j,-1, 0.25856740686699742-3.85211668616143559j),
|
||||
(-0.5,-1, -0.794023632344689368-0.770111750510379110j),
|
||||
(-1./10000,1, -11.82350837248724344+6.80546081842002101j),
|
||||
(-1./10000,-1, -11.6671145325663544),
|
||||
(-1./10000,-2, -11.82350837248724344-6.80546081842002101j),
|
||||
(-1./100000,4, -14.9186890769540539+26.1856750178782046j),
|
||||
(-1./100000,5, -15.0931437726379218666+32.5525721210262290086j),
|
||||
((2+1j)/10,0, 0.173704503762911669+0.071781336752835511j),
|
||||
((2+1j)/10,1, -3.21746028349820063+4.56175438896292539j),
|
||||
((2+1j)/10,-1, -3.03781405002993088-3.53946629633505737j),
|
||||
((2+1j)/10,4, -4.6878509692773249+23.8313630697683291j),
|
||||
(-(2+1j)/10,0, -0.226933772515757933-0.164986470020154580j),
|
||||
(-(2+1j)/10,1, -2.43569517046110001+0.76974067544756289j),
|
||||
(-(2+1j)/10,-1, -3.54858738151989450-6.91627921869943589j),
|
||||
(-(2+1j)/10,4, -4.5500846928118151+20.6672982215434637j),
|
||||
(pi,0, 1.073658194796149172092178407024821347547745350410314531),
|
||||
|
||||
# Former bug in generated branch,
|
||||
(-0.5+0.002j,0, -0.78917138132659918344 + 0.76743539379990327749j),
|
||||
(-0.5-0.002j,0, -0.78917138132659918344 - 0.76743539379990327749j),
|
||||
(-0.448+0.4j,0, -0.11855133765652382241 + 0.66570534313583423116j),
|
||||
(-0.448-0.4j,0, -0.11855133765652382241 - 0.66570534313583423116j),
|
||||
]
|
||||
data = array(data, dtype=complex128)
|
||||
|
||||
def w(x, y):
|
||||
return lambertw(x, y.real.astype(int))
|
||||
with np.errstate(all='ignore'):
|
||||
FuncData(w, data, (0,1), 2, rtol=1e-10, atol=1e-13).check()
|
||||
|
||||
|
||||
def test_ufunc():
|
||||
assert_array_almost_equal(
|
||||
lambertw(r_[0., e, 1.]), r_[0., 1., 0.567143290409783873])
|
||||
|
||||
|
||||
def test_lambertw_ufunc_loop_selection():
|
||||
# see https://github.com/scipy/scipy/issues/4895
|
||||
dt = np.dtype(np.complex128)
|
||||
assert_equal(lambertw(0, 0, 0).dtype, dt)
|
||||
assert_equal(lambertw([0], 0, 0).dtype, dt)
|
||||
assert_equal(lambertw(0, [0], 0).dtype, dt)
|
||||
assert_equal(lambertw(0, 0, [0]).dtype, dt)
|
||||
assert_equal(lambertw([0], [0], [0]).dtype, dt)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('z', [1e-316, -2e-320j, -5e-318+1e-320j])
|
||||
def test_lambertw_subnormal_k0(z):
|
||||
# Verify that subnormal inputs are handled correctly on
|
||||
# the branch k=0 (regression test for gh-16291).
|
||||
w = lambertw(z)
|
||||
# For values this small, we can be sure that numerically,
|
||||
# lambertw(z) is z.
|
||||
assert w == z
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,85 @@
|
|||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
|
||||
from scipy.special._ufuncs import _log1mexp
|
||||
|
||||
# # Test cases generated with the script
|
||||
#
|
||||
# import numpy as np
|
||||
|
||||
# from mpmath import mp
|
||||
|
||||
|
||||
# def mp_log1mexp(x):
|
||||
# with mp.workdps(324):
|
||||
# return float(mp.log(mp.one - mp.exp(x)))
|
||||
|
||||
# X = np.concat([-np.logspace(-1, -300, 20), np.linspace(-745, -1, 20)])
|
||||
|
||||
# cases = [(float(x), mp_log1mexp(x)) for x in X]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"x,expected",
|
||||
[
|
||||
(-0.1, -2.3521684610440907),
|
||||
(-1.8329807108324374e-17, -38.538003135374026),
|
||||
(-3.359818286283788e-33, -74.773421177754),
|
||||
(-6.1584821106602796e-49, -111.00883922013399),
|
||||
(-1.1288378916846929e-64, -147.24425726251397),
|
||||
(-2.0691380811148324e-80, -183.47967530489393),
|
||||
(-3.792690190732269e-96, -219.71509334727392),
|
||||
(-6.951927961775534e-112, -255.95051138965394),
|
||||
(-1.2742749857031425e-127, -292.1859294320339),
|
||||
(-2.3357214690901785e-143, -328.42134747441384),
|
||||
(-4.281332398719571e-159, -364.6567655167938),
|
||||
(-7.847599703514559e-175, -400.8921835591739),
|
||||
(-1.4384498882876776e-190, -437.1276016015538),
|
||||
(-2.6366508987304307e-206, -473.3630196439338),
|
||||
(-4.832930238571653e-222, -509.59843768631384),
|
||||
(-8.858667904100796e-238, -545.8338557286938),
|
||||
(-1.623776739188744e-253, -582.0692737710738),
|
||||
(-2.9763514416312156e-269, -618.3046918134538),
|
||||
(-5.455594781168782e-285, -654.5401098558336),
|
||||
(-1e-300, -690.7755278982137),
|
||||
(-745.0, -5e-324),
|
||||
(-705.8421052631579, -2.8619931451743316e-307),
|
||||
(-666.6842105263158, -2.9021923726875757e-290),
|
||||
(-627.5263157894738, -2.9429562339405562e-273),
|
||||
(-588.3684210526316, -2.9842926597143714e-256),
|
||||
(-549.2105263157895, -3.0262096921839423e-239),
|
||||
(-510.0526315789474, -3.0687154864846747e-222),
|
||||
(-470.89473684210526, -3.1118183122979086e-205),
|
||||
(-431.7368421052632, -3.155526555459449e-188),
|
||||
(-392.5789473684211, -3.1998487195921207e-171),
|
||||
(-353.42105263157896, -3.2447934277596653e-154),
|
||||
(-314.2631578947369, -3.2903694241438367e-137),
|
||||
(-275.1052631578948, -3.3365855757467166e-120),
|
||||
(-235.94736842105266, -3.3834508741152875e-103),
|
||||
(-196.78947368421052, -3.4309744370903894e-86),
|
||||
(-157.63157894736844, -3.4791655105810003e-69),
|
||||
(-118.47368421052636, -3.528033470363468e-52),
|
||||
(-79.31578947368428, -3.577587823905024e-35),
|
||||
(-40.157894736842195, -3.627838212213697e-18),
|
||||
(-1.0, -0.4586751453870819),
|
||||
]
|
||||
)
|
||||
def test_log1mexp(x, expected):
|
||||
observed = _log1mexp(x)
|
||||
assert_allclose(observed, expected, rtol=1e-15)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x", [1.1, 1e10, np.inf])
|
||||
def test_log1mexp_out_of_domain(x):
|
||||
observed = _log1mexp(x)
|
||||
assert np.isnan(observed)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"x,expected",
|
||||
[(-np.inf, -0.0), (0.0, -np.inf), (-0.0, -np.inf), (np.nan, np.nan)]
|
||||
)
|
||||
def test_log1mexp_extreme(x, expected):
|
||||
observed = _log1mexp(x)
|
||||
assert_equal(expected, observed)
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_
|
||||
|
||||
from scipy.special._testutils import FuncData
|
||||
from scipy.special import gamma, gammaln, loggamma
|
||||
|
||||
|
||||
def test_identities1():
|
||||
# test the identity exp(loggamma(z)) = gamma(z)
|
||||
x = np.array([-99.5, -9.5, -0.5, 0.5, 9.5, 99.5])
|
||||
y = x.copy()
|
||||
x, y = np.meshgrid(x, y)
|
||||
z = (x + 1J*y).flatten()
|
||||
dataset = np.vstack((z, gamma(z))).T
|
||||
|
||||
def f(z):
|
||||
return np.exp(loggamma(z))
|
||||
|
||||
FuncData(f, dataset, 0, 1, rtol=1e-14, atol=1e-14).check()
|
||||
|
||||
|
||||
def test_identities2():
|
||||
# test the identity loggamma(z + 1) = log(z) + loggamma(z)
|
||||
x = np.array([-99.5, -9.5, -0.5, 0.5, 9.5, 99.5])
|
||||
y = x.copy()
|
||||
x, y = np.meshgrid(x, y)
|
||||
z = (x + 1J*y).flatten()
|
||||
dataset = np.vstack((z, np.log(z) + loggamma(z))).T
|
||||
|
||||
def f(z):
|
||||
return loggamma(z + 1)
|
||||
|
||||
FuncData(f, dataset, 0, 1, rtol=1e-14, atol=1e-14).check()
|
||||
|
||||
|
||||
def test_complex_dispatch_realpart():
|
||||
# Test that the real parts of loggamma and gammaln agree on the
|
||||
# real axis.
|
||||
x = np.r_[-np.logspace(10, -10), np.logspace(-10, 10)] + 0.5
|
||||
|
||||
dataset = np.vstack((x, gammaln(x))).T
|
||||
|
||||
def f(z):
|
||||
z = np.array(z, dtype='complex128')
|
||||
return loggamma(z).real
|
||||
|
||||
FuncData(f, dataset, 0, 1, rtol=1e-14, atol=1e-14).check()
|
||||
|
||||
|
||||
def test_real_dispatch():
|
||||
x = np.logspace(-10, 10) + 0.5
|
||||
dataset = np.vstack((x, gammaln(x))).T
|
||||
|
||||
FuncData(loggamma, dataset, 0, 1, rtol=1e-14, atol=1e-14).check()
|
||||
assert_(loggamma(0) == np.inf)
|
||||
assert_(np.isnan(loggamma(-1)))
|
||||
|
||||
|
||||
def test_gh_6536():
|
||||
z = loggamma(complex(-3.4, +0.0))
|
||||
zbar = loggamma(complex(-3.4, -0.0))
|
||||
assert_allclose(z, zbar.conjugate(), rtol=1e-15, atol=0)
|
||||
|
||||
|
||||
def test_branch_cut():
|
||||
# Make sure negative zero is treated correctly
|
||||
x = -np.logspace(300, -30, 100)
|
||||
z = np.asarray([complex(x0, 0.0) for x0 in x])
|
||||
zbar = np.asarray([complex(x0, -0.0) for x0 in x])
|
||||
assert_allclose(z, zbar.conjugate(), rtol=1e-15, atol=0)
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
import numpy as np
|
||||
from numpy.testing import (assert_equal, assert_almost_equal,
|
||||
assert_allclose)
|
||||
from scipy.special import logit, expit, log_expit
|
||||
|
||||
|
||||
class TestLogit:
|
||||
|
||||
def check_logit_out(self, a, expected):
|
||||
actual = logit(a)
|
||||
assert_equal(actual.dtype, a.dtype)
|
||||
rtol = 16*np.finfo(a.dtype).eps
|
||||
assert_allclose(actual, expected, rtol=rtol)
|
||||
|
||||
def test_float32(self):
|
||||
a = np.concatenate((np.linspace(0, 1, 10, dtype=np.float32),
|
||||
[np.float32(0.0001), np.float32(0.49999),
|
||||
np.float32(0.50001)]))
|
||||
# Expected values computed with mpmath from float32 inputs, e.g.
|
||||
# from mpmath import mp
|
||||
# mp.dps = 200
|
||||
# a = np.float32(1/9)
|
||||
# print(np.float32(mp.log(a) - mp.log1p(-a)))
|
||||
# prints `-2.0794415`.
|
||||
expected = np.array([-np.inf, -2.0794415, -1.2527629, -6.9314712e-01,
|
||||
-2.2314353e-01, 2.2314365e-01, 6.9314724e-01,
|
||||
1.2527630, 2.0794415, np.inf,
|
||||
-9.2102404, -4.0054321e-05, 4.0054321e-05],
|
||||
dtype=np.float32)
|
||||
self.check_logit_out(a, expected)
|
||||
|
||||
def test_float64(self):
|
||||
a = np.concatenate((np.linspace(0, 1, 10, dtype=np.float64),
|
||||
[1e-8, 0.4999999999999, 0.50000000001]))
|
||||
# Expected values computed with mpmath.
|
||||
expected = np.array([-np.inf,
|
||||
-2.079441541679836,
|
||||
-1.252762968495368,
|
||||
-0.6931471805599454,
|
||||
-0.22314355131420985,
|
||||
0.22314355131420985,
|
||||
0.6931471805599452,
|
||||
1.2527629684953674,
|
||||
2.0794415416798353,
|
||||
np.inf,
|
||||
-18.420680733952366,
|
||||
-3.999023334699814e-13,
|
||||
4.000000330961484e-11])
|
||||
self.check_logit_out(a, expected)
|
||||
|
||||
def test_nan(self):
|
||||
expected = np.array([np.nan]*4)
|
||||
with np.errstate(invalid='ignore'):
|
||||
actual = logit(np.array([-3., -2., 2., 3.]))
|
||||
|
||||
assert_equal(expected, actual)
|
||||
|
||||
|
||||
class TestExpit:
|
||||
def check_expit_out(self, dtype, expected):
|
||||
a = np.linspace(-4, 4, 10)
|
||||
a = np.array(a, dtype=dtype)
|
||||
actual = expit(a)
|
||||
assert_almost_equal(actual, expected)
|
||||
assert_equal(actual.dtype, np.dtype(dtype))
|
||||
|
||||
def test_float32(self):
|
||||
expected = np.array([0.01798621, 0.04265125,
|
||||
0.09777259, 0.20860852,
|
||||
0.39068246, 0.60931754,
|
||||
0.79139149, 0.9022274,
|
||||
0.95734876, 0.98201376], dtype=np.float32)
|
||||
self.check_expit_out('f4', expected)
|
||||
|
||||
def test_float64(self):
|
||||
expected = np.array([0.01798621, 0.04265125,
|
||||
0.0977726, 0.20860853,
|
||||
0.39068246, 0.60931754,
|
||||
0.79139147, 0.9022274,
|
||||
0.95734875, 0.98201379])
|
||||
self.check_expit_out('f8', expected)
|
||||
|
||||
def test_large(self):
|
||||
for dtype in (np.float32, np.float64, np.longdouble):
|
||||
for n in (88, 89, 709, 710, 11356, 11357):
|
||||
n = np.array(n, dtype=dtype)
|
||||
assert_allclose(expit(n), 1.0, atol=1e-20)
|
||||
assert_allclose(expit(-n), 0.0, atol=1e-20)
|
||||
assert_equal(expit(n).dtype, dtype)
|
||||
assert_equal(expit(-n).dtype, dtype)
|
||||
|
||||
|
||||
class TestLogExpit:
|
||||
|
||||
def test_large_negative(self):
|
||||
x = np.array([-10000.0, -750.0, -500.0, -35.0])
|
||||
y = log_expit(x)
|
||||
assert_equal(y, x)
|
||||
|
||||
def test_large_positive(self):
|
||||
x = np.array([750.0, 1000.0, 10000.0])
|
||||
y = log_expit(x)
|
||||
# y will contain -0.0, and -0.0 is used in the expected value,
|
||||
# but assert_equal does not check the sign of zeros, and I don't
|
||||
# think the sign is an essential part of the test (i.e. it would
|
||||
# probably be OK if log_expit(1000) returned 0.0 instead of -0.0).
|
||||
assert_equal(y, np.array([-0.0, -0.0, -0.0]))
|
||||
|
||||
def test_basic_float64(self):
|
||||
x = np.array([-32, -20, -10, -3, -1, -0.1, -1e-9,
|
||||
0, 1e-9, 0.1, 1, 10, 100, 500, 710, 725, 735])
|
||||
y = log_expit(x)
|
||||
#
|
||||
# Expected values were computed with mpmath:
|
||||
#
|
||||
# import mpmath
|
||||
#
|
||||
# mpmath.mp.dps = 100
|
||||
#
|
||||
# def mp_log_expit(x):
|
||||
# return -mpmath.log1p(mpmath.exp(-x))
|
||||
#
|
||||
# expected = [float(mp_log_expit(t)) for t in x]
|
||||
#
|
||||
expected = [-32.000000000000014, -20.000000002061153,
|
||||
-10.000045398899218, -3.048587351573742,
|
||||
-1.3132616875182228, -0.7443966600735709,
|
||||
-0.6931471810599453, -0.6931471805599453,
|
||||
-0.6931471800599454, -0.6443966600735709,
|
||||
-0.3132616875182228, -4.539889921686465e-05,
|
||||
-3.720075976020836e-44, -7.124576406741286e-218,
|
||||
-4.47628622567513e-309, -1.36930634e-315,
|
||||
-6.217e-320]
|
||||
|
||||
# When tested locally, only one value in y was not exactly equal to
|
||||
# expected. That was for x=1, and the y value differed from the
|
||||
# expected by 1 ULP. For this test, however, I'll use rtol=1e-15.
|
||||
assert_allclose(y, expected, rtol=1e-15)
|
||||
|
||||
def test_basic_float32(self):
|
||||
x = np.array([-32, -20, -10, -3, -1, -0.1, -1e-9,
|
||||
0, 1e-9, 0.1, 1, 10, 100], dtype=np.float32)
|
||||
y = log_expit(x)
|
||||
#
|
||||
# Expected values were computed with mpmath:
|
||||
#
|
||||
# import mpmath
|
||||
#
|
||||
# mpmath.mp.dps = 100
|
||||
#
|
||||
# def mp_log_expit(x):
|
||||
# return -mpmath.log1p(mpmath.exp(-x))
|
||||
#
|
||||
# expected = [np.float32(mp_log_expit(t)) for t in x]
|
||||
#
|
||||
expected = np.array([-32.0, -20.0, -10.000046, -3.0485873,
|
||||
-1.3132616, -0.7443967, -0.6931472,
|
||||
-0.6931472, -0.6931472, -0.64439666,
|
||||
-0.3132617, -4.5398898e-05, -3.8e-44],
|
||||
dtype=np.float32)
|
||||
|
||||
assert_allclose(y, expected, rtol=5e-7)
|
||||
|
|
@ -0,0 +1,469 @@
|
|||
import itertools as it
|
||||
import math
|
||||
import pytest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from scipy._lib._array_api import (is_array_api_strict, make_xp_test_case,
|
||||
xp_default_dtype, xp_device)
|
||||
from scipy._lib._array_api_no_0d import (xp_assert_equal, xp_assert_close,
|
||||
xp_assert_less)
|
||||
|
||||
from scipy.special import log_softmax, logsumexp, softmax
|
||||
from scipy.special._logsumexp import _wrap_radians
|
||||
|
||||
|
||||
dtypes = ['float32', 'float64', 'int32', 'int64', 'complex64', 'complex128']
|
||||
integral_dtypes = ['int32', 'int64']
|
||||
|
||||
|
||||
def test_wrap_radians(xp):
|
||||
x = xp.asarray([-math.pi-1, -math.pi, -1, -1e-300,
|
||||
0, 1e-300, 1, math.pi, math.pi+1])
|
||||
ref = xp.asarray([math.pi-1, math.pi, -1, -1e-300,
|
||||
0, 1e-300, 1, math.pi, -math.pi+1])
|
||||
res = _wrap_radians(x, xp=xp)
|
||||
xp_assert_close(res, ref, atol=0)
|
||||
|
||||
|
||||
# numpy warning filters don't work for dask (dask/dask#3245)
|
||||
# (also we should not expect the numpy warning filter to work for any Array API
|
||||
# library)
|
||||
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning")
|
||||
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning")
|
||||
@pytest.mark.filterwarnings("ignore:overflow encountered:RuntimeWarning")
|
||||
@make_xp_test_case(logsumexp)
|
||||
class TestLogSumExp:
|
||||
def test_logsumexp(self, xp):
|
||||
# Test with zero-size array
|
||||
a = xp.asarray([])
|
||||
desired = xp.asarray(-xp.inf)
|
||||
xp_assert_equal(logsumexp(a), desired)
|
||||
|
||||
# Test whether logsumexp() function correctly handles large inputs.
|
||||
a = xp.arange(200., dtype=xp.float64)
|
||||
desired = xp.log(xp.sum(xp.exp(a)))
|
||||
xp_assert_close(logsumexp(a), desired)
|
||||
|
||||
# Now test with large numbers
|
||||
b = xp.asarray([1000., 1000.])
|
||||
desired = xp.asarray(1000.0 + math.log(2.0))
|
||||
xp_assert_close(logsumexp(b), desired)
|
||||
|
||||
n = 1000
|
||||
b = xp.full((n,), 10000)
|
||||
desired = xp.asarray(10000.0 + math.log(n))
|
||||
xp_assert_close(logsumexp(b), desired)
|
||||
|
||||
x = xp.asarray([1e-40] * 1000000)
|
||||
logx = xp.log(x)
|
||||
X = xp.stack([x, x])
|
||||
logX = xp.stack([logx, logx])
|
||||
xp_assert_close(xp.exp(logsumexp(logX)), xp.sum(X))
|
||||
xp_assert_close(xp.exp(logsumexp(logX, axis=0)), xp.sum(X, axis=0))
|
||||
xp_assert_close(xp.exp(logsumexp(logX, axis=1)), xp.sum(X, axis=1))
|
||||
|
||||
# Handling special values properly
|
||||
inf = xp.asarray([xp.inf])
|
||||
nan = xp.asarray([xp.nan])
|
||||
xp_assert_equal(logsumexp(inf), inf[0])
|
||||
xp_assert_equal(logsumexp(-inf), -inf[0])
|
||||
xp_assert_equal(logsumexp(nan), nan[0])
|
||||
xp_assert_equal(logsumexp(xp.asarray([-xp.inf, -xp.inf])), -inf[0])
|
||||
|
||||
# Handling an array with different magnitudes on the axes
|
||||
a = xp.asarray([[1e10, 1e-10],
|
||||
[-1e10, -np.inf]])
|
||||
ref = xp.asarray([1e10, -1e10])
|
||||
xp_assert_close(logsumexp(a, axis=-1), ref)
|
||||
|
||||
# Test keeping dimensions
|
||||
ref = xp.expand_dims(ref, axis=-1)
|
||||
xp_assert_close(logsumexp(a, axis=-1, keepdims=True), ref)
|
||||
|
||||
# Test multiple axes
|
||||
xp_assert_close(logsumexp(a, axis=(-1, -2)), xp.asarray(1e10))
|
||||
|
||||
def test_logsumexp_b(self, xp):
|
||||
a = xp.arange(200., dtype=xp.float64)
|
||||
b = xp.arange(200., 0., -1.)
|
||||
desired = xp.log(xp.sum(b*xp.exp(a)))
|
||||
xp_assert_close(logsumexp(a, b=b), desired)
|
||||
|
||||
a = xp.asarray([1000, 1000])
|
||||
b = xp.asarray([1.2, 1.2])
|
||||
desired = xp.asarray(1000 + math.log(2 * 1.2))
|
||||
xp_assert_close(logsumexp(a, b=b), desired)
|
||||
|
||||
x = xp.asarray([1e-40] * 100000)
|
||||
b = xp.linspace(1, 1000, 100000)
|
||||
logx = xp.log(x)
|
||||
X = xp.stack((x, x))
|
||||
logX = xp.stack((logx, logx))
|
||||
B = xp.stack((b, b))
|
||||
xp_assert_close(xp.exp(logsumexp(logX, b=B)), xp.sum(B * X))
|
||||
xp_assert_close(xp.exp(logsumexp(logX, b=B, axis=0)), xp.sum(B * X, axis=0))
|
||||
xp_assert_close(xp.exp(logsumexp(logX, b=B, axis=1)), xp.sum(B * X, axis=1))
|
||||
|
||||
def test_logsumexp_sign(self, xp):
|
||||
a = xp.asarray([1, 1, 1])
|
||||
b = xp.asarray([1, -1, -1])
|
||||
|
||||
r, s = logsumexp(a, b=b, return_sign=True)
|
||||
xp_assert_close(r, xp.asarray(1.))
|
||||
xp_assert_equal(s, xp.asarray(-1.))
|
||||
|
||||
def test_logsumexp_sign_zero(self, xp):
|
||||
a = xp.asarray([1, 1])
|
||||
b = xp.asarray([1, -1])
|
||||
|
||||
r, s = logsumexp(a, b=b, return_sign=True)
|
||||
assert not xp.isfinite(r)
|
||||
assert not xp.isnan(r)
|
||||
assert r < 0
|
||||
assert s == 0
|
||||
|
||||
def test_logsumexp_sign_shape(self, xp):
|
||||
a = xp.ones((1, 2, 3, 4))
|
||||
b = xp.ones_like(a)
|
||||
|
||||
r, s = logsumexp(a, axis=2, b=b, return_sign=True)
|
||||
assert r.shape == s.shape == (1, 2, 4)
|
||||
|
||||
r, s = logsumexp(a, axis=(1, 3), b=b, return_sign=True)
|
||||
assert r.shape == s.shape == (1,3)
|
||||
|
||||
def test_logsumexp_complex_sign(self, xp):
|
||||
a = xp.asarray([1 + 1j, 2 - 1j, -2 + 3j])
|
||||
|
||||
r, s = logsumexp(a, return_sign=True)
|
||||
|
||||
expected_sumexp = xp.sum(xp.exp(a))
|
||||
# This is the numpy>=2.0 convention for np.sign
|
||||
expected_sign = expected_sumexp / xp.abs(expected_sumexp)
|
||||
|
||||
xp_assert_close(s, expected_sign)
|
||||
xp_assert_close(s * xp.exp(r), expected_sumexp)
|
||||
|
||||
def test_logsumexp_shape(self, xp):
|
||||
a = xp.ones((1, 2, 3, 4))
|
||||
b = xp.ones_like(a)
|
||||
|
||||
r = logsumexp(a, axis=2, b=b)
|
||||
assert r.shape == (1, 2, 4)
|
||||
|
||||
r = logsumexp(a, axis=(1, 3), b=b)
|
||||
assert r.shape == (1, 3)
|
||||
|
||||
def test_logsumexp_b_zero(self, xp):
|
||||
a = xp.asarray([1, 10000])
|
||||
b = xp.asarray([1, 0])
|
||||
|
||||
xp_assert_close(logsumexp(a, b=b), xp.asarray(1.))
|
||||
|
||||
def test_logsumexp_b_shape(self, xp):
|
||||
a = xp.zeros((4, 1, 2, 1))
|
||||
b = xp.ones((3, 1, 5))
|
||||
|
||||
logsumexp(a, b=b)
|
||||
|
||||
@pytest.mark.parametrize('arg', (1, [1, 2, 3]))
|
||||
def test_xp_invalid_input(self, arg):
|
||||
assert logsumexp(arg) == logsumexp(np.asarray(np.atleast_1d(arg)))
|
||||
|
||||
def test_array_like(self):
|
||||
a = [1000, 1000]
|
||||
desired = np.asarray(1000.0 + math.log(2.0))
|
||||
xp_assert_close(logsumexp(a), desired)
|
||||
|
||||
@pytest.mark.parametrize('dtype', dtypes)
|
||||
def test_dtypes_a(self, dtype, xp):
|
||||
dtype = getattr(xp, dtype)
|
||||
a = xp.asarray([1000., 1000.], dtype=dtype)
|
||||
desired_dtype = (xp.asarray(1.).dtype if xp.isdtype(dtype, 'integral')
|
||||
else dtype) # true for all libraries tested
|
||||
desired = xp.asarray(1000.0 + math.log(2.0), dtype=desired_dtype)
|
||||
xp_assert_close(logsumexp(a), desired)
|
||||
|
||||
@pytest.mark.parametrize('dtype_a', dtypes)
|
||||
@pytest.mark.parametrize('dtype_b', dtypes)
|
||||
def test_dtypes_ab(self, dtype_a, dtype_b, xp):
|
||||
xp_dtype_a = getattr(xp, dtype_a)
|
||||
xp_dtype_b = getattr(xp, dtype_b)
|
||||
a = xp.asarray([2, 1], dtype=xp_dtype_a)
|
||||
b = xp.asarray([1, -1], dtype=xp_dtype_b)
|
||||
if is_array_api_strict(xp):
|
||||
# special-case for `TypeError: array_api_strict.float32 and
|
||||
# and array_api_strict.int64 cannot be type promoted together`
|
||||
xp_float_dtypes = [dtype for dtype in [xp_dtype_a, xp_dtype_b]
|
||||
if not xp.isdtype(dtype, 'integral')]
|
||||
if len(xp_float_dtypes) < 2: # at least one is integral
|
||||
xp_float_dtypes.append(xp.asarray(1.).dtype)
|
||||
desired_dtype = xp.result_type(*xp_float_dtypes)
|
||||
else:
|
||||
desired_dtype = xp.result_type(xp_dtype_a, xp_dtype_b)
|
||||
if xp.isdtype(desired_dtype, 'integral'):
|
||||
desired_dtype = xp_default_dtype(xp)
|
||||
desired = xp.asarray(math.log(math.exp(2) - math.exp(1)), dtype=desired_dtype)
|
||||
xp_assert_close(logsumexp(a, b=b), desired)
|
||||
|
||||
def test_gh18295(self, xp):
|
||||
# gh-18295 noted loss of precision when real part of one element is much
|
||||
# larger than the rest. Check that this is resolved.
|
||||
a = xp.asarray([0.0, -40.0])
|
||||
res = logsumexp(a)
|
||||
ref = xp.logaddexp(a[0], a[1])
|
||||
xp_assert_close(res, ref)
|
||||
|
||||
@pytest.mark.parametrize('dtype', ['complex64', 'complex128'])
|
||||
def test_gh21610(self, xp, dtype):
|
||||
# gh-21610 noted that `logsumexp` could return imaginary components
|
||||
# outside the range (-pi, pi]. Check that this is resolved.
|
||||
# While working on this, I noticed that all other tests passed even
|
||||
# when the imaginary component of the result was zero. This suggested
|
||||
# the need of a stronger test with imaginary dtype.
|
||||
rng = np.random.default_rng(324984329582349862)
|
||||
dtype = getattr(xp, dtype)
|
||||
shape = (10, 100)
|
||||
x = rng.uniform(1, 40, shape) + 1.j * rng.uniform(1, 40, shape)
|
||||
x = xp.asarray(x, dtype=dtype)
|
||||
|
||||
res = logsumexp(x, axis=1)
|
||||
ref = xp.log(xp.sum(xp.exp(x), axis=1))
|
||||
max = xp.full_like(xp.imag(res), xp.pi)
|
||||
xp_assert_less(xp.abs(xp.imag(res)), max)
|
||||
xp_assert_close(res, ref)
|
||||
|
||||
out, sgn = logsumexp(x, return_sign=True, axis=1)
|
||||
ref = xp.sum(xp.exp(x), axis=1)
|
||||
xp_assert_less(xp.abs(xp.imag(sgn)), max)
|
||||
xp_assert_close(out, xp.real(xp.log(ref)))
|
||||
xp_assert_close(sgn, ref/xp.abs(ref))
|
||||
|
||||
def test_gh21709_small_imaginary(self, xp):
|
||||
# Test that `logsumexp` does not lose relative precision of
|
||||
# small imaginary components
|
||||
x = xp.asarray([0, 0.+2.2204460492503132e-17j])
|
||||
res = logsumexp(x)
|
||||
# from mpmath import mp
|
||||
# mp.dps = 100
|
||||
# x, y = mp.mpc(0), mp.mpc('0', '2.2204460492503132e-17')
|
||||
# ref = complex(mp.log(mp.exp(x) + mp.exp(y)))
|
||||
ref = xp.asarray(0.6931471805599453+1.1102230246251566e-17j)
|
||||
xp_assert_close(xp.real(res), xp.real(ref))
|
||||
xp_assert_close(xp.imag(res), xp.imag(ref), atol=0, rtol=1e-15)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('x,y', it.product(
|
||||
[
|
||||
-np.inf,
|
||||
np.inf,
|
||||
complex(-np.inf, 0.),
|
||||
complex(-np.inf, -0.),
|
||||
complex(-np.inf, np.inf),
|
||||
complex(-np.inf, -np.inf),
|
||||
complex(np.inf, 0.),
|
||||
complex(np.inf, -0.),
|
||||
complex(np.inf, np.inf),
|
||||
complex(np.inf, -np.inf),
|
||||
# Phase in each quadrant.
|
||||
complex(-np.inf, 0.7533),
|
||||
complex(-np.inf, 2.3562),
|
||||
complex(-np.inf, 3.9270),
|
||||
complex(-np.inf, 5.4978),
|
||||
complex(np.inf, 0.7533),
|
||||
complex(np.inf, 2.3562),
|
||||
complex(np.inf, 3.9270),
|
||||
complex(np.inf, 5.4978),
|
||||
], repeat=2)
|
||||
)
|
||||
def test_gh22601_infinite_elements(self, x, y, xp):
|
||||
# Test that `logsumexp` does reasonable things in the presence of
|
||||
# real and complex infinities.
|
||||
res = logsumexp(xp.asarray([x, y]))
|
||||
ref = xp.log(xp.sum(xp.exp(xp.asarray([x, y]))))
|
||||
xp_assert_equal(res, ref)
|
||||
|
||||
def test_no_writeback(self, xp):
|
||||
"""Test that logsumexp doesn't accidentally write back to its parameters."""
|
||||
a = xp.asarray([5., 4.])
|
||||
b = xp.asarray([3., 2.])
|
||||
logsumexp(a)
|
||||
logsumexp(a, b=b)
|
||||
xp_assert_equal(a, xp.asarray([5., 4.]))
|
||||
xp_assert_equal(b, xp.asarray([3., 2.]))
|
||||
|
||||
@pytest.mark.parametrize("x_raw", [1.0, 1.0j, []])
|
||||
def test_device(self, x_raw, xp, devices):
|
||||
"""Test input device propagation to output."""
|
||||
for d in devices:
|
||||
x = xp.asarray(x_raw, device=d)
|
||||
assert xp_device(logsumexp(x)) == xp_device(x)
|
||||
assert xp_device(logsumexp(x, b=x)) == xp_device(x)
|
||||
|
||||
def test_gh22903(self, xp):
|
||||
# gh-22903 reported that `logsumexp` produced NaN where the weight associated
|
||||
# with the max magnitude element was negative and `return_sign=False`, even if
|
||||
# the net result should be the log of a positive number.
|
||||
|
||||
# result is log of positive number
|
||||
a = xp.asarray([3.06409428, 0.37251854, 3.87471931])
|
||||
b = xp.asarray([1.88190708, 2.84174795, -0.85016884])
|
||||
xp_assert_close(logsumexp(a, b=b), logsumexp(a, b=b, return_sign=True)[0])
|
||||
|
||||
# result is log of negative number
|
||||
b = xp.asarray([1.88190708, 2.84174795, -3.85016884])
|
||||
xp_assert_close(logsumexp(a, b=b), xp.asarray(xp.nan))
|
||||
|
||||
|
||||
@make_xp_test_case(softmax)
|
||||
class TestSoftmax:
|
||||
def test_softmax_fixtures(self, xp):
|
||||
xp_assert_close(softmax(xp.asarray([1000., 0., 0., 0.])),
|
||||
xp.asarray([1., 0., 0., 0.]), rtol=1e-13)
|
||||
xp_assert_close(softmax(xp.asarray([1., 1.])),
|
||||
xp.asarray([.5, .5]), rtol=1e-13)
|
||||
xp_assert_close(softmax(xp.asarray([0., 1.])),
|
||||
xp.asarray([1., np.e])/(1 + np.e),
|
||||
rtol=1e-13)
|
||||
|
||||
# Expected value computed using mpmath (with mpmath.mp.dps = 200) and then
|
||||
# converted to float.
|
||||
x = xp.arange(4, dtype=xp.float64)
|
||||
expected = xp.asarray([0.03205860328008499,
|
||||
0.08714431874203256,
|
||||
0.23688281808991013,
|
||||
0.6439142598879722], dtype=xp.float64)
|
||||
|
||||
xp_assert_close(softmax(x), expected, rtol=1e-13)
|
||||
|
||||
# Translation property. If all the values are changed by the same amount,
|
||||
# the softmax result does not change.
|
||||
xp_assert_close(softmax(x + 100), expected, rtol=1e-13)
|
||||
|
||||
# When axis=None, softmax operates on the entire array, and preserves
|
||||
# the shape.
|
||||
xp_assert_close(softmax(xp.reshape(x, (2, 2))),
|
||||
xp.reshape(expected, (2, 2)), rtol=1e-13)
|
||||
|
||||
def test_softmax_multi_axes(self, xp):
|
||||
xp_assert_close(softmax(xp.asarray([[1000., 0.], [1000., 0.]]), axis=0),
|
||||
xp.asarray([[.5, .5], [.5, .5]]), rtol=1e-13)
|
||||
xp_assert_close(softmax(xp.asarray([[1000., 0.], [1000., 0.]]), axis=1),
|
||||
xp.asarray([[1., 0.], [1., 0.]]), rtol=1e-13)
|
||||
|
||||
# Expected value computed using mpmath (with mpmath.mp.dps = 200) and then
|
||||
# converted to float.
|
||||
x = xp.asarray([[-25., 0., 25., 50.],
|
||||
[ 1., 325., 749., 750.]])
|
||||
expected = xp.asarray([[2.678636961770877e-33,
|
||||
1.9287498479371314e-22,
|
||||
1.3887943864771144e-11,
|
||||
0.999999999986112],
|
||||
[0.0,
|
||||
1.9444526359919372e-185,
|
||||
0.2689414213699951,
|
||||
0.7310585786300048]])
|
||||
xp_assert_close(softmax(x, axis=1), expected, rtol=1e-13)
|
||||
xp_assert_close(softmax(x.T, axis=0), expected.T, rtol=1e-13)
|
||||
|
||||
# 3-d input, with a tuple for the axis.
|
||||
x3d = xp.reshape(x, (2, 2, 2))
|
||||
xp_assert_close(softmax(x3d, axis=(1, 2)),
|
||||
xp.reshape(expected, (2, 2, 2)), rtol=1e-13)
|
||||
|
||||
@pytest.mark.xfail_xp_backends("array_api_strict", reason="int->float promotion")
|
||||
def test_softmax_int_array(self, xp):
|
||||
xp_assert_close(softmax(xp.asarray([1000, 0, 0, 0])),
|
||||
xp.asarray([1., 0., 0., 0.]), rtol=1e-13)
|
||||
|
||||
def test_softmax_scalar(self):
|
||||
xp_assert_close(softmax(1000), np.asarray(1.), rtol=1e-13)
|
||||
|
||||
def test_softmax_array_like(self):
|
||||
xp_assert_close(softmax([1000, 0, 0, 0]),
|
||||
np.asarray([1., 0., 0., 0.]), rtol=1e-13)
|
||||
|
||||
|
||||
@make_xp_test_case(log_softmax)
|
||||
class TestLogSoftmax:
|
||||
def test_log_softmax_basic(self, xp):
|
||||
xp_assert_close(log_softmax(xp.asarray([1000., 1.])),
|
||||
xp.asarray([0., -999.]), rtol=1e-13)
|
||||
|
||||
@pytest.mark.xfail_xp_backends("array_api_strict", reason="int->float promotion")
|
||||
def test_log_softmax_int_array(self, xp):
|
||||
xp_assert_close(log_softmax(xp.asarray([1000, 1])),
|
||||
xp.asarray([0., -999.]), rtol=1e-13)
|
||||
|
||||
def test_log_softmax_scalar(self):
|
||||
xp_assert_close(log_softmax(1.0), 0.0, rtol=1e-13)
|
||||
|
||||
def test_log_softmax_array_like(self):
|
||||
xp_assert_close(log_softmax([1000, 1]),
|
||||
np.asarray([0., -999.]), rtol=1e-13)
|
||||
|
||||
@staticmethod
|
||||
def data_1d(xp):
|
||||
x = xp.arange(4, dtype=xp.float64)
|
||||
# Expected value computed using mpmath (with mpmath.mp.dps = 200)
|
||||
expect = [-3.4401896985611953,
|
||||
-2.4401896985611953,
|
||||
-1.4401896985611953,
|
||||
-0.44018969856119533]
|
||||
return x, xp.asarray(expect, dtype=xp.float64)
|
||||
|
||||
@staticmethod
|
||||
def data_2d(xp):
|
||||
x = xp.reshape(xp.arange(8, dtype=xp.float64), (2, 4))
|
||||
|
||||
# Expected value computed using mpmath (with mpmath.mp.dps = 200)
|
||||
expect = [[-3.4401896985611953,
|
||||
-2.4401896985611953,
|
||||
-1.4401896985611953,
|
||||
-0.44018969856119533],
|
||||
[-3.4401896985611953,
|
||||
-2.4401896985611953,
|
||||
-1.4401896985611953,
|
||||
-0.44018969856119533]]
|
||||
return x, xp.asarray(expect, dtype=xp.float64)
|
||||
|
||||
@pytest.mark.parametrize("offset", [0, 100])
|
||||
def test_log_softmax_translation(self, offset, xp):
|
||||
# Translation property. If all the values are changed by the same amount,
|
||||
# the softmax result does not change.
|
||||
x, expect = self.data_1d(xp)
|
||||
x += offset
|
||||
xp_assert_close(log_softmax(x), expect, rtol=1e-13)
|
||||
|
||||
def test_log_softmax_noneaxis(self, xp):
|
||||
# When axis=None, softmax operates on the entire array, and preserves
|
||||
# the shape.
|
||||
x, expect = self.data_1d(xp)
|
||||
x = xp.reshape(x, (2, 2))
|
||||
expect = xp.reshape(expect, (2, 2))
|
||||
xp_assert_close(log_softmax(x), expect, rtol=1e-13)
|
||||
|
||||
@pytest.mark.parametrize('axis_2d, expected_2d', [
|
||||
(0, np.log(0.5) * np.ones((2, 2))),
|
||||
(1, [[0., -999.], [0., -999.]]),
|
||||
])
|
||||
def test_axes(self, axis_2d, expected_2d, xp):
|
||||
x = xp.asarray([[1000., 1.], [1000., 1.]])
|
||||
xp_assert_close(log_softmax(x, axis=axis_2d),
|
||||
xp.asarray(expected_2d, dtype=x.dtype), rtol=1e-13)
|
||||
|
||||
def test_log_softmax_2d_axis1(self, xp):
|
||||
x, expect = self.data_2d(xp)
|
||||
xp_assert_close(log_softmax(x, axis=1), expect, rtol=1e-13)
|
||||
|
||||
def test_log_softmax_2d_axis0(self, xp):
|
||||
x, expect = self.data_2d(xp)
|
||||
xp_assert_close(log_softmax(x.T, axis=0), expect.T, rtol=1e-13)
|
||||
|
||||
def test_log_softmax_3d(self, xp):
|
||||
# 3D input, with a tuple for the axis.
|
||||
x, expect = self.data_2d(xp)
|
||||
x = xp.reshape(x, (2, 2, 2))
|
||||
expect = xp.reshape(expect, (2, 2, 2))
|
||||
xp_assert_close(log_softmax(x, axis=(1, 2)), expect, rtol=1e-13)
|
||||
2293
venv/lib/python3.13/site-packages/scipy/special/tests/test_mpmath.py
Normal file
2293
venv/lib/python3.13/site-packages/scipy/special/tests/test_mpmath.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,65 @@
|
|||
"""Test how the ufuncs in special handle nan inputs.
|
||||
|
||||
"""
|
||||
from collections.abc import Callable
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal, assert_, suppress_warnings
|
||||
import pytest
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
KNOWNFAILURES: dict[str, Callable] = {}
|
||||
|
||||
POSTPROCESSING: dict[str, Callable] = {}
|
||||
|
||||
|
||||
def _get_ufuncs():
|
||||
ufuncs = []
|
||||
ufunc_names = []
|
||||
for name in sorted(sc.__dict__):
|
||||
obj = sc.__dict__[name]
|
||||
if not isinstance(obj, np.ufunc):
|
||||
continue
|
||||
msg = KNOWNFAILURES.get(obj)
|
||||
if msg is None:
|
||||
ufuncs.append(obj)
|
||||
ufunc_names.append(name)
|
||||
else:
|
||||
fail = pytest.mark.xfail(run=False, reason=msg)
|
||||
ufuncs.append(pytest.param(obj, marks=fail))
|
||||
ufunc_names.append(name)
|
||||
return ufuncs, ufunc_names
|
||||
|
||||
|
||||
UFUNCS, UFUNC_NAMES = _get_ufuncs()
|
||||
|
||||
|
||||
@pytest.mark.thread_unsafe
|
||||
@pytest.mark.parametrize("func", UFUNCS, ids=UFUNC_NAMES)
|
||||
def test_nan_inputs(func):
|
||||
args = (np.nan,)*func.nin
|
||||
with suppress_warnings() as sup:
|
||||
# Ignore warnings about unsafe casts from legacy wrappers
|
||||
sup.filter(RuntimeWarning,
|
||||
"floating point number truncated to an integer")
|
||||
try:
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(DeprecationWarning)
|
||||
res = func(*args)
|
||||
except TypeError:
|
||||
# One of the arguments doesn't take real inputs
|
||||
return
|
||||
if func in POSTPROCESSING:
|
||||
res = POSTPROCESSING[func](*res)
|
||||
|
||||
msg = f"got {res} instead of nan"
|
||||
assert_array_equal(np.isnan(res), True, err_msg=msg)
|
||||
|
||||
|
||||
def test_legacy_cast():
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(RuntimeWarning,
|
||||
"floating point number truncated to an integer")
|
||||
res = sc.bdtrc(np.nan, 1, 0.5)
|
||||
assert_(np.isnan(res))
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_allclose
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
def test_ndtr():
|
||||
assert_equal(sc.ndtr(0), 0.5)
|
||||
assert_allclose(sc.ndtr(1), 0.8413447460685429)
|
||||
|
||||
|
||||
class TestNdtri:
|
||||
|
||||
def test_zero(self):
|
||||
assert sc.ndtri(0.5) == 0.0
|
||||
|
||||
def test_asymptotes(self):
|
||||
assert_equal(sc.ndtri([0.0, 1.0]), [-np.inf, np.inf])
|
||||
|
||||
def test_outside_of_domain(self):
|
||||
assert all(np.isnan(sc.ndtri([-1.5, 1.5])))
|
||||
|
||||
|
||||
class TestLogNdtr:
|
||||
|
||||
# The expected values in these tests were computed with mpmath:
|
||||
#
|
||||
# def log_ndtr_mp(x):
|
||||
# return mpmath.log(mpmath.ncdf(x))
|
||||
#
|
||||
|
||||
def test_log_ndtr_moderate_le8(self):
|
||||
x = np.array([-0.75, -0.25, 0, 0.5, 1.5, 2.5, 3, 4, 5, 7, 8])
|
||||
expected = np.array([-1.4844482299196562,
|
||||
-0.9130617648111351,
|
||||
-0.6931471805599453,
|
||||
-0.3689464152886564,
|
||||
-0.06914345561223398,
|
||||
-0.006229025485860002,
|
||||
-0.0013508099647481938,
|
||||
-3.167174337748927e-05,
|
||||
-2.866516129637636e-07,
|
||||
-1.279812543886654e-12,
|
||||
-6.220960574271786e-16])
|
||||
y = sc.log_ndtr(x)
|
||||
assert_allclose(y, expected, rtol=1e-14)
|
||||
|
||||
def test_log_ndtr_values_8_16(self):
|
||||
x = np.array([8.001, 8.06, 8.15, 8.5, 10, 12, 14, 16])
|
||||
expected = [-6.170639424817055e-16,
|
||||
-3.814722443652823e-16,
|
||||
-1.819621363526629e-16,
|
||||
-9.479534822203318e-18,
|
||||
-7.619853024160525e-24,
|
||||
-1.776482112077679e-33,
|
||||
-7.7935368191928e-45,
|
||||
-6.388754400538087e-58]
|
||||
y = sc.log_ndtr(x)
|
||||
assert_allclose(y, expected, rtol=5e-14)
|
||||
|
||||
def test_log_ndtr_values_16_31(self):
|
||||
x = np.array([16.15, 20.3, 21.4, 26.2, 30.9])
|
||||
expected = [-5.678084565148492e-59,
|
||||
-6.429244467698346e-92,
|
||||
-6.680402412553295e-102,
|
||||
-1.328698078458869e-151,
|
||||
-5.972288641838264e-210]
|
||||
y = sc.log_ndtr(x)
|
||||
assert_allclose(y, expected, rtol=2e-13)
|
||||
|
||||
def test_log_ndtr_values_gt31(self):
|
||||
x = np.array([31.6, 32.8, 34.9, 37.1])
|
||||
expected = [-1.846036234858162e-219,
|
||||
-2.9440539964066835e-236,
|
||||
-3.71721649450857e-267,
|
||||
-1.4047119663106221e-301]
|
||||
y = sc.log_ndtr(x)
|
||||
assert_allclose(y, expected, rtol=3e-13)
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_allclose
|
||||
from scipy.special import log_ndtr, ndtri_exp
|
||||
from scipy.special._testutils import assert_func_equal
|
||||
|
||||
|
||||
def log_ndtr_ndtri_exp(y):
|
||||
return log_ndtr(ndtri_exp(y))
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def uniform_random_points():
|
||||
random_state = np.random.RandomState(1234)
|
||||
points = random_state.random_sample(1000)
|
||||
return points
|
||||
|
||||
|
||||
class TestNdtriExp:
|
||||
"""Tests that ndtri_exp is sufficiently close to an inverse of log_ndtr.
|
||||
|
||||
We have separate tests for the five intervals (-inf, -10),
|
||||
[-10, -2), [-2, -0.14542), [-0.14542, -1e-6), and [-1e-6, 0).
|
||||
ndtri_exp(y) is computed in three different ways depending on if y
|
||||
is in (-inf, -2), [-2, log(1 - exp(-2))], or [log(1 - exp(-2), 0).
|
||||
Each of these intervals is given its own test with two additional tests
|
||||
for handling very small values and values very close to zero.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input", [-1e1, -1e2, -1e10, -1e20, -np.finfo(float).max]
|
||||
)
|
||||
def test_very_small_arg(self, test_input, uniform_random_points):
|
||||
scale = test_input
|
||||
points = scale * (0.5 * uniform_random_points + 0.5)
|
||||
assert_func_equal(
|
||||
log_ndtr_ndtri_exp,
|
||||
lambda y: y, points,
|
||||
rtol=1e-14,
|
||||
nan_ok=True
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"interval,expected_rtol",
|
||||
[
|
||||
((-10, -2), 1e-14),
|
||||
((-2, -0.14542), 1e-12),
|
||||
((-0.14542, -1e-6), 1e-10),
|
||||
((-1e-6, 0), 1e-6),
|
||||
],
|
||||
)
|
||||
def test_in_interval(self, interval, expected_rtol, uniform_random_points):
|
||||
left, right = interval
|
||||
points = (right - left) * uniform_random_points + left
|
||||
assert_func_equal(
|
||||
log_ndtr_ndtri_exp,
|
||||
lambda y: y, points,
|
||||
rtol=expected_rtol,
|
||||
nan_ok=True
|
||||
)
|
||||
|
||||
def test_extreme(self):
|
||||
# bigneg is not quite the largest negative double precision value.
|
||||
# Here's why:
|
||||
# The round-trip calculation
|
||||
# y = ndtri_exp(bigneg)
|
||||
# bigneg2 = log_ndtr(y)
|
||||
# where bigneg is a very large negative value, would--with infinite
|
||||
# precision--result in bigneg2 == bigneg. When bigneg is large enough,
|
||||
# y is effectively equal to -sqrt(2)*sqrt(-bigneg), and log_ndtr(y) is
|
||||
# effectively -(y/sqrt(2))**2. If we use bigneg = np.finfo(float).min,
|
||||
# then by construction, the theoretical value is the most negative
|
||||
# finite value that can be represented with 64 bit float point. This
|
||||
# means tiny changes in how the computation proceeds can result in the
|
||||
# return value being -inf. (E.g. changing the constant representation
|
||||
# of 1/sqrt(2) from 0.7071067811865475--which is the value returned by
|
||||
# 1/np.sqrt(2)--to 0.7071067811865476--which is the most accurate 64
|
||||
# bit floating point representation of 1/sqrt(2)--results in the
|
||||
# round-trip that starts with np.finfo(float).min returning -inf. So
|
||||
# we'll move the bigneg value a few ULPs towards 0 to avoid this
|
||||
# sensitivity.
|
||||
# Use the reduce method to apply nextafter four times.
|
||||
bigneg = np.nextafter.reduce([np.finfo(float).min, 0, 0, 0, 0])
|
||||
# tinyneg is approx. -2.225e-308.
|
||||
tinyneg = -np.finfo(float).tiny
|
||||
x = np.array([tinyneg, bigneg])
|
||||
result = log_ndtr_ndtri_exp(x)
|
||||
assert_allclose(result, x, rtol=1e-12)
|
||||
|
||||
def test_asymptotes(self):
|
||||
assert_equal(ndtri_exp([-np.inf, 0.0]), [-np.inf, np.inf])
|
||||
|
||||
def test_outside_domain(self):
|
||||
assert np.isnan(ndtri_exp(1.0))
|
||||
|
|
@ -0,0 +1,821 @@
|
|||
import pytest
|
||||
from pytest import raises as assert_raises
|
||||
|
||||
import numpy as np
|
||||
from numpy import array, sqrt
|
||||
from numpy.testing import (assert_array_almost_equal, assert_equal,
|
||||
assert_almost_equal, assert_allclose)
|
||||
|
||||
from scipy import integrate
|
||||
import scipy.special as sc
|
||||
from scipy.special import gamma
|
||||
import scipy.special._orthogonal as orth
|
||||
|
||||
|
||||
class TestCheby:
|
||||
def test_chebyc(self):
|
||||
C0 = orth.chebyc(0)
|
||||
C1 = orth.chebyc(1)
|
||||
with np.errstate(all='ignore'):
|
||||
C2 = orth.chebyc(2)
|
||||
C3 = orth.chebyc(3)
|
||||
C4 = orth.chebyc(4)
|
||||
C5 = orth.chebyc(5)
|
||||
|
||||
assert_array_almost_equal(C0.c,[2],13)
|
||||
assert_array_almost_equal(C1.c,[1,0],13)
|
||||
assert_array_almost_equal(C2.c,[1,0,-2],13)
|
||||
assert_array_almost_equal(C3.c,[1,0,-3,0],13)
|
||||
assert_array_almost_equal(C4.c,[1,0,-4,0,2],13)
|
||||
assert_array_almost_equal(C5.c,[1,0,-5,0,5,0],13)
|
||||
|
||||
def test_chebys(self):
|
||||
S0 = orth.chebys(0)
|
||||
S1 = orth.chebys(1)
|
||||
S2 = orth.chebys(2)
|
||||
S3 = orth.chebys(3)
|
||||
S4 = orth.chebys(4)
|
||||
S5 = orth.chebys(5)
|
||||
assert_array_almost_equal(S0.c,[1],13)
|
||||
assert_array_almost_equal(S1.c,[1,0],13)
|
||||
assert_array_almost_equal(S2.c,[1,0,-1],13)
|
||||
assert_array_almost_equal(S3.c,[1,0,-2,0],13)
|
||||
assert_array_almost_equal(S4.c,[1,0,-3,0,1],13)
|
||||
assert_array_almost_equal(S5.c,[1,0,-4,0,3,0],13)
|
||||
|
||||
def test_chebyt(self):
|
||||
T0 = orth.chebyt(0)
|
||||
T1 = orth.chebyt(1)
|
||||
T2 = orth.chebyt(2)
|
||||
T3 = orth.chebyt(3)
|
||||
T4 = orth.chebyt(4)
|
||||
T5 = orth.chebyt(5)
|
||||
assert_array_almost_equal(T0.c,[1],13)
|
||||
assert_array_almost_equal(T1.c,[1,0],13)
|
||||
assert_array_almost_equal(T2.c,[2,0,-1],13)
|
||||
assert_array_almost_equal(T3.c,[4,0,-3,0],13)
|
||||
assert_array_almost_equal(T4.c,[8,0,-8,0,1],13)
|
||||
assert_array_almost_equal(T5.c,[16,0,-20,0,5,0],13)
|
||||
|
||||
def test_chebyu(self):
|
||||
U0 = orth.chebyu(0)
|
||||
U1 = orth.chebyu(1)
|
||||
U2 = orth.chebyu(2)
|
||||
U3 = orth.chebyu(3)
|
||||
U4 = orth.chebyu(4)
|
||||
U5 = orth.chebyu(5)
|
||||
assert_array_almost_equal(U0.c,[1],13)
|
||||
assert_array_almost_equal(U1.c,[2,0],13)
|
||||
assert_array_almost_equal(U2.c,[4,0,-1],13)
|
||||
assert_array_almost_equal(U3.c,[8,0,-4,0],13)
|
||||
assert_array_almost_equal(U4.c,[16,0,-12,0,1],13)
|
||||
assert_array_almost_equal(U5.c,[32,0,-32,0,6,0],13)
|
||||
|
||||
|
||||
class TestGegenbauer:
|
||||
|
||||
def test_gegenbauer(self):
|
||||
a = 5*np.random.random() - 0.5
|
||||
if np.any(a == 0):
|
||||
a = -0.2
|
||||
Ca0 = orth.gegenbauer(0,a)
|
||||
Ca1 = orth.gegenbauer(1,a)
|
||||
Ca2 = orth.gegenbauer(2,a)
|
||||
Ca3 = orth.gegenbauer(3,a)
|
||||
Ca4 = orth.gegenbauer(4,a)
|
||||
Ca5 = orth.gegenbauer(5,a)
|
||||
|
||||
assert_array_almost_equal(Ca0.c,array([1]),13)
|
||||
assert_array_almost_equal(Ca1.c,array([2*a,0]),13)
|
||||
assert_array_almost_equal(Ca2.c,array([2*a*(a+1),0,-a]),13)
|
||||
assert_array_almost_equal(Ca3.c,array([4*sc.poch(a,3),0,-6*a*(a+1),
|
||||
0])/3.0,11)
|
||||
assert_array_almost_equal(Ca4.c,array([4*sc.poch(a,4),0,-12*sc.poch(a,3),
|
||||
0,3*a*(a+1)])/6.0,11)
|
||||
assert_array_almost_equal(Ca5.c,array([4*sc.poch(a,5),0,-20*sc.poch(a,4),
|
||||
0,15*sc.poch(a,3),0])/15.0,11)
|
||||
|
||||
@pytest.mark.parametrize('a', [0, 1])
|
||||
def test_n_zero_gh8888(self, a):
|
||||
# gh-8888 reported that gegenbauer(0, 0) returns NaN polynomial
|
||||
Cn0 = orth.gegenbauer(0, a)
|
||||
assert_equal(Cn0.c, np.asarray([1.]))
|
||||
|
||||
def test_valid_alpha(self):
|
||||
# Check input validation of `alpha`
|
||||
message = '`alpha` must be a finite number greater...'
|
||||
with pytest.raises(ValueError, match=message):
|
||||
orth.gegenbauer(0, np.nan)
|
||||
with pytest.raises(ValueError, match=message):
|
||||
orth.gegenbauer(1, -0.5)
|
||||
with pytest.raises(ValueError, match=message):
|
||||
orth.gegenbauer(2, -np.inf)
|
||||
|
||||
|
||||
class TestHermite:
|
||||
def test_hermite(self):
|
||||
H0 = orth.hermite(0)
|
||||
H1 = orth.hermite(1)
|
||||
H2 = orth.hermite(2)
|
||||
H3 = orth.hermite(3)
|
||||
H4 = orth.hermite(4)
|
||||
H5 = orth.hermite(5)
|
||||
assert_array_almost_equal(H0.c,[1],13)
|
||||
assert_array_almost_equal(H1.c,[2,0],13)
|
||||
assert_array_almost_equal(H2.c,[4,0,-2],13)
|
||||
assert_array_almost_equal(H3.c,[8,0,-12,0],13)
|
||||
assert_array_almost_equal(H4.c,[16,0,-48,0,12],12)
|
||||
assert_array_almost_equal(H5.c,[32,0,-160,0,120,0],12)
|
||||
|
||||
def test_hermitenorm(self):
|
||||
# He_n(x) = 2**(-n/2) H_n(x/sqrt(2))
|
||||
psub = np.poly1d([1.0/sqrt(2),0])
|
||||
H0 = orth.hermitenorm(0)
|
||||
H1 = orth.hermitenorm(1)
|
||||
H2 = orth.hermitenorm(2)
|
||||
H3 = orth.hermitenorm(3)
|
||||
H4 = orth.hermitenorm(4)
|
||||
H5 = orth.hermitenorm(5)
|
||||
he0 = orth.hermite(0)(psub)
|
||||
he1 = orth.hermite(1)(psub) / sqrt(2)
|
||||
he2 = orth.hermite(2)(psub) / 2.0
|
||||
he3 = orth.hermite(3)(psub) / (2*sqrt(2))
|
||||
he4 = orth.hermite(4)(psub) / 4.0
|
||||
he5 = orth.hermite(5)(psub) / (4.0*sqrt(2))
|
||||
|
||||
assert_array_almost_equal(H0.c,he0.c,13)
|
||||
assert_array_almost_equal(H1.c,he1.c,13)
|
||||
assert_array_almost_equal(H2.c,he2.c,13)
|
||||
assert_array_almost_equal(H3.c,he3.c,13)
|
||||
assert_array_almost_equal(H4.c,he4.c,13)
|
||||
assert_array_almost_equal(H5.c,he5.c,13)
|
||||
|
||||
|
||||
class TestShLegendre:
|
||||
def test_sh_legendre(self):
|
||||
# P*_n(x) = P_n(2x-1)
|
||||
psub = np.poly1d([2,-1])
|
||||
Ps0 = orth.sh_legendre(0)
|
||||
Ps1 = orth.sh_legendre(1)
|
||||
Ps2 = orth.sh_legendre(2)
|
||||
Ps3 = orth.sh_legendre(3)
|
||||
Ps4 = orth.sh_legendre(4)
|
||||
Ps5 = orth.sh_legendre(5)
|
||||
pse0 = orth.legendre(0)(psub)
|
||||
pse1 = orth.legendre(1)(psub)
|
||||
pse2 = orth.legendre(2)(psub)
|
||||
pse3 = orth.legendre(3)(psub)
|
||||
pse4 = orth.legendre(4)(psub)
|
||||
pse5 = orth.legendre(5)(psub)
|
||||
assert_array_almost_equal(Ps0.c,pse0.c,13)
|
||||
assert_array_almost_equal(Ps1.c,pse1.c,13)
|
||||
assert_array_almost_equal(Ps2.c,pse2.c,13)
|
||||
assert_array_almost_equal(Ps3.c,pse3.c,13)
|
||||
assert_array_almost_equal(Ps4.c,pse4.c,12)
|
||||
assert_array_almost_equal(Ps5.c,pse5.c,12)
|
||||
|
||||
|
||||
class TestShChebyt:
|
||||
def test_sh_chebyt(self):
|
||||
# T*_n(x) = T_n(2x-1)
|
||||
psub = np.poly1d([2,-1])
|
||||
Ts0 = orth.sh_chebyt(0)
|
||||
Ts1 = orth.sh_chebyt(1)
|
||||
Ts2 = orth.sh_chebyt(2)
|
||||
Ts3 = orth.sh_chebyt(3)
|
||||
Ts4 = orth.sh_chebyt(4)
|
||||
Ts5 = orth.sh_chebyt(5)
|
||||
tse0 = orth.chebyt(0)(psub)
|
||||
tse1 = orth.chebyt(1)(psub)
|
||||
tse2 = orth.chebyt(2)(psub)
|
||||
tse3 = orth.chebyt(3)(psub)
|
||||
tse4 = orth.chebyt(4)(psub)
|
||||
tse5 = orth.chebyt(5)(psub)
|
||||
assert_array_almost_equal(Ts0.c,tse0.c,13)
|
||||
assert_array_almost_equal(Ts1.c,tse1.c,13)
|
||||
assert_array_almost_equal(Ts2.c,tse2.c,13)
|
||||
assert_array_almost_equal(Ts3.c,tse3.c,13)
|
||||
assert_array_almost_equal(Ts4.c,tse4.c,12)
|
||||
assert_array_almost_equal(Ts5.c,tse5.c,12)
|
||||
|
||||
|
||||
class TestShChebyu:
|
||||
def test_sh_chebyu(self):
|
||||
# U*_n(x) = U_n(2x-1)
|
||||
psub = np.poly1d([2,-1])
|
||||
Us0 = orth.sh_chebyu(0)
|
||||
Us1 = orth.sh_chebyu(1)
|
||||
Us2 = orth.sh_chebyu(2)
|
||||
Us3 = orth.sh_chebyu(3)
|
||||
Us4 = orth.sh_chebyu(4)
|
||||
Us5 = orth.sh_chebyu(5)
|
||||
use0 = orth.chebyu(0)(psub)
|
||||
use1 = orth.chebyu(1)(psub)
|
||||
use2 = orth.chebyu(2)(psub)
|
||||
use3 = orth.chebyu(3)(psub)
|
||||
use4 = orth.chebyu(4)(psub)
|
||||
use5 = orth.chebyu(5)(psub)
|
||||
assert_array_almost_equal(Us0.c,use0.c,13)
|
||||
assert_array_almost_equal(Us1.c,use1.c,13)
|
||||
assert_array_almost_equal(Us2.c,use2.c,13)
|
||||
assert_array_almost_equal(Us3.c,use3.c,13)
|
||||
assert_array_almost_equal(Us4.c,use4.c,12)
|
||||
assert_array_almost_equal(Us5.c,use5.c,11)
|
||||
|
||||
|
||||
class TestShJacobi:
|
||||
def test_sh_jacobi(self):
|
||||
# G^(p,q)_n(x) = n! gamma(n+p)/gamma(2*n+p) * P^(p-q,q-1)_n(2*x-1)
|
||||
def conv(n, p):
|
||||
return gamma(n + 1) * gamma(n + p) / gamma(2 * n + p)
|
||||
psub = np.poly1d([2,-1])
|
||||
q = 4 * np.random.random()
|
||||
p = q-1 + 2*np.random.random()
|
||||
# print("shifted jacobi p,q = ", p, q)
|
||||
G0 = orth.sh_jacobi(0,p,q)
|
||||
G1 = orth.sh_jacobi(1,p,q)
|
||||
G2 = orth.sh_jacobi(2,p,q)
|
||||
G3 = orth.sh_jacobi(3,p,q)
|
||||
G4 = orth.sh_jacobi(4,p,q)
|
||||
G5 = orth.sh_jacobi(5,p,q)
|
||||
ge0 = orth.jacobi(0,p-q,q-1)(psub) * conv(0,p)
|
||||
ge1 = orth.jacobi(1,p-q,q-1)(psub) * conv(1,p)
|
||||
ge2 = orth.jacobi(2,p-q,q-1)(psub) * conv(2,p)
|
||||
ge3 = orth.jacobi(3,p-q,q-1)(psub) * conv(3,p)
|
||||
ge4 = orth.jacobi(4,p-q,q-1)(psub) * conv(4,p)
|
||||
ge5 = orth.jacobi(5,p-q,q-1)(psub) * conv(5,p)
|
||||
|
||||
assert_array_almost_equal(G0.c,ge0.c,13)
|
||||
assert_array_almost_equal(G1.c,ge1.c,13)
|
||||
assert_array_almost_equal(G2.c,ge2.c,13)
|
||||
assert_array_almost_equal(G3.c,ge3.c,13)
|
||||
assert_array_almost_equal(G4.c,ge4.c,13)
|
||||
assert_array_almost_equal(G5.c,ge5.c,13)
|
||||
|
||||
|
||||
class TestCall:
|
||||
def test_call(self):
|
||||
poly = []
|
||||
for n in range(5):
|
||||
poly.extend([x.strip() for x in
|
||||
(f"""
|
||||
orth.jacobi({n},0.3,0.9)
|
||||
orth.sh_jacobi({n},0.3,0.9)
|
||||
orth.genlaguerre({n},0.3)
|
||||
orth.laguerre({n})
|
||||
orth.hermite({n})
|
||||
orth.hermitenorm({n})
|
||||
orth.gegenbauer({n},0.3)
|
||||
orth.chebyt({n})
|
||||
orth.chebyu({n})
|
||||
orth.chebyc({n})
|
||||
orth.chebys({n})
|
||||
orth.sh_chebyt({n})
|
||||
orth.sh_chebyu({n})
|
||||
orth.legendre({n})
|
||||
orth.sh_legendre({n})
|
||||
""").split()])
|
||||
with np.errstate(all='ignore'):
|
||||
for pstr in poly:
|
||||
p = eval(pstr)
|
||||
assert_almost_equal(p(0.315), np.poly1d(p.coef)(0.315),
|
||||
err_msg=pstr)
|
||||
|
||||
|
||||
class TestGenlaguerre:
|
||||
def test_regression(self):
|
||||
assert_equal(orth.genlaguerre(1, 1, monic=False)(0), 2.)
|
||||
assert_equal(orth.genlaguerre(1, 1, monic=True)(0), -2.)
|
||||
assert_equal(orth.genlaguerre(1, 1, monic=False), np.poly1d([-1, 2]))
|
||||
assert_equal(orth.genlaguerre(1, 1, monic=True), np.poly1d([1, -2]))
|
||||
|
||||
|
||||
def verify_gauss_quad(root_func, eval_func, weight_func, a, b, N,
|
||||
rtol=1e-15, atol=5e-14):
|
||||
# this test is copied from numpy's TestGauss in test_hermite.py
|
||||
x, w, mu = root_func(N, True)
|
||||
|
||||
n = np.arange(N, dtype=np.dtype("long"))
|
||||
v = eval_func(n[:,np.newaxis], x)
|
||||
vv = np.dot(v*w, v.T)
|
||||
vd = 1 / np.sqrt(vv.diagonal())
|
||||
vv = vd[:, np.newaxis] * vv * vd
|
||||
assert_allclose(vv, np.eye(N), rtol, atol)
|
||||
|
||||
# check that the integral of 1 is correct
|
||||
assert_allclose(w.sum(), mu, rtol, atol)
|
||||
|
||||
# compare the results of integrating a function with quad.
|
||||
def f(x):
|
||||
return x ** 3 - 3 * x ** 2 + x - 2
|
||||
resI = integrate.quad(lambda x: f(x)*weight_func(x), a, b)
|
||||
resG = np.vdot(f(x), w)
|
||||
rtol = 1e-6 if 1e-6 < resI[1] else resI[1] * 10
|
||||
assert_allclose(resI[0], resG, rtol=rtol)
|
||||
|
||||
def test_roots_jacobi():
|
||||
def rf(a, b):
|
||||
return lambda n, mu: sc.roots_jacobi(n, a, b, mu)
|
||||
def ef(a, b):
|
||||
return lambda n, x: sc.eval_jacobi(n, a, b, x)
|
||||
def wf(a, b):
|
||||
return lambda x: (1 - x) ** a * (1 + x) ** b
|
||||
|
||||
vgq = verify_gauss_quad
|
||||
vgq(rf(-0.5, -0.75), ef(-0.5, -0.75), wf(-0.5, -0.75), -1., 1., 5)
|
||||
vgq(rf(-0.5, -0.75), ef(-0.5, -0.75), wf(-0.5, -0.75), -1., 1.,
|
||||
25, atol=1e-12)
|
||||
vgq(rf(-0.5, -0.75), ef(-0.5, -0.75), wf(-0.5, -0.75), -1., 1.,
|
||||
100, atol=1e-11)
|
||||
|
||||
vgq(rf(0.5, -0.5), ef(0.5, -0.5), wf(0.5, -0.5), -1., 1., 5)
|
||||
vgq(rf(0.5, -0.5), ef(0.5, -0.5), wf(0.5, -0.5), -1., 1., 25, atol=1.5e-13)
|
||||
vgq(rf(0.5, -0.5), ef(0.5, -0.5), wf(0.5, -0.5), -1., 1., 100, atol=2e-12)
|
||||
|
||||
vgq(rf(1, 0.5), ef(1, 0.5), wf(1, 0.5), -1., 1., 5, atol=2e-13)
|
||||
vgq(rf(1, 0.5), ef(1, 0.5), wf(1, 0.5), -1., 1., 25, atol=2e-13)
|
||||
vgq(rf(1, 0.5), ef(1, 0.5), wf(1, 0.5), -1., 1., 100, atol=1e-12)
|
||||
|
||||
vgq(rf(0.9, 2), ef(0.9, 2), wf(0.9, 2), -1., 1., 5)
|
||||
vgq(rf(0.9, 2), ef(0.9, 2), wf(0.9, 2), -1., 1., 25, atol=1e-13)
|
||||
vgq(rf(0.9, 2), ef(0.9, 2), wf(0.9, 2), -1., 1., 100, atol=3e-13)
|
||||
|
||||
vgq(rf(18.24, 27.3), ef(18.24, 27.3), wf(18.24, 27.3), -1., 1., 5)
|
||||
vgq(rf(18.24, 27.3), ef(18.24, 27.3), wf(18.24, 27.3), -1., 1., 25,
|
||||
atol=1.1e-14)
|
||||
vgq(rf(18.24, 27.3), ef(18.24, 27.3), wf(18.24, 27.3), -1., 1.,
|
||||
100, atol=1e-13)
|
||||
|
||||
vgq(rf(47.1, -0.2), ef(47.1, -0.2), wf(47.1, -0.2), -1., 1., 5, atol=1e-13)
|
||||
vgq(rf(47.1, -0.2), ef(47.1, -0.2), wf(47.1, -0.2), -1., 1., 25, atol=2e-13)
|
||||
vgq(rf(47.1, -0.2), ef(47.1, -0.2), wf(47.1, -0.2), -1., 1.,
|
||||
100, atol=1e-11)
|
||||
|
||||
vgq(rf(1., 658.), ef(1., 658.), wf(1., 658.), -1., 1., 5, atol=2e-13)
|
||||
vgq(rf(1., 658.), ef(1., 658.), wf(1., 658.), -1., 1., 25, atol=1e-12)
|
||||
vgq(rf(1., 658.), ef(1., 658.), wf(1., 658.), -1., 1., 100, atol=1e-11)
|
||||
vgq(rf(1., 658.), ef(1., 658.), wf(1., 658.), -1., 1., 250, atol=1e-11)
|
||||
|
||||
vgq(rf(511., 511.), ef(511., 511.), wf(511., 511.), -1., 1., 5,
|
||||
atol=1e-12)
|
||||
vgq(rf(511., 511.), ef(511., 511.), wf(511., 511.), -1., 1., 25,
|
||||
atol=1e-11)
|
||||
vgq(rf(511., 511.), ef(511., 511.), wf(511., 511.), -1., 1., 100,
|
||||
atol=1e-10)
|
||||
|
||||
vgq(rf(511., 512.), ef(511., 512.), wf(511., 512.), -1., 1., 5,
|
||||
atol=1e-12)
|
||||
vgq(rf(511., 512.), ef(511., 512.), wf(511., 512.), -1., 1., 25,
|
||||
atol=1e-11)
|
||||
vgq(rf(511., 512.), ef(511., 512.), wf(511., 512.), -1., 1., 100,
|
||||
atol=1e-10)
|
||||
|
||||
vgq(rf(1000., 500.), ef(1000., 500.), wf(1000., 500.), -1., 1., 5,
|
||||
atol=1e-12)
|
||||
vgq(rf(1000., 500.), ef(1000., 500.), wf(1000., 500.), -1., 1., 25,
|
||||
atol=1e-11)
|
||||
vgq(rf(1000., 500.), ef(1000., 500.), wf(1000., 500.), -1., 1., 100,
|
||||
atol=1e-10)
|
||||
|
||||
vgq(rf(2.25, 68.9), ef(2.25, 68.9), wf(2.25, 68.9), -1., 1., 5)
|
||||
vgq(rf(2.25, 68.9), ef(2.25, 68.9), wf(2.25, 68.9), -1., 1., 25,
|
||||
atol=1e-13)
|
||||
vgq(rf(2.25, 68.9), ef(2.25, 68.9), wf(2.25, 68.9), -1., 1., 100,
|
||||
atol=1e-13)
|
||||
|
||||
# when alpha == beta == 0, P_n^{a,b}(x) == P_n(x)
|
||||
xj, wj = sc.roots_jacobi(6, 0.0, 0.0)
|
||||
xl, wl = sc.roots_legendre(6)
|
||||
assert_allclose(xj, xl, 1e-14, 1e-14)
|
||||
assert_allclose(wj, wl, 1e-14, 1e-14)
|
||||
|
||||
# when alpha == beta != 0, P_n^{a,b}(x) == C_n^{alpha+0.5}(x)
|
||||
xj, wj = sc.roots_jacobi(6, 4.0, 4.0)
|
||||
xc, wc = sc.roots_gegenbauer(6, 4.5)
|
||||
assert_allclose(xj, xc, 1e-14, 1e-14)
|
||||
assert_allclose(wj, wc, 1e-14, 1e-14)
|
||||
|
||||
x, w = sc.roots_jacobi(5, 2, 3, False)
|
||||
y, v, m = sc.roots_jacobi(5, 2, 3, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(wf(2,3), -1, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_jacobi, 0, 1, 1)
|
||||
assert_raises(ValueError, sc.roots_jacobi, 3.3, 1, 1)
|
||||
assert_raises(ValueError, sc.roots_jacobi, 3, -2, 1)
|
||||
assert_raises(ValueError, sc.roots_jacobi, 3, 1, -2)
|
||||
assert_raises(ValueError, sc.roots_jacobi, 3, -2, -2)
|
||||
|
||||
def test_roots_sh_jacobi():
|
||||
def rf(a, b):
|
||||
return lambda n, mu: sc.roots_sh_jacobi(n, a, b, mu)
|
||||
def ef(a, b):
|
||||
return lambda n, x: sc.eval_sh_jacobi(n, a, b, x)
|
||||
def wf(a, b):
|
||||
return lambda x: (1.0 - x) ** (a - b) * x ** (b - 1.0)
|
||||
|
||||
vgq = verify_gauss_quad
|
||||
vgq(rf(-0.5, 0.25), ef(-0.5, 0.25), wf(-0.5, 0.25), 0., 1., 5)
|
||||
vgq(rf(-0.5, 0.25), ef(-0.5, 0.25), wf(-0.5, 0.25), 0., 1.,
|
||||
25, atol=1e-12)
|
||||
vgq(rf(-0.5, 0.25), ef(-0.5, 0.25), wf(-0.5, 0.25), 0., 1.,
|
||||
100, atol=1e-11)
|
||||
|
||||
vgq(rf(0.5, 0.5), ef(0.5, 0.5), wf(0.5, 0.5), 0., 1., 5)
|
||||
vgq(rf(0.5, 0.5), ef(0.5, 0.5), wf(0.5, 0.5), 0., 1., 25, atol=1e-13)
|
||||
vgq(rf(0.5, 0.5), ef(0.5, 0.5), wf(0.5, 0.5), 0., 1., 100, atol=1e-12)
|
||||
|
||||
vgq(rf(1, 0.5), ef(1, 0.5), wf(1, 0.5), 0., 1., 5)
|
||||
vgq(rf(1, 0.5), ef(1, 0.5), wf(1, 0.5), 0., 1., 25, atol=1.5e-13)
|
||||
vgq(rf(1, 0.5), ef(1, 0.5), wf(1, 0.5), 0., 1., 100, atol=2e-12)
|
||||
|
||||
vgq(rf(2, 0.9), ef(2, 0.9), wf(2, 0.9), 0., 1., 5)
|
||||
vgq(rf(2, 0.9), ef(2, 0.9), wf(2, 0.9), 0., 1., 25, atol=1e-13)
|
||||
vgq(rf(2, 0.9), ef(2, 0.9), wf(2, 0.9), 0., 1., 100, atol=1e-12)
|
||||
|
||||
vgq(rf(27.3, 18.24), ef(27.3, 18.24), wf(27.3, 18.24), 0., 1., 5)
|
||||
vgq(rf(27.3, 18.24), ef(27.3, 18.24), wf(27.3, 18.24), 0., 1., 25)
|
||||
vgq(rf(27.3, 18.24), ef(27.3, 18.24), wf(27.3, 18.24), 0., 1.,
|
||||
100, atol=1e-13)
|
||||
|
||||
vgq(rf(47.1, 0.2), ef(47.1, 0.2), wf(47.1, 0.2), 0., 1., 5, atol=1e-12)
|
||||
vgq(rf(47.1, 0.2), ef(47.1, 0.2), wf(47.1, 0.2), 0., 1., 25, atol=1e-11)
|
||||
vgq(rf(47.1, 0.2), ef(47.1, 0.2), wf(47.1, 0.2), 0., 1., 100, atol=1e-10)
|
||||
|
||||
vgq(rf(68.9, 2.25), ef(68.9, 2.25), wf(68.9, 2.25), 0., 1., 5, atol=3.5e-14)
|
||||
vgq(rf(68.9, 2.25), ef(68.9, 2.25), wf(68.9, 2.25), 0., 1., 25, atol=2e-13)
|
||||
vgq(rf(68.9, 2.25), ef(68.9, 2.25), wf(68.9, 2.25), 0., 1.,
|
||||
100, atol=1e-12)
|
||||
|
||||
x, w = sc.roots_sh_jacobi(5, 3, 2, False)
|
||||
y, v, m = sc.roots_sh_jacobi(5, 3, 2, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(wf(3,2), 0, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_sh_jacobi, 0, 1, 1)
|
||||
assert_raises(ValueError, sc.roots_sh_jacobi, 3.3, 1, 1)
|
||||
assert_raises(ValueError, sc.roots_sh_jacobi, 3, 1, 2) # p - q <= -1
|
||||
assert_raises(ValueError, sc.roots_sh_jacobi, 3, 2, -1) # q <= 0
|
||||
assert_raises(ValueError, sc.roots_sh_jacobi, 3, -2, -1) # both
|
||||
|
||||
def test_roots_hermite():
|
||||
rootf = sc.roots_hermite
|
||||
evalf = sc.eval_hermite
|
||||
weightf = orth.hermite(5).weight_func
|
||||
|
||||
verify_gauss_quad(rootf, evalf, weightf, -np.inf, np.inf, 5)
|
||||
verify_gauss_quad(rootf, evalf, weightf, -np.inf, np.inf, 25, atol=1e-13)
|
||||
verify_gauss_quad(rootf, evalf, weightf, -np.inf, np.inf, 100, atol=1e-12)
|
||||
|
||||
# Golub-Welsch branch
|
||||
x, w = sc.roots_hermite(5, False)
|
||||
y, v, m = sc.roots_hermite(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, -np.inf, np.inf)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
# Asymptotic branch (switch over at n >= 150)
|
||||
x, w = sc.roots_hermite(200, False)
|
||||
y, v, m = sc.roots_hermite(200, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
assert_allclose(sum(v), m, 1e-14, 1e-14)
|
||||
|
||||
assert_raises(ValueError, sc.roots_hermite, 0)
|
||||
assert_raises(ValueError, sc.roots_hermite, 3.3)
|
||||
|
||||
def test_roots_hermite_asy():
|
||||
# Recursion for Hermite functions
|
||||
def hermite_recursion(n, nodes):
|
||||
H = np.zeros((n, nodes.size))
|
||||
H[0,:] = np.pi**(-0.25) * np.exp(-0.5*nodes**2)
|
||||
if n > 1:
|
||||
H[1,:] = sqrt(2.0) * nodes * H[0,:]
|
||||
for k in range(2, n):
|
||||
H[k,:] = sqrt(2.0/k) * nodes * H[k-1,:] - sqrt((k-1.0)/k) * H[k-2,:]
|
||||
return H
|
||||
|
||||
# This tests only the nodes
|
||||
def test(N, rtol=1e-15, atol=1e-14):
|
||||
x, w = orth._roots_hermite_asy(N)
|
||||
H = hermite_recursion(N+1, x)
|
||||
assert_allclose(H[-1,:], np.zeros(N), rtol, atol)
|
||||
assert_allclose(sum(w), sqrt(np.pi), rtol, atol)
|
||||
|
||||
test(150, atol=1e-12)
|
||||
test(151, atol=1e-12)
|
||||
test(300, atol=1e-12)
|
||||
test(301, atol=1e-12)
|
||||
test(500, atol=1e-12)
|
||||
test(501, atol=1e-12)
|
||||
test(999, atol=1e-12)
|
||||
test(1000, atol=1e-12)
|
||||
test(2000, atol=1e-12)
|
||||
test(5000, atol=1e-12)
|
||||
|
||||
def test_roots_hermitenorm():
|
||||
rootf = sc.roots_hermitenorm
|
||||
evalf = sc.eval_hermitenorm
|
||||
weightf = orth.hermitenorm(5).weight_func
|
||||
|
||||
verify_gauss_quad(rootf, evalf, weightf, -np.inf, np.inf, 5)
|
||||
verify_gauss_quad(rootf, evalf, weightf, -np.inf, np.inf, 25, atol=1e-13)
|
||||
verify_gauss_quad(rootf, evalf, weightf, -np.inf, np.inf, 100, atol=1e-12)
|
||||
|
||||
x, w = sc.roots_hermitenorm(5, False)
|
||||
y, v, m = sc.roots_hermitenorm(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, -np.inf, np.inf)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_hermitenorm, 0)
|
||||
assert_raises(ValueError, sc.roots_hermitenorm, 3.3)
|
||||
|
||||
def test_roots_gegenbauer():
|
||||
def rootf(a):
|
||||
return lambda n, mu: sc.roots_gegenbauer(n, a, mu)
|
||||
def evalf(a):
|
||||
return lambda n, x: sc.eval_gegenbauer(n, a, x)
|
||||
def weightf(a):
|
||||
return lambda x: (1 - x ** 2) ** (a - 0.5)
|
||||
|
||||
vgq = verify_gauss_quad
|
||||
vgq(rootf(-0.25), evalf(-0.25), weightf(-0.25), -1., 1., 5)
|
||||
vgq(rootf(-0.25), evalf(-0.25), weightf(-0.25), -1., 1., 25, atol=1e-12)
|
||||
vgq(rootf(-0.25), evalf(-0.25), weightf(-0.25), -1., 1., 100, atol=1e-11)
|
||||
|
||||
vgq(rootf(0.1), evalf(0.1), weightf(0.1), -1., 1., 5)
|
||||
vgq(rootf(0.1), evalf(0.1), weightf(0.1), -1., 1., 25, atol=1e-13)
|
||||
vgq(rootf(0.1), evalf(0.1), weightf(0.1), -1., 1., 100, atol=1e-12)
|
||||
|
||||
vgq(rootf(1), evalf(1), weightf(1), -1., 1., 5)
|
||||
vgq(rootf(1), evalf(1), weightf(1), -1., 1., 25, atol=1e-13)
|
||||
vgq(rootf(1), evalf(1), weightf(1), -1., 1., 100, atol=1e-12)
|
||||
|
||||
vgq(rootf(10), evalf(10), weightf(10), -1., 1., 5)
|
||||
vgq(rootf(10), evalf(10), weightf(10), -1., 1., 25, atol=1e-13)
|
||||
vgq(rootf(10), evalf(10), weightf(10), -1., 1., 100, atol=1e-12)
|
||||
|
||||
vgq(rootf(50), evalf(50), weightf(50), -1., 1., 5, atol=1e-13)
|
||||
vgq(rootf(50), evalf(50), weightf(50), -1., 1., 25, atol=1e-12)
|
||||
vgq(rootf(50), evalf(50), weightf(50), -1., 1., 100, atol=1e-11)
|
||||
|
||||
# Alpha=170 is where the approximation used in roots_gegenbauer changes
|
||||
vgq(rootf(170), evalf(170), weightf(170), -1., 1., 5, atol=1e-13)
|
||||
vgq(rootf(170), evalf(170), weightf(170), -1., 1., 25, atol=1e-12)
|
||||
vgq(rootf(170), evalf(170), weightf(170), -1., 1., 100, atol=1e-11)
|
||||
vgq(rootf(170.5), evalf(170.5), weightf(170.5), -1., 1., 5, atol=1.25e-13)
|
||||
vgq(rootf(170.5), evalf(170.5), weightf(170.5), -1., 1., 25, atol=1e-12)
|
||||
vgq(rootf(170.5), evalf(170.5), weightf(170.5), -1., 1., 100, atol=1e-11)
|
||||
|
||||
# Test for failures, e.g. overflows, resulting from large alphas
|
||||
vgq(rootf(238), evalf(238), weightf(238), -1., 1., 5, atol=1e-13)
|
||||
vgq(rootf(238), evalf(238), weightf(238), -1., 1., 25, atol=1e-12)
|
||||
vgq(rootf(238), evalf(238), weightf(238), -1., 1., 100, atol=1e-11)
|
||||
vgq(rootf(512.5), evalf(512.5), weightf(512.5), -1., 1., 5, atol=1e-12)
|
||||
vgq(rootf(512.5), evalf(512.5), weightf(512.5), -1., 1., 25, atol=1e-11)
|
||||
vgq(rootf(512.5), evalf(512.5), weightf(512.5), -1., 1., 100, atol=1e-10)
|
||||
|
||||
# this is a special case that the old code supported.
|
||||
# when alpha = 0, the gegenbauer polynomial is uniformly 0. but it goes
|
||||
# to a scaled down copy of T_n(x) there.
|
||||
vgq(rootf(0), sc.eval_chebyt, weightf(0), -1., 1., 5)
|
||||
vgq(rootf(0), sc.eval_chebyt, weightf(0), -1., 1., 25)
|
||||
vgq(rootf(0), sc.eval_chebyt, weightf(0), -1., 1., 100, atol=1e-12)
|
||||
|
||||
x, w = sc.roots_gegenbauer(5, 2, False)
|
||||
y, v, m = sc.roots_gegenbauer(5, 2, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf(2), -1, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_gegenbauer, 0, 2)
|
||||
assert_raises(ValueError, sc.roots_gegenbauer, 3.3, 2)
|
||||
assert_raises(ValueError, sc.roots_gegenbauer, 3, -.75)
|
||||
|
||||
def test_roots_chebyt():
|
||||
weightf = orth.chebyt(5).weight_func
|
||||
verify_gauss_quad(sc.roots_chebyt, sc.eval_chebyt, weightf, -1., 1., 5)
|
||||
verify_gauss_quad(sc.roots_chebyt, sc.eval_chebyt, weightf, -1., 1., 25)
|
||||
verify_gauss_quad(sc.roots_chebyt, sc.eval_chebyt, weightf, -1., 1., 100,
|
||||
atol=1e-12)
|
||||
|
||||
x, w = sc.roots_chebyt(5, False)
|
||||
y, v, m = sc.roots_chebyt(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, -1, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_chebyt, 0)
|
||||
assert_raises(ValueError, sc.roots_chebyt, 3.3)
|
||||
|
||||
def test_chebyt_symmetry():
|
||||
x, w = sc.roots_chebyt(21)
|
||||
pos, neg = x[:10], x[11:]
|
||||
assert_equal(neg, -pos[::-1])
|
||||
assert_equal(x[10], 0)
|
||||
|
||||
def test_roots_chebyu():
|
||||
weightf = orth.chebyu(5).weight_func
|
||||
verify_gauss_quad(sc.roots_chebyu, sc.eval_chebyu, weightf, -1., 1., 5)
|
||||
verify_gauss_quad(sc.roots_chebyu, sc.eval_chebyu, weightf, -1., 1., 25)
|
||||
verify_gauss_quad(sc.roots_chebyu, sc.eval_chebyu, weightf, -1., 1., 100)
|
||||
|
||||
x, w = sc.roots_chebyu(5, False)
|
||||
y, v, m = sc.roots_chebyu(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, -1, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_chebyu, 0)
|
||||
assert_raises(ValueError, sc.roots_chebyu, 3.3)
|
||||
|
||||
def test_roots_chebyc():
|
||||
weightf = orth.chebyc(5).weight_func
|
||||
verify_gauss_quad(sc.roots_chebyc, sc.eval_chebyc, weightf, -2., 2., 5)
|
||||
verify_gauss_quad(sc.roots_chebyc, sc.eval_chebyc, weightf, -2., 2., 25)
|
||||
verify_gauss_quad(sc.roots_chebyc, sc.eval_chebyc, weightf, -2., 2., 100,
|
||||
atol=1e-12)
|
||||
|
||||
x, w = sc.roots_chebyc(5, False)
|
||||
y, v, m = sc.roots_chebyc(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, -2, 2)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_chebyc, 0)
|
||||
assert_raises(ValueError, sc.roots_chebyc, 3.3)
|
||||
|
||||
def test_roots_chebys():
|
||||
weightf = orth.chebys(5).weight_func
|
||||
verify_gauss_quad(sc.roots_chebys, sc.eval_chebys, weightf, -2., 2., 5)
|
||||
verify_gauss_quad(sc.roots_chebys, sc.eval_chebys, weightf, -2., 2., 25)
|
||||
verify_gauss_quad(sc.roots_chebys, sc.eval_chebys, weightf, -2., 2., 100)
|
||||
|
||||
x, w = sc.roots_chebys(5, False)
|
||||
y, v, m = sc.roots_chebys(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, -2, 2)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_chebys, 0)
|
||||
assert_raises(ValueError, sc.roots_chebys, 3.3)
|
||||
|
||||
def test_roots_sh_chebyt():
|
||||
weightf = orth.sh_chebyt(5).weight_func
|
||||
verify_gauss_quad(sc.roots_sh_chebyt, sc.eval_sh_chebyt, weightf, 0., 1., 5)
|
||||
verify_gauss_quad(sc.roots_sh_chebyt, sc.eval_sh_chebyt, weightf, 0., 1., 25)
|
||||
verify_gauss_quad(sc.roots_sh_chebyt, sc.eval_sh_chebyt, weightf, 0., 1.,
|
||||
100, atol=1e-13)
|
||||
|
||||
x, w = sc.roots_sh_chebyt(5, False)
|
||||
y, v, m = sc.roots_sh_chebyt(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, 0, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_sh_chebyt, 0)
|
||||
assert_raises(ValueError, sc.roots_sh_chebyt, 3.3)
|
||||
|
||||
def test_roots_sh_chebyu():
|
||||
weightf = orth.sh_chebyu(5).weight_func
|
||||
verify_gauss_quad(sc.roots_sh_chebyu, sc.eval_sh_chebyu, weightf, 0., 1., 5)
|
||||
verify_gauss_quad(sc.roots_sh_chebyu, sc.eval_sh_chebyu, weightf, 0., 1., 25)
|
||||
verify_gauss_quad(sc.roots_sh_chebyu, sc.eval_sh_chebyu, weightf, 0., 1.,
|
||||
100, atol=1e-13)
|
||||
|
||||
x, w = sc.roots_sh_chebyu(5, False)
|
||||
y, v, m = sc.roots_sh_chebyu(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, 0, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_sh_chebyu, 0)
|
||||
assert_raises(ValueError, sc.roots_sh_chebyu, 3.3)
|
||||
|
||||
def test_roots_legendre():
|
||||
weightf = orth.legendre(5).weight_func
|
||||
verify_gauss_quad(sc.roots_legendre, sc.eval_legendre, weightf, -1., 1., 5)
|
||||
verify_gauss_quad(sc.roots_legendre, sc.eval_legendre, weightf, -1., 1.,
|
||||
25, atol=1e-13)
|
||||
verify_gauss_quad(sc.roots_legendre, sc.eval_legendre, weightf, -1., 1.,
|
||||
100, atol=1e-12)
|
||||
|
||||
x, w = sc.roots_legendre(5, False)
|
||||
y, v, m = sc.roots_legendre(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, -1, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_legendre, 0)
|
||||
assert_raises(ValueError, sc.roots_legendre, 3.3)
|
||||
|
||||
def test_roots_sh_legendre():
|
||||
weightf = orth.sh_legendre(5).weight_func
|
||||
verify_gauss_quad(sc.roots_sh_legendre, sc.eval_sh_legendre, weightf, 0., 1., 5)
|
||||
verify_gauss_quad(sc.roots_sh_legendre, sc.eval_sh_legendre, weightf, 0., 1.,
|
||||
25, atol=1e-13)
|
||||
verify_gauss_quad(sc.roots_sh_legendre, sc.eval_sh_legendre, weightf, 0., 1.,
|
||||
100, atol=1e-12)
|
||||
|
||||
x, w = sc.roots_sh_legendre(5, False)
|
||||
y, v, m = sc.roots_sh_legendre(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, 0, 1)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_sh_legendre, 0)
|
||||
assert_raises(ValueError, sc.roots_sh_legendre, 3.3)
|
||||
|
||||
def test_roots_laguerre():
|
||||
weightf = orth.laguerre(5).weight_func
|
||||
verify_gauss_quad(sc.roots_laguerre, sc.eval_laguerre, weightf, 0., np.inf, 5)
|
||||
verify_gauss_quad(sc.roots_laguerre, sc.eval_laguerre, weightf, 0., np.inf,
|
||||
25, atol=1e-13)
|
||||
verify_gauss_quad(sc.roots_laguerre, sc.eval_laguerre, weightf, 0., np.inf,
|
||||
100, atol=1e-12)
|
||||
|
||||
x, w = sc.roots_laguerre(5, False)
|
||||
y, v, m = sc.roots_laguerre(5, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf, 0, np.inf)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_laguerre, 0)
|
||||
assert_raises(ValueError, sc.roots_laguerre, 3.3)
|
||||
|
||||
def test_roots_genlaguerre():
|
||||
def rootf(a):
|
||||
return lambda n, mu: sc.roots_genlaguerre(n, a, mu)
|
||||
def evalf(a):
|
||||
return lambda n, x: sc.eval_genlaguerre(n, a, x)
|
||||
def weightf(a):
|
||||
return lambda x: x ** a * np.exp(-x)
|
||||
|
||||
vgq = verify_gauss_quad
|
||||
vgq(rootf(-0.5), evalf(-0.5), weightf(-0.5), 0., np.inf, 5)
|
||||
vgq(rootf(-0.5), evalf(-0.5), weightf(-0.5), 0., np.inf, 25, atol=1e-13)
|
||||
vgq(rootf(-0.5), evalf(-0.5), weightf(-0.5), 0., np.inf, 100, atol=1e-12)
|
||||
|
||||
vgq(rootf(0.1), evalf(0.1), weightf(0.1), 0., np.inf, 5)
|
||||
vgq(rootf(0.1), evalf(0.1), weightf(0.1), 0., np.inf, 25, atol=1e-13)
|
||||
vgq(rootf(0.1), evalf(0.1), weightf(0.1), 0., np.inf, 100, atol=1.6e-13)
|
||||
|
||||
vgq(rootf(1), evalf(1), weightf(1), 0., np.inf, 5)
|
||||
vgq(rootf(1), evalf(1), weightf(1), 0., np.inf, 25, atol=1e-13)
|
||||
vgq(rootf(1), evalf(1), weightf(1), 0., np.inf, 100, atol=1.03e-13)
|
||||
|
||||
vgq(rootf(10), evalf(10), weightf(10), 0., np.inf, 5)
|
||||
vgq(rootf(10), evalf(10), weightf(10), 0., np.inf, 25, atol=1e-13)
|
||||
vgq(rootf(10), evalf(10), weightf(10), 0., np.inf, 100, atol=1e-12)
|
||||
|
||||
vgq(rootf(50), evalf(50), weightf(50), 0., np.inf, 5)
|
||||
vgq(rootf(50), evalf(50), weightf(50), 0., np.inf, 25, atol=1e-13)
|
||||
vgq(rootf(50), evalf(50), weightf(50), 0., np.inf, 100, rtol=1e-14, atol=2e-13)
|
||||
|
||||
x, w = sc.roots_genlaguerre(5, 2, False)
|
||||
y, v, m = sc.roots_genlaguerre(5, 2, True)
|
||||
assert_allclose(x, y, 1e-14, 1e-14)
|
||||
assert_allclose(w, v, 1e-14, 1e-14)
|
||||
|
||||
muI, muI_err = integrate.quad(weightf(2.), 0., np.inf)
|
||||
assert_allclose(m, muI, rtol=muI_err)
|
||||
|
||||
assert_raises(ValueError, sc.roots_genlaguerre, 0, 2)
|
||||
assert_raises(ValueError, sc.roots_genlaguerre, 3.3, 2)
|
||||
assert_raises(ValueError, sc.roots_genlaguerre, 3, -1.1)
|
||||
|
||||
|
||||
def test_gh_6721():
|
||||
# Regression test for gh_6721. This should not raise.
|
||||
sc.chebyt(65)(0.2)
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_, assert_allclose
|
||||
import pytest
|
||||
|
||||
from scipy.special import _ufuncs
|
||||
import scipy.special._orthogonal as orth
|
||||
from scipy.special._testutils import FuncData
|
||||
|
||||
|
||||
def test_eval_chebyt():
|
||||
n = np.arange(0, 10000, 7, dtype=np.dtype("long"))
|
||||
x = 2*np.random.rand() - 1
|
||||
v1 = np.cos(n*np.arccos(x))
|
||||
v2 = _ufuncs.eval_chebyt(n, x)
|
||||
assert_(np.allclose(v1, v2, rtol=1e-15))
|
||||
|
||||
|
||||
def test_eval_chebyt_gh20129():
|
||||
# https://github.com/scipy/scipy/issues/20129
|
||||
assert _ufuncs.eval_chebyt(7, 2 + 0j) == 5042.0
|
||||
|
||||
|
||||
def test_eval_genlaguerre_restriction():
|
||||
# check it returns nan for alpha <= -1
|
||||
assert_(np.isnan(_ufuncs.eval_genlaguerre(0, -1, 0)))
|
||||
assert_(np.isnan(_ufuncs.eval_genlaguerre(0.1, -1, 0)))
|
||||
|
||||
|
||||
def test_warnings():
|
||||
# ticket 1334
|
||||
with np.errstate(all='raise'):
|
||||
# these should raise no fp warnings
|
||||
_ufuncs.eval_legendre(1, 0)
|
||||
_ufuncs.eval_laguerre(1, 1)
|
||||
_ufuncs.eval_gegenbauer(1, 1, 0)
|
||||
|
||||
|
||||
class TestPolys:
|
||||
"""
|
||||
Check that the eval_* functions agree with the constructed polynomials
|
||||
|
||||
"""
|
||||
|
||||
def check_poly(self, func, cls, param_ranges=(), x_range=(), nn=10,
|
||||
nparam=10, nx=10, rtol=1e-8):
|
||||
rng = np.random.RandomState(1234)
|
||||
|
||||
dataset = []
|
||||
for n in np.arange(nn):
|
||||
params = [a + (b-a)*rng.rand(nparam) for a,b in param_ranges]
|
||||
params = np.asarray(params).T
|
||||
if not param_ranges:
|
||||
params = [0]
|
||||
for p in params:
|
||||
if param_ranges:
|
||||
p = (n,) + tuple(p)
|
||||
else:
|
||||
p = (n,)
|
||||
x = x_range[0] + (x_range[1] - x_range[0])*rng.rand(nx)
|
||||
x[0] = x_range[0] # always include domain start point
|
||||
x[1] = x_range[1] # always include domain end point
|
||||
poly = np.poly1d(cls(*p).coef)
|
||||
z = np.c_[np.tile(p, (nx,1)), x, poly(x)]
|
||||
dataset.append(z)
|
||||
|
||||
dataset = np.concatenate(dataset, axis=0)
|
||||
|
||||
def polyfunc(*p):
|
||||
p = (p[0].astype(np.dtype("long")),) + p[1:]
|
||||
return func(*p)
|
||||
|
||||
with np.errstate(all='raise'):
|
||||
ds = FuncData(polyfunc, dataset, list(range(len(param_ranges)+2)), -1,
|
||||
rtol=rtol)
|
||||
ds.check()
|
||||
|
||||
def test_jacobi(self):
|
||||
self.check_poly(_ufuncs.eval_jacobi, orth.jacobi,
|
||||
param_ranges=[(-0.99, 10), (-0.99, 10)],
|
||||
x_range=[-1, 1], rtol=1e-5)
|
||||
|
||||
def test_sh_jacobi(self):
|
||||
self.check_poly(_ufuncs.eval_sh_jacobi, orth.sh_jacobi,
|
||||
param_ranges=[(1, 10), (0, 1)], x_range=[0, 1],
|
||||
rtol=1e-5)
|
||||
|
||||
def test_gegenbauer(self):
|
||||
self.check_poly(_ufuncs.eval_gegenbauer, orth.gegenbauer,
|
||||
param_ranges=[(-0.499, 10)], x_range=[-1, 1],
|
||||
rtol=1e-7)
|
||||
|
||||
def test_chebyt(self):
|
||||
self.check_poly(_ufuncs.eval_chebyt, orth.chebyt,
|
||||
param_ranges=[], x_range=[-1, 1])
|
||||
|
||||
def test_chebyu(self):
|
||||
self.check_poly(_ufuncs.eval_chebyu, orth.chebyu,
|
||||
param_ranges=[], x_range=[-1, 1])
|
||||
|
||||
def test_chebys(self):
|
||||
self.check_poly(_ufuncs.eval_chebys, orth.chebys,
|
||||
param_ranges=[], x_range=[-2, 2])
|
||||
|
||||
def test_chebyc(self):
|
||||
self.check_poly(_ufuncs.eval_chebyc, orth.chebyc,
|
||||
param_ranges=[], x_range=[-2, 2])
|
||||
|
||||
def test_sh_chebyt(self):
|
||||
with np.errstate(all='ignore'):
|
||||
self.check_poly(_ufuncs.eval_sh_chebyt, orth.sh_chebyt,
|
||||
param_ranges=[], x_range=[0, 1])
|
||||
|
||||
def test_sh_chebyu(self):
|
||||
self.check_poly(_ufuncs.eval_sh_chebyu, orth.sh_chebyu,
|
||||
param_ranges=[], x_range=[0, 1])
|
||||
|
||||
def test_legendre(self):
|
||||
self.check_poly(_ufuncs.eval_legendre, orth.legendre,
|
||||
param_ranges=[], x_range=[-1, 1])
|
||||
|
||||
def test_sh_legendre(self):
|
||||
with np.errstate(all='ignore'):
|
||||
self.check_poly(_ufuncs.eval_sh_legendre, orth.sh_legendre,
|
||||
param_ranges=[], x_range=[0, 1])
|
||||
|
||||
def test_genlaguerre(self):
|
||||
self.check_poly(_ufuncs.eval_genlaguerre, orth.genlaguerre,
|
||||
param_ranges=[(-0.99, 10)], x_range=[0, 100])
|
||||
|
||||
def test_laguerre(self):
|
||||
self.check_poly(_ufuncs.eval_laguerre, orth.laguerre,
|
||||
param_ranges=[], x_range=[0, 100])
|
||||
|
||||
def test_hermite(self):
|
||||
self.check_poly(_ufuncs.eval_hermite, orth.hermite,
|
||||
param_ranges=[], x_range=[-100, 100])
|
||||
|
||||
def test_hermitenorm(self):
|
||||
self.check_poly(_ufuncs.eval_hermitenorm, orth.hermitenorm,
|
||||
param_ranges=[], x_range=[-100, 100])
|
||||
|
||||
|
||||
class TestRecurrence:
|
||||
"""
|
||||
Check that the eval_* functions sig='ld->d' and 'dd->d' agree.
|
||||
|
||||
"""
|
||||
|
||||
def check_poly(self, func, param_ranges=(), x_range=(), nn=10,
|
||||
nparam=10, nx=10, rtol=1e-8):
|
||||
np.random.seed(1234)
|
||||
|
||||
dataset = []
|
||||
for n in np.arange(nn):
|
||||
params = [a + (b-a)*np.random.rand(nparam) for a,b in param_ranges]
|
||||
params = np.asarray(params).T
|
||||
if not param_ranges:
|
||||
params = [0]
|
||||
for p in params:
|
||||
if param_ranges:
|
||||
p = (n,) + tuple(p)
|
||||
else:
|
||||
p = (n,)
|
||||
x = x_range[0] + (x_range[1] - x_range[0])*np.random.rand(nx)
|
||||
x[0] = x_range[0] # always include domain start point
|
||||
x[1] = x_range[1] # always include domain end point
|
||||
kw = dict(sig=(len(p)+1)*'d'+'->d')
|
||||
z = np.c_[np.tile(p, (nx,1)), x, func(*(p + (x,)), **kw)]
|
||||
dataset.append(z)
|
||||
|
||||
dataset = np.concatenate(dataset, axis=0)
|
||||
|
||||
def polyfunc(*p):
|
||||
p0 = p[0].astype(np.intp)
|
||||
p = (p0,) + p[1:]
|
||||
p0_type_char = p0.dtype.char
|
||||
kw = dict(sig=p0_type_char + (len(p)-1)*'d' + '->d')
|
||||
return func(*p, **kw)
|
||||
|
||||
with np.errstate(all='raise'):
|
||||
ds = FuncData(polyfunc, dataset, list(range(len(param_ranges)+2)), -1,
|
||||
rtol=rtol)
|
||||
ds.check()
|
||||
|
||||
def test_jacobi(self):
|
||||
self.check_poly(_ufuncs.eval_jacobi,
|
||||
param_ranges=[(-0.99, 10), (-0.99, 10)],
|
||||
x_range=[-1, 1])
|
||||
|
||||
def test_sh_jacobi(self):
|
||||
self.check_poly(_ufuncs.eval_sh_jacobi,
|
||||
param_ranges=[(1, 10), (0, 1)], x_range=[0, 1])
|
||||
|
||||
def test_gegenbauer(self):
|
||||
self.check_poly(_ufuncs.eval_gegenbauer,
|
||||
param_ranges=[(-0.499, 10)], x_range=[-1, 1])
|
||||
|
||||
def test_chebyt(self):
|
||||
self.check_poly(_ufuncs.eval_chebyt,
|
||||
param_ranges=[], x_range=[-1, 1])
|
||||
|
||||
def test_chebyu(self):
|
||||
self.check_poly(_ufuncs.eval_chebyu,
|
||||
param_ranges=[], x_range=[-1, 1])
|
||||
|
||||
def test_chebys(self):
|
||||
self.check_poly(_ufuncs.eval_chebys,
|
||||
param_ranges=[], x_range=[-2, 2])
|
||||
|
||||
def test_chebyc(self):
|
||||
self.check_poly(_ufuncs.eval_chebyc,
|
||||
param_ranges=[], x_range=[-2, 2])
|
||||
|
||||
def test_sh_chebyt(self):
|
||||
self.check_poly(_ufuncs.eval_sh_chebyt,
|
||||
param_ranges=[], x_range=[0, 1])
|
||||
|
||||
def test_sh_chebyu(self):
|
||||
self.check_poly(_ufuncs.eval_sh_chebyu,
|
||||
param_ranges=[], x_range=[0, 1])
|
||||
|
||||
def test_legendre(self):
|
||||
self.check_poly(_ufuncs.eval_legendre,
|
||||
param_ranges=[], x_range=[-1, 1])
|
||||
|
||||
def test_sh_legendre(self):
|
||||
self.check_poly(_ufuncs.eval_sh_legendre,
|
||||
param_ranges=[], x_range=[0, 1])
|
||||
|
||||
def test_genlaguerre(self):
|
||||
self.check_poly(_ufuncs.eval_genlaguerre,
|
||||
param_ranges=[(-0.99, 10)], x_range=[0, 100])
|
||||
|
||||
def test_laguerre(self):
|
||||
self.check_poly(_ufuncs.eval_laguerre,
|
||||
param_ranges=[], x_range=[0, 100])
|
||||
|
||||
def test_hermite(self):
|
||||
v = _ufuncs.eval_hermite(70, 1.0)
|
||||
a = -1.457076485701412e60
|
||||
assert_allclose(v, a)
|
||||
|
||||
|
||||
def test_hermite_domain():
|
||||
# Regression test for gh-11091.
|
||||
assert np.isnan(_ufuncs.eval_hermite(-1, 1.0))
|
||||
assert np.isnan(_ufuncs.eval_hermitenorm(-1, 1.0))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("n", [0, 1, 2])
|
||||
@pytest.mark.parametrize("x", [0, 1, np.nan])
|
||||
def test_hermite_nan(n, x):
|
||||
# Regression test for gh-11369.
|
||||
assert np.isnan(_ufuncs.eval_hermite(n, x)) == np.any(np.isnan([n, x]))
|
||||
assert np.isnan(_ufuncs.eval_hermitenorm(n, x)) == np.any(np.isnan([n, x]))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('n', [0, 1, 2, 3.2])
|
||||
@pytest.mark.parametrize('alpha', [1, np.nan])
|
||||
@pytest.mark.parametrize('x', [2, np.nan])
|
||||
def test_genlaguerre_nan(n, alpha, x):
|
||||
# Regression test for gh-11361.
|
||||
nan_laguerre = np.isnan(_ufuncs.eval_genlaguerre(n, alpha, x))
|
||||
nan_arg = np.any(np.isnan([n, alpha, x]))
|
||||
assert nan_laguerre == nan_arg
|
||||
|
||||
|
||||
@pytest.mark.parametrize('n', [0, 1, 2, 3.2])
|
||||
@pytest.mark.parametrize('alpha', [0.0, 1, np.nan])
|
||||
@pytest.mark.parametrize('x', [1e-6, 2, np.nan])
|
||||
def test_gegenbauer_nan(n, alpha, x):
|
||||
# Regression test for gh-11370.
|
||||
nan_gegenbauer = np.isnan(_ufuncs.eval_gegenbauer(n, alpha, x))
|
||||
nan_arg = np.any(np.isnan([n, alpha, x]))
|
||||
assert nan_gegenbauer == nan_arg
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue