Sam Chaudry
Upload folder using huggingface_hub
7885a28 verified
raw
history blame
23.5 kB
"""
Test cdflib functions versus mpmath, if available.
The following functions still need tests:
- ncfdtri
- ncfdtridfn
- ncfdtridfd
- ncfdtrinc
- nbdtrik
- nbdtrin
- pdtrik
- nctdtrit
- 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",
[[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(dfn, dfd, nc, f, expected):
# 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, rtol=1e-13, atol=0)
class TestNctdtr:
# 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", [
(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):
# 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.
result = sp.nctdtr(df, nc, x)
assert_allclose(result, expected, 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_accuracy(self, df, nc, x, expected, rtol):
assert_allclose(sp.nctdtr(df, nc, x), expected, rtol=rtol)