init
This commit is contained in:
@@ -0,0 +1 @@
|
||||
from scipy.special cimport cython_special
|
||||
@@ -0,0 +1,841 @@
|
||||
"""
|
||||
========================================
|
||||
Special functions (:mod:`scipy.special`)
|
||||
========================================
|
||||
|
||||
.. currentmodule:: scipy.special
|
||||
|
||||
Almost all of the functions below accept NumPy arrays as input
|
||||
arguments as well as single numbers. This means they follow
|
||||
broadcasting and automatic array-looping rules. Technically,
|
||||
they are `NumPy universal functions
|
||||
<https://numpy.org/doc/stable/user/basics.ufuncs.html#ufuncs-basics>`_.
|
||||
Functions which do not accept NumPy arrays are marked by a warning
|
||||
in the section description.
|
||||
|
||||
.. seealso::
|
||||
|
||||
`scipy.special.cython_special` -- Typed Cython versions of special functions
|
||||
|
||||
|
||||
Error handling
|
||||
==============
|
||||
|
||||
Errors are handled by returning NaNs or other appropriate values.
|
||||
Some of the special function routines can emit warnings or raise
|
||||
exceptions when an error occurs. By default this is disabled, except
|
||||
for memory allocation errors, which result in an exception being raised.
|
||||
To query and control the current error handling state the following
|
||||
functions are provided.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
geterr -- Get the current way of handling special-function errors.
|
||||
seterr -- Set how special-function errors are handled.
|
||||
errstate -- Context manager for special-function error handling.
|
||||
SpecialFunctionWarning -- Warning that can be emitted by special functions.
|
||||
SpecialFunctionError -- Exception that can be raised by special functions.
|
||||
|
||||
Available functions
|
||||
===================
|
||||
|
||||
Airy functions
|
||||
--------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
airy -- Airy functions and their derivatives.
|
||||
airye -- Exponentially scaled Airy functions and their derivatives.
|
||||
ai_zeros -- Compute `nt` zeros and values of the Airy function Ai and its derivative.
|
||||
bi_zeros -- Compute `nt` zeros and values of the Airy function Bi and its derivative.
|
||||
itairy -- Integrals of Airy functions
|
||||
|
||||
|
||||
Elliptic functions and integrals
|
||||
--------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
ellipj -- Jacobian elliptic functions.
|
||||
ellipk -- Complete elliptic integral of the first kind.
|
||||
ellipkm1 -- Complete elliptic integral of the first kind around `m` = 1.
|
||||
ellipkinc -- Incomplete elliptic integral of the first kind.
|
||||
ellipe -- Complete elliptic integral of the second kind.
|
||||
ellipeinc -- Incomplete elliptic integral of the second kind.
|
||||
elliprc -- Degenerate symmetric integral RC.
|
||||
elliprd -- Symmetric elliptic integral of the second kind.
|
||||
elliprf -- Completely-symmetric elliptic integral of the first kind.
|
||||
elliprg -- Completely-symmetric elliptic integral of the second kind.
|
||||
elliprj -- Symmetric elliptic integral of the third kind.
|
||||
|
||||
Bessel functions
|
||||
----------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
jv -- Bessel function of the first kind of real order and \
|
||||
complex argument.
|
||||
jve -- Exponentially scaled Bessel function of order `v`.
|
||||
yn -- Bessel function of the second kind of integer order and \
|
||||
real argument.
|
||||
yv -- Bessel function of the second kind of real order and \
|
||||
complex argument.
|
||||
yve -- Exponentially scaled Bessel function of the second kind \
|
||||
of real order.
|
||||
kn -- Modified Bessel function of the second kind of integer \
|
||||
order `n`
|
||||
kv -- Modified Bessel function of the second kind of real order \
|
||||
`v`
|
||||
kve -- Exponentially scaled modified Bessel function of the \
|
||||
second kind.
|
||||
iv -- Modified Bessel function of the first kind of real order.
|
||||
ive -- Exponentially scaled modified Bessel function of the \
|
||||
first kind.
|
||||
hankel1 -- Hankel function of the first kind.
|
||||
hankel1e -- Exponentially scaled Hankel function of the first kind.
|
||||
hankel2 -- Hankel function of the second kind.
|
||||
hankel2e -- Exponentially scaled Hankel function of the second kind.
|
||||
wright_bessel -- Wright's generalized Bessel function.
|
||||
log_wright_bessel -- Logarithm of Wright's generalized Bessel function.
|
||||
|
||||
The following function does not accept NumPy arrays (it is not a
|
||||
universal function):
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
lmbda -- Jahnke-Emden Lambda function, Lambdav(x).
|
||||
|
||||
Zeros of Bessel functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following functions do not accept NumPy arrays (they are not
|
||||
universal functions):
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
jnjnp_zeros -- Compute zeros of integer-order Bessel functions Jn and Jn'.
|
||||
jnyn_zeros -- Compute nt zeros of Bessel functions Jn(x), Jn'(x), Yn(x), and Yn'(x).
|
||||
jn_zeros -- Compute zeros of integer-order Bessel function Jn(x).
|
||||
jnp_zeros -- Compute zeros of integer-order Bessel function derivative Jn'(x).
|
||||
yn_zeros -- Compute zeros of integer-order Bessel function Yn(x).
|
||||
ynp_zeros -- Compute zeros of integer-order Bessel function derivative Yn'(x).
|
||||
y0_zeros -- Compute nt zeros of Bessel function Y0(z), and derivative at each zero.
|
||||
y1_zeros -- Compute nt zeros of Bessel function Y1(z), and derivative at each zero.
|
||||
y1p_zeros -- Compute nt zeros of Bessel derivative Y1'(z), and value at each zero.
|
||||
|
||||
Faster versions of common Bessel functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
j0 -- Bessel function of the first kind of order 0.
|
||||
j1 -- Bessel function of the first kind of order 1.
|
||||
y0 -- Bessel function of the second kind of order 0.
|
||||
y1 -- Bessel function of the second kind of order 1.
|
||||
i0 -- Modified Bessel function of order 0.
|
||||
i0e -- Exponentially scaled modified Bessel function of order 0.
|
||||
i1 -- Modified Bessel function of order 1.
|
||||
i1e -- Exponentially scaled modified Bessel function of order 1.
|
||||
k0 -- Modified Bessel function of the second kind of order 0, :math:`K_0`.
|
||||
k0e -- Exponentially scaled modified Bessel function K of order 0
|
||||
k1 -- Modified Bessel function of the second kind of order 1, :math:`K_1(x)`.
|
||||
k1e -- Exponentially scaled modified Bessel function K of order 1.
|
||||
|
||||
Integrals of Bessel functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
itj0y0 -- Integrals of Bessel functions of order 0.
|
||||
it2j0y0 -- Integrals related to Bessel functions of order 0.
|
||||
iti0k0 -- Integrals of modified Bessel functions of order 0.
|
||||
it2i0k0 -- Integrals related to modified Bessel functions of order 0.
|
||||
besselpoly -- Weighted integral of a Bessel function.
|
||||
|
||||
Derivatives of Bessel functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
jvp -- Compute nth derivative of Bessel function Jv(z) with respect to `z`.
|
||||
yvp -- Compute nth derivative of Bessel function Yv(z) with respect to `z`.
|
||||
kvp -- Compute nth derivative of real-order modified Bessel function Kv(z)
|
||||
ivp -- Compute nth derivative of modified Bessel function Iv(z) with respect to `z`.
|
||||
h1vp -- Compute nth derivative of Hankel function H1v(z) with respect to `z`.
|
||||
h2vp -- Compute nth derivative of Hankel function H2v(z) with respect to `z`.
|
||||
|
||||
Spherical Bessel functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
spherical_jn -- Spherical Bessel function of the first kind or its derivative.
|
||||
spherical_yn -- Spherical Bessel function of the second kind or its derivative.
|
||||
spherical_in -- Modified spherical Bessel function of the first kind or its derivative.
|
||||
spherical_kn -- Modified spherical Bessel function of the second kind or its derivative.
|
||||
|
||||
Riccati-Bessel functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following functions do not accept NumPy arrays (they are not
|
||||
universal functions):
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
riccati_jn -- Compute Ricatti-Bessel function of the first kind and its derivative.
|
||||
riccati_yn -- Compute Ricatti-Bessel function of the second kind and its derivative.
|
||||
|
||||
Struve functions
|
||||
----------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
struve -- Struve function.
|
||||
modstruve -- Modified Struve function.
|
||||
itstruve0 -- Integral of the Struve function of order 0.
|
||||
it2struve0 -- Integral related to the Struve function of order 0.
|
||||
itmodstruve0 -- Integral of the modified Struve function of order 0.
|
||||
|
||||
|
||||
Raw statistical functions
|
||||
-------------------------
|
||||
|
||||
.. seealso:: :mod:`scipy.stats`: Friendly versions of these functions.
|
||||
|
||||
Binomial distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
bdtr -- Binomial distribution cumulative distribution function.
|
||||
bdtrc -- Binomial distribution survival function.
|
||||
bdtri -- Inverse function to `bdtr` with respect to `p`.
|
||||
bdtrik -- Inverse function to `bdtr` with respect to `k`.
|
||||
bdtrin -- Inverse function to `bdtr` with respect to `n`.
|
||||
|
||||
Beta distribution
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
btdtria -- Inverse of `betainc` with respect to `a`.
|
||||
btdtrib -- Inverse of `betainc` with respect to `b`.
|
||||
|
||||
F distribution
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
fdtr -- F cumulative distribution function.
|
||||
fdtrc -- F survival function.
|
||||
fdtri -- The `p`-th quantile of the F-distribution.
|
||||
fdtridfd -- Inverse to `fdtr` vs dfd.
|
||||
|
||||
Gamma distribution
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
gdtr -- Gamma distribution cumulative distribution function.
|
||||
gdtrc -- Gamma distribution survival function.
|
||||
gdtria -- Inverse of `gdtr` vs a.
|
||||
gdtrib -- Inverse of `gdtr` vs b.
|
||||
gdtrix -- Inverse of `gdtr` vs x.
|
||||
|
||||
Negative binomial distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
nbdtr -- Negative binomial cumulative distribution function.
|
||||
nbdtrc -- Negative binomial survival function.
|
||||
nbdtri -- Inverse of `nbdtr` vs `p`.
|
||||
nbdtrik -- Inverse of `nbdtr` vs `k`.
|
||||
nbdtrin -- Inverse of `nbdtr` vs `n`.
|
||||
|
||||
Noncentral F distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
ncfdtr -- Cumulative distribution function of the non-central F distribution.
|
||||
ncfdtridfd -- Calculate degrees of freedom (denominator) for the noncentral F-distribution.
|
||||
ncfdtridfn -- Calculate degrees of freedom (numerator) for the noncentral F-distribution.
|
||||
ncfdtri -- Inverse cumulative distribution function of the non-central F distribution.
|
||||
ncfdtrinc -- Calculate non-centrality parameter for non-central F distribution.
|
||||
|
||||
Noncentral t distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
nctdtr -- Cumulative distribution function of the non-central `t` distribution.
|
||||
nctdtridf -- Calculate degrees of freedom for non-central t distribution.
|
||||
nctdtrit -- Inverse cumulative distribution function of the non-central t distribution.
|
||||
nctdtrinc -- Calculate non-centrality parameter for non-central t distribution.
|
||||
|
||||
Normal distribution
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
nrdtrimn -- Calculate mean of normal distribution given other params.
|
||||
nrdtrisd -- Calculate standard deviation of normal distribution given other params.
|
||||
ndtr -- Normal cumulative distribution function.
|
||||
log_ndtr -- Logarithm of normal cumulative distribution function.
|
||||
ndtri -- Inverse of `ndtr` vs x.
|
||||
ndtri_exp -- Inverse of `log_ndtr` vs x.
|
||||
|
||||
Poisson distribution
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
pdtr -- Poisson cumulative distribution function.
|
||||
pdtrc -- Poisson survival function.
|
||||
pdtri -- Inverse to `pdtr` vs m.
|
||||
pdtrik -- Inverse to `pdtr` vs k.
|
||||
|
||||
Student t distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
stdtr -- Student t distribution cumulative distribution function.
|
||||
stdtridf -- Inverse of `stdtr` vs df.
|
||||
stdtrit -- Inverse of `stdtr` vs `t`.
|
||||
|
||||
Chi square distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
chdtr -- Chi square cumulative distribution function.
|
||||
chdtrc -- Chi square survival function.
|
||||
chdtri -- Inverse to `chdtrc`.
|
||||
chdtriv -- Inverse to `chdtr` vs `v`.
|
||||
|
||||
Non-central chi square distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
chndtr -- Non-central chi square cumulative distribution function.
|
||||
chndtridf -- Inverse to `chndtr` vs `df`.
|
||||
chndtrinc -- Inverse to `chndtr` vs `nc`.
|
||||
chndtrix -- Inverse to `chndtr` vs `x`.
|
||||
|
||||
Kolmogorov distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
smirnov -- Kolmogorov-Smirnov complementary cumulative distribution function.
|
||||
smirnovi -- Inverse to `smirnov`.
|
||||
kolmogorov -- Complementary cumulative distribution function of Kolmogorov distribution.
|
||||
kolmogi -- Inverse function to `kolmogorov`.
|
||||
|
||||
Box-Cox transformation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
boxcox -- Compute the Box-Cox transformation.
|
||||
boxcox1p -- Compute the Box-Cox transformation of 1 + `x`.
|
||||
inv_boxcox -- Compute the inverse of the Box-Cox transformation.
|
||||
inv_boxcox1p -- Compute the inverse of the Box-Cox transformation.
|
||||
|
||||
|
||||
Sigmoidal functions
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
logit -- Logit ufunc for ndarrays.
|
||||
expit -- Logistic sigmoid function.
|
||||
log_expit -- Logarithm of the logistic sigmoid function.
|
||||
|
||||
Miscellaneous
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
tklmbda -- Tukey-Lambda cumulative distribution function.
|
||||
owens_t -- Owen's T Function.
|
||||
|
||||
|
||||
Information Theory functions
|
||||
----------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
entr -- Elementwise function for computing entropy.
|
||||
rel_entr -- Elementwise function for computing relative entropy.
|
||||
kl_div -- Elementwise function for computing Kullback-Leibler divergence.
|
||||
huber -- Huber loss function.
|
||||
pseudo_huber -- Pseudo-Huber loss function.
|
||||
|
||||
|
||||
Gamma and related functions
|
||||
---------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
gamma -- Gamma function.
|
||||
gammaln -- Logarithm of the absolute value of the Gamma function for real inputs.
|
||||
loggamma -- Principal branch of the logarithm of the Gamma function.
|
||||
gammasgn -- Sign of the gamma function.
|
||||
gammainc -- Regularized lower incomplete gamma function.
|
||||
gammaincinv -- Inverse to `gammainc`.
|
||||
gammaincc -- Regularized upper incomplete gamma function.
|
||||
gammainccinv -- Inverse to `gammaincc`.
|
||||
beta -- Beta function.
|
||||
betaln -- Natural logarithm of absolute value of beta function.
|
||||
betainc -- Incomplete beta integral.
|
||||
betaincc -- Complemented incomplete beta integral.
|
||||
betaincinv -- Inverse function to beta integral.
|
||||
betainccinv -- Inverse of the complemented incomplete beta integral.
|
||||
psi -- The digamma function.
|
||||
rgamma -- Gamma function inverted.
|
||||
polygamma -- Polygamma function n.
|
||||
multigammaln -- Returns the log of multivariate gamma, also sometimes called the generalized gamma.
|
||||
digamma -- psi(x[, out]).
|
||||
poch -- Rising factorial (z)_m.
|
||||
|
||||
Error function and Fresnel integrals
|
||||
------------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
erf -- Returns the error function of complex argument.
|
||||
erfc -- Complementary error function, ``1 - erf(x)``.
|
||||
erfcx -- Scaled complementary error function, ``exp(x**2) * erfc(x)``.
|
||||
erfi -- Imaginary error function, ``-i erf(i z)``.
|
||||
erfinv -- Inverse function for erf.
|
||||
erfcinv -- Inverse function for erfc.
|
||||
wofz -- Faddeeva function.
|
||||
dawsn -- Dawson's integral.
|
||||
fresnel -- Fresnel sin and cos integrals.
|
||||
fresnel_zeros -- Compute nt complex zeros of sine and cosine Fresnel integrals S(z) and C(z).
|
||||
modfresnelp -- Modified Fresnel positive integrals.
|
||||
modfresnelm -- Modified Fresnel negative integrals.
|
||||
voigt_profile -- Voigt profile.
|
||||
|
||||
The following functions do not accept NumPy arrays (they are not
|
||||
universal functions):
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
erf_zeros -- Compute nt complex zeros of error function erf(z).
|
||||
fresnelc_zeros -- Compute nt complex zeros of cosine Fresnel integral C(z).
|
||||
fresnels_zeros -- Compute nt complex zeros of sine Fresnel integral S(z).
|
||||
|
||||
Legendre functions
|
||||
------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
legendre_p -- Legendre polynomials of the first kind.
|
||||
legendre_p_all -- All Legendre polynomials of the first kind up to a specified order.
|
||||
assoc_legendre_p -- Associated Legendre polynomials of the first kind.
|
||||
assoc_legendre_p_all -- All associated Legendre polynomials of the first kind up to a specified order and degree.
|
||||
sph_legendre_p -- Spherical Legendre polynomials of the first kind.
|
||||
sph_legendre_p_all -- All spherical Legendre polynomials of the first kind up to a specified order and degree.
|
||||
sph_harm_y -- Spherical harmonics.
|
||||
sph_harm_y_all -- All spherical harmonics up to a specified order and degree.
|
||||
|
||||
The following functions are in the process of being deprecated in favor of the above,
|
||||
which provide a more flexible and consistent interface.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
lpmv -- Associated Legendre function of integer order and real degree.
|
||||
sph_harm -- Compute spherical harmonics.
|
||||
clpmn -- Associated Legendre function of the first kind for complex arguments.
|
||||
lpn -- Legendre function of the first kind.
|
||||
lqn -- Legendre function of the second kind.
|
||||
lpmn -- Sequence of associated Legendre functions of the first kind.
|
||||
lqmn -- Sequence of associated Legendre functions of the second kind.
|
||||
|
||||
Ellipsoidal harmonics
|
||||
---------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
ellip_harm -- Ellipsoidal harmonic functions E^p_n(l).
|
||||
ellip_harm_2 -- Ellipsoidal harmonic functions F^p_n(l).
|
||||
ellip_normal -- Ellipsoidal harmonic normalization constants gamma^p_n.
|
||||
|
||||
Orthogonal polynomials
|
||||
----------------------
|
||||
|
||||
The following functions evaluate values of orthogonal polynomials:
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
assoc_laguerre -- Compute the generalized (associated) Laguerre polynomial of degree n and order k.
|
||||
eval_legendre -- Evaluate Legendre polynomial at a point.
|
||||
eval_chebyt -- Evaluate Chebyshev polynomial of the first kind at a point.
|
||||
eval_chebyu -- Evaluate Chebyshev polynomial of the second kind at a point.
|
||||
eval_chebyc -- Evaluate Chebyshev polynomial of the first kind on [-2, 2] at a point.
|
||||
eval_chebys -- Evaluate Chebyshev polynomial of the second kind on [-2, 2] at a point.
|
||||
eval_jacobi -- Evaluate Jacobi polynomial at a point.
|
||||
eval_laguerre -- Evaluate Laguerre polynomial at a point.
|
||||
eval_genlaguerre -- Evaluate generalized Laguerre polynomial at a point.
|
||||
eval_hermite -- Evaluate physicist's Hermite polynomial at a point.
|
||||
eval_hermitenorm -- Evaluate probabilist's (normalized) Hermite polynomial at a point.
|
||||
eval_gegenbauer -- Evaluate Gegenbauer polynomial at a point.
|
||||
eval_sh_legendre -- Evaluate shifted Legendre polynomial at a point.
|
||||
eval_sh_chebyt -- Evaluate shifted Chebyshev polynomial of the first kind at a point.
|
||||
eval_sh_chebyu -- Evaluate shifted Chebyshev polynomial of the second kind at a point.
|
||||
eval_sh_jacobi -- Evaluate shifted Jacobi polynomial at a point.
|
||||
|
||||
The following functions compute roots and quadrature weights for
|
||||
orthogonal polynomials:
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
roots_legendre -- Gauss-Legendre quadrature.
|
||||
roots_chebyt -- Gauss-Chebyshev (first kind) quadrature.
|
||||
roots_chebyu -- Gauss-Chebyshev (second kind) quadrature.
|
||||
roots_chebyc -- Gauss-Chebyshev (first kind) quadrature.
|
||||
roots_chebys -- Gauss-Chebyshev (second kind) quadrature.
|
||||
roots_jacobi -- Gauss-Jacobi quadrature.
|
||||
roots_laguerre -- Gauss-Laguerre quadrature.
|
||||
roots_genlaguerre -- Gauss-generalized Laguerre quadrature.
|
||||
roots_hermite -- Gauss-Hermite (physicist's) quadrature.
|
||||
roots_hermitenorm -- Gauss-Hermite (statistician's) quadrature.
|
||||
roots_gegenbauer -- Gauss-Gegenbauer quadrature.
|
||||
roots_sh_legendre -- Gauss-Legendre (shifted) quadrature.
|
||||
roots_sh_chebyt -- Gauss-Chebyshev (first kind, shifted) quadrature.
|
||||
roots_sh_chebyu -- Gauss-Chebyshev (second kind, shifted) quadrature.
|
||||
roots_sh_jacobi -- Gauss-Jacobi (shifted) quadrature.
|
||||
|
||||
The functions below, in turn, return the polynomial coefficients in
|
||||
``orthopoly1d`` objects, which function similarly as `numpy.poly1d`.
|
||||
The ``orthopoly1d`` class also has an attribute ``weights``, which returns
|
||||
the roots, weights, and total weights for the appropriate form of Gaussian
|
||||
quadrature. These are returned in an ``n x 3`` array with roots in the first
|
||||
column, weights in the second column, and total weights in the final column.
|
||||
Note that ``orthopoly1d`` objects are converted to `~numpy.poly1d` when doing
|
||||
arithmetic, and lose information of the original orthogonal polynomial.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
legendre -- Legendre polynomial.
|
||||
chebyt -- Chebyshev polynomial of the first kind.
|
||||
chebyu -- Chebyshev polynomial of the second kind.
|
||||
chebyc -- Chebyshev polynomial of the first kind on :math:`[-2, 2]`.
|
||||
chebys -- Chebyshev polynomial of the second kind on :math:`[-2, 2]`.
|
||||
jacobi -- Jacobi polynomial.
|
||||
laguerre -- Laguerre polynomial.
|
||||
genlaguerre -- Generalized (associated) Laguerre polynomial.
|
||||
hermite -- Physicist's Hermite polynomial.
|
||||
hermitenorm -- Normalized (probabilist's) Hermite polynomial.
|
||||
gegenbauer -- Gegenbauer (ultraspherical) polynomial.
|
||||
sh_legendre -- Shifted Legendre polynomial.
|
||||
sh_chebyt -- Shifted Chebyshev polynomial of the first kind.
|
||||
sh_chebyu -- Shifted Chebyshev polynomial of the second kind.
|
||||
sh_jacobi -- Shifted Jacobi polynomial.
|
||||
|
||||
.. warning::
|
||||
|
||||
Computing values of high-order polynomials (around ``order > 20``) using
|
||||
polynomial coefficients is numerically unstable. To evaluate polynomial
|
||||
values, the ``eval_*`` functions should be used instead.
|
||||
|
||||
|
||||
Hypergeometric functions
|
||||
------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
hyp2f1 -- Gauss hypergeometric function 2F1(a, b; c; z).
|
||||
hyp1f1 -- Confluent hypergeometric function 1F1(a, b; x).
|
||||
hyperu -- Confluent hypergeometric function U(a, b, x) of the second kind.
|
||||
hyp0f1 -- Confluent hypergeometric limit function 0F1.
|
||||
|
||||
|
||||
Parabolic cylinder functions
|
||||
----------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
pbdv -- Parabolic cylinder function D.
|
||||
pbvv -- Parabolic cylinder function V.
|
||||
pbwa -- Parabolic cylinder function W.
|
||||
|
||||
The following functions do not accept NumPy arrays (they are not
|
||||
universal functions):
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
pbdv_seq -- Parabolic cylinder functions Dv(x) and derivatives.
|
||||
pbvv_seq -- Parabolic cylinder functions Vv(x) and derivatives.
|
||||
pbdn_seq -- Parabolic cylinder functions Dn(z) and derivatives.
|
||||
|
||||
Mathieu and related functions
|
||||
-----------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
mathieu_a -- Characteristic value of even Mathieu functions.
|
||||
mathieu_b -- Characteristic value of odd Mathieu functions.
|
||||
|
||||
The following functions do not accept NumPy arrays (they are not
|
||||
universal functions):
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
mathieu_even_coef -- Fourier coefficients for even Mathieu and modified Mathieu functions.
|
||||
mathieu_odd_coef -- Fourier coefficients for even Mathieu and modified Mathieu functions.
|
||||
|
||||
The following return both function and first derivative:
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
mathieu_cem -- Even Mathieu function and its derivative.
|
||||
mathieu_sem -- Odd Mathieu function and its derivative.
|
||||
mathieu_modcem1 -- Even modified Mathieu function of the first kind and its derivative.
|
||||
mathieu_modcem2 -- Even modified Mathieu function of the second kind and its derivative.
|
||||
mathieu_modsem1 -- Odd modified Mathieu function of the first kind and its derivative.
|
||||
mathieu_modsem2 -- Odd modified Mathieu function of the second kind and its derivative.
|
||||
|
||||
Spheroidal wave functions
|
||||
-------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
pro_ang1 -- Prolate spheroidal angular function of the first kind and its derivative.
|
||||
pro_rad1 -- Prolate spheroidal radial function of the first kind and its derivative.
|
||||
pro_rad2 -- Prolate spheroidal radial function of the second kind and its derivative.
|
||||
obl_ang1 -- Oblate spheroidal angular function of the first kind and its derivative.
|
||||
obl_rad1 -- Oblate spheroidal radial function of the first kind and its derivative.
|
||||
obl_rad2 -- Oblate spheroidal radial function of the second kind and its derivative.
|
||||
pro_cv -- Characteristic value of prolate spheroidal function.
|
||||
obl_cv -- Characteristic value of oblate spheroidal function.
|
||||
pro_cv_seq -- Characteristic values for prolate spheroidal wave functions.
|
||||
obl_cv_seq -- Characteristic values for oblate spheroidal wave functions.
|
||||
|
||||
The following functions require pre-computed characteristic value:
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
pro_ang1_cv -- Prolate spheroidal angular function pro_ang1 for precomputed characteristic value.
|
||||
pro_rad1_cv -- Prolate spheroidal radial function pro_rad1 for precomputed characteristic value.
|
||||
pro_rad2_cv -- Prolate spheroidal radial function pro_rad2 for precomputed characteristic value.
|
||||
obl_ang1_cv -- Oblate spheroidal angular function obl_ang1 for precomputed characteristic value.
|
||||
obl_rad1_cv -- Oblate spheroidal radial function obl_rad1 for precomputed characteristic value.
|
||||
obl_rad2_cv -- Oblate spheroidal radial function obl_rad2 for precomputed characteristic value.
|
||||
|
||||
Kelvin functions
|
||||
----------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
kelvin -- Kelvin functions as complex numbers.
|
||||
kelvin_zeros -- Compute nt zeros of all Kelvin functions.
|
||||
ber -- Kelvin function ber.
|
||||
bei -- Kelvin function bei
|
||||
berp -- Derivative of the Kelvin function `ber`.
|
||||
beip -- Derivative of the Kelvin function `bei`.
|
||||
ker -- Kelvin function ker.
|
||||
kei -- Kelvin function ker.
|
||||
kerp -- Derivative of the Kelvin function ker.
|
||||
keip -- Derivative of the Kelvin function kei.
|
||||
|
||||
The following functions do not accept NumPy arrays (they are not
|
||||
universal functions):
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
ber_zeros -- Compute nt zeros of the Kelvin function ber(x).
|
||||
bei_zeros -- Compute nt zeros of the Kelvin function bei(x).
|
||||
berp_zeros -- Compute nt zeros of the Kelvin function ber'(x).
|
||||
beip_zeros -- Compute nt zeros of the Kelvin function bei'(x).
|
||||
ker_zeros -- Compute nt zeros of the Kelvin function ker(x).
|
||||
kei_zeros -- Compute nt zeros of the Kelvin function kei(x).
|
||||
kerp_zeros -- Compute nt zeros of the Kelvin function ker'(x).
|
||||
keip_zeros -- Compute nt zeros of the Kelvin function kei'(x).
|
||||
|
||||
Combinatorics
|
||||
-------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
comb -- The number of combinations of N things taken k at a time.
|
||||
perm -- Permutations of N things taken k at a time, i.e., k-permutations of N.
|
||||
stirling2 -- Stirling numbers of the second kind.
|
||||
|
||||
Lambert W and related functions
|
||||
-------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
lambertw -- Lambert W function.
|
||||
wrightomega -- Wright Omega function.
|
||||
|
||||
Other special functions
|
||||
-----------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
agm -- Arithmetic, Geometric Mean.
|
||||
bernoulli -- Bernoulli numbers B0..Bn (inclusive).
|
||||
binom -- Binomial coefficient
|
||||
diric -- Periodic sinc function, also called the Dirichlet function.
|
||||
euler -- Euler numbers E0..En (inclusive).
|
||||
expn -- Exponential integral E_n.
|
||||
exp1 -- Exponential integral E_1 of complex argument z.
|
||||
expi -- Exponential integral Ei.
|
||||
factorial -- The factorial of a number or array of numbers.
|
||||
factorial2 -- Double factorial.
|
||||
factorialk -- Multifactorial of n of order k, n(!!...!).
|
||||
shichi -- Hyperbolic sine and cosine integrals.
|
||||
sici -- Sine and cosine integrals.
|
||||
softmax -- Softmax function.
|
||||
log_softmax -- Logarithm of softmax function.
|
||||
spence -- Spence's function, also known as the dilogarithm.
|
||||
zeta -- Riemann zeta function.
|
||||
zetac -- Riemann zeta function minus 1.
|
||||
softplus -- Softplus function.
|
||||
|
||||
Convenience functions
|
||||
---------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
cbrt -- Cube root of `x`.
|
||||
exp10 -- 10**x.
|
||||
exp2 -- 2**x.
|
||||
radian -- Convert from degrees to radians.
|
||||
cosdg -- Cosine of the angle `x` given in degrees.
|
||||
sindg -- Sine of angle given in degrees.
|
||||
tandg -- Tangent of angle x given in degrees.
|
||||
cotdg -- Cotangent of the angle `x` given in degrees.
|
||||
log1p -- Calculates log(1+x) for use when `x` is near zero.
|
||||
expm1 -- ``exp(x) - 1`` for use when `x` is near zero.
|
||||
cosm1 -- ``cos(x) - 1`` for use when `x` is near zero.
|
||||
powm1 -- ``x**y - 1`` for use when `y` is near zero or `x` is near 1.
|
||||
round -- Round to nearest integer.
|
||||
xlogy -- Compute ``x*log(y)`` so that the result is 0 if ``x = 0``.
|
||||
xlog1py -- Compute ``x*log1p(y)`` so that the result is 0 if ``x = 0``.
|
||||
logsumexp -- Compute the log of the sum of exponentials of input elements.
|
||||
exprel -- Relative error exponential, (exp(x)-1)/x, for use when `x` is near zero.
|
||||
sinc -- Return the sinc function.
|
||||
|
||||
""" # noqa: E501
|
||||
|
||||
|
||||
from ._sf_error import SpecialFunctionWarning, SpecialFunctionError
|
||||
|
||||
from . import _ufuncs
|
||||
from ._ufuncs import *
|
||||
|
||||
# Replace some function definitions from _ufuncs to add Array API support
|
||||
from ._support_alternative_backends import *
|
||||
|
||||
from . import _basic
|
||||
from ._basic import *
|
||||
|
||||
from ._logsumexp import logsumexp, softmax, log_softmax
|
||||
|
||||
from . import _multiufuncs
|
||||
from ._multiufuncs import *
|
||||
|
||||
from . import _orthogonal
|
||||
from ._orthogonal import *
|
||||
|
||||
from ._spfun_stats import multigammaln
|
||||
from ._ellip_harm import (
|
||||
ellip_harm,
|
||||
ellip_harm_2,
|
||||
ellip_normal
|
||||
)
|
||||
from ._lambertw import lambertw
|
||||
from ._spherical_bessel import (
|
||||
spherical_jn,
|
||||
spherical_yn,
|
||||
spherical_in,
|
||||
spherical_kn
|
||||
)
|
||||
|
||||
# Deprecated namespaces, to be removed in v2.0.0
|
||||
from . import add_newdocs, basic, orthogonal, specfun, sf_error, spfun_stats
|
||||
|
||||
# We replace some function definitions from _ufuncs with those from
|
||||
# _support_alternative_backends above, but those are all listed in _ufuncs.__all__,
|
||||
# so there is no need to consider _support_alternative_backends.__all__ here.
|
||||
__all__ = _ufuncs.__all__ + _basic.__all__ + _orthogonal.__all__ + _multiufuncs.__all__
|
||||
__all__ += [
|
||||
'SpecialFunctionWarning',
|
||||
'SpecialFunctionError',
|
||||
'logsumexp',
|
||||
'softmax',
|
||||
'log_softmax',
|
||||
'multigammaln',
|
||||
'ellip_harm',
|
||||
'ellip_harm_2',
|
||||
'ellip_normal',
|
||||
'lambertw',
|
||||
'spherical_jn',
|
||||
'spherical_yn',
|
||||
'spherical_in',
|
||||
'spherical_kn',
|
||||
]
|
||||
|
||||
from scipy._lib._testutils import PytestTester
|
||||
test = PytestTester(__name__)
|
||||
del PytestTester
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,214 @@
|
||||
import numpy as np
|
||||
|
||||
from ._ufuncs import _ellip_harm
|
||||
from ._ellip_harm_2 import _ellipsoid, _ellipsoid_norm
|
||||
|
||||
|
||||
def ellip_harm(h2, k2, n, p, s, signm=1, signn=1):
|
||||
r"""
|
||||
Ellipsoidal harmonic functions E^p_n(l)
|
||||
|
||||
These are also known as Lame functions of the first kind, and are
|
||||
solutions to the Lame equation:
|
||||
|
||||
.. math:: (s^2 - h^2)(s^2 - k^2)E''(s)
|
||||
+ s(2s^2 - h^2 - k^2)E'(s) + (a - q s^2)E(s) = 0
|
||||
|
||||
where :math:`q = (n+1)n` and :math:`a` is the eigenvalue (not
|
||||
returned) corresponding to the solutions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
h2 : float
|
||||
``h**2``
|
||||
k2 : float
|
||||
``k**2``; should be larger than ``h**2``
|
||||
n : int
|
||||
Degree
|
||||
s : float
|
||||
Coordinate
|
||||
p : int
|
||||
Order, can range between [1,2n+1]
|
||||
signm : {1, -1}, optional
|
||||
Sign of prefactor of functions. Can be +/-1. See Notes.
|
||||
signn : {1, -1}, optional
|
||||
Sign of prefactor of functions. Can be +/-1. See Notes.
|
||||
|
||||
Returns
|
||||
-------
|
||||
E : float
|
||||
the harmonic :math:`E^p_n(s)`
|
||||
|
||||
See Also
|
||||
--------
|
||||
ellip_harm_2, ellip_normal
|
||||
|
||||
Notes
|
||||
-----
|
||||
The geometric interpretation of the ellipsoidal functions is
|
||||
explained in [2]_, [3]_, [4]_. The `signm` and `signn` arguments control the
|
||||
sign of prefactors for functions according to their type::
|
||||
|
||||
K : +1
|
||||
L : signm
|
||||
M : signn
|
||||
N : signm*signn
|
||||
|
||||
.. versionadded:: 0.15.0
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Digital Library of Mathematical Functions 29.12
|
||||
https://dlmf.nist.gov/29.12
|
||||
.. [2] Bardhan and Knepley, "Computational science and
|
||||
re-discovery: open-source implementations of
|
||||
ellipsoidal harmonics for problems in potential theory",
|
||||
Comput. Sci. Disc. 5, 014006 (2012)
|
||||
:doi:`10.1088/1749-4699/5/1/014006`.
|
||||
.. [3] David J.and Dechambre P, "Computation of Ellipsoidal
|
||||
Gravity Field Harmonics for small solar system bodies"
|
||||
pp. 30-36, 2000
|
||||
.. [4] George Dassios, "Ellipsoidal Harmonics: Theory and Applications"
|
||||
pp. 418, 2012
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy.special import ellip_harm
|
||||
>>> w = ellip_harm(5,8,1,1,2.5)
|
||||
>>> w
|
||||
2.5
|
||||
|
||||
Check that the functions indeed are solutions to the Lame equation:
|
||||
|
||||
>>> import numpy as np
|
||||
>>> from scipy.interpolate import UnivariateSpline
|
||||
>>> def eigenvalue(f, df, ddf):
|
||||
... r = (((s**2 - h**2) * (s**2 - k**2) * ddf
|
||||
... + s * (2*s**2 - h**2 - k**2) * df
|
||||
... - n * (n + 1)*s**2*f) / f)
|
||||
... return -r.mean(), r.std()
|
||||
>>> s = np.linspace(0.1, 10, 200)
|
||||
>>> k, h, n, p = 8.0, 2.2, 3, 2
|
||||
>>> E = ellip_harm(h**2, k**2, n, p, s)
|
||||
>>> E_spl = UnivariateSpline(s, E)
|
||||
>>> a, a_err = eigenvalue(E_spl(s), E_spl(s,1), E_spl(s,2))
|
||||
>>> a, a_err
|
||||
(583.44366156701483, 6.4580890640310646e-11)
|
||||
|
||||
""" # noqa: E501
|
||||
return _ellip_harm(h2, k2, n, p, s, signm, signn)
|
||||
|
||||
|
||||
_ellip_harm_2_vec = np.vectorize(_ellipsoid, otypes='d')
|
||||
|
||||
|
||||
def ellip_harm_2(h2, k2, n, p, s):
|
||||
r"""
|
||||
Ellipsoidal harmonic functions F^p_n(l)
|
||||
|
||||
These are also known as Lame functions of the second kind, and are
|
||||
solutions to the Lame equation:
|
||||
|
||||
.. math:: (s^2 - h^2)(s^2 - k^2)F''(s)
|
||||
+ s(2s^2 - h^2 - k^2)F'(s) + (a - q s^2)F(s) = 0
|
||||
|
||||
where :math:`q = (n+1)n` and :math:`a` is the eigenvalue (not
|
||||
returned) corresponding to the solutions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
h2 : float
|
||||
``h**2``
|
||||
k2 : float
|
||||
``k**2``; should be larger than ``h**2``
|
||||
n : int
|
||||
Degree.
|
||||
p : int
|
||||
Order, can range between [1,2n+1].
|
||||
s : float
|
||||
Coordinate
|
||||
|
||||
Returns
|
||||
-------
|
||||
F : float
|
||||
The harmonic :math:`F^p_n(s)`
|
||||
|
||||
See Also
|
||||
--------
|
||||
ellip_harm, ellip_normal
|
||||
|
||||
Notes
|
||||
-----
|
||||
Lame functions of the second kind are related to the functions of the first kind:
|
||||
|
||||
.. math::
|
||||
|
||||
F^p_n(s)=(2n + 1)E^p_n(s)\int_{0}^{1/s}
|
||||
\frac{du}{(E^p_n(1/u))^2\sqrt{(1-u^2k^2)(1-u^2h^2)}}
|
||||
|
||||
.. versionadded:: 0.15.0
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy.special import ellip_harm_2
|
||||
>>> w = ellip_harm_2(5,8,2,1,10)
|
||||
>>> w
|
||||
0.00108056853382
|
||||
|
||||
"""
|
||||
with np.errstate(all='ignore'):
|
||||
return _ellip_harm_2_vec(h2, k2, n, p, s)
|
||||
|
||||
|
||||
def _ellip_normal_vec(h2, k2, n, p):
|
||||
return _ellipsoid_norm(h2, k2, n, p)
|
||||
|
||||
|
||||
_ellip_normal_vec = np.vectorize(_ellip_normal_vec, otypes='d')
|
||||
|
||||
|
||||
def ellip_normal(h2, k2, n, p):
|
||||
r"""
|
||||
Ellipsoidal harmonic normalization constants gamma^p_n
|
||||
|
||||
The normalization constant is defined as
|
||||
|
||||
.. math::
|
||||
|
||||
\gamma^p_n=8\int_{0}^{h}dx\int_{h}^{k}dy
|
||||
\frac{(y^2-x^2)(E^p_n(y)E^p_n(x))^2}{\sqrt((k^2-y^2)(y^2-h^2)(h^2-x^2)(k^2-x^2)}
|
||||
|
||||
Parameters
|
||||
----------
|
||||
h2 : float
|
||||
``h**2``
|
||||
k2 : float
|
||||
``k**2``; should be larger than ``h**2``
|
||||
n : int
|
||||
Degree.
|
||||
p : int
|
||||
Order, can range between [1,2n+1].
|
||||
|
||||
Returns
|
||||
-------
|
||||
gamma : float
|
||||
The normalization constant :math:`\gamma^p_n`
|
||||
|
||||
See Also
|
||||
--------
|
||||
ellip_harm, ellip_harm_2
|
||||
|
||||
Notes
|
||||
-----
|
||||
.. versionadded:: 0.15.0
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy.special import ellip_normal
|
||||
>>> w = ellip_normal(5,8,3,7)
|
||||
>>> w
|
||||
1723.38796997
|
||||
|
||||
"""
|
||||
with np.errstate(all='ignore'):
|
||||
return _ellip_normal_vec(h2, k2, n, p)
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
import math
|
||||
import operator
|
||||
|
||||
def _nonneg_int_or_fail(n, var_name, strict=True):
|
||||
try:
|
||||
if strict:
|
||||
# Raises an exception if float
|
||||
n = operator.index(n)
|
||||
elif n == math.floor(n):
|
||||
n = int(n)
|
||||
else:
|
||||
raise ValueError()
|
||||
if n < 0:
|
||||
raise ValueError()
|
||||
except (ValueError, TypeError) as err:
|
||||
raise err.__class__(f"{var_name} must be a non-negative integer") from err
|
||||
return n
|
||||
@@ -0,0 +1,149 @@
|
||||
from ._ufuncs import _lambertw
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def lambertw(z, k=0, tol=1e-8):
|
||||
r"""
|
||||
lambertw(z, k=0, tol=1e-8)
|
||||
|
||||
Lambert W function.
|
||||
|
||||
The Lambert W function `W(z)` is defined as the inverse function
|
||||
of ``w * exp(w)``. In other words, the value of ``W(z)`` is
|
||||
such that ``z = W(z) * exp(W(z))`` for any complex number
|
||||
``z``.
|
||||
|
||||
The Lambert W function is a multivalued function with infinitely
|
||||
many branches. Each branch gives a separate solution of the
|
||||
equation ``z = w exp(w)``. Here, the branches are indexed by the
|
||||
integer `k`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
z : array_like
|
||||
Input argument.
|
||||
k : int, optional
|
||||
Branch index.
|
||||
tol : float, optional
|
||||
Evaluation tolerance.
|
||||
|
||||
Returns
|
||||
-------
|
||||
w : array
|
||||
`w` will have the same shape as `z`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
wrightomega : the Wright Omega function
|
||||
|
||||
Notes
|
||||
-----
|
||||
All branches are supported by `lambertw`:
|
||||
|
||||
* ``lambertw(z)`` gives the principal solution (branch 0)
|
||||
* ``lambertw(z, k)`` gives the solution on branch `k`
|
||||
|
||||
The Lambert W function has two partially real branches: the
|
||||
principal branch (`k = 0`) is real for real ``z > -1/e``, and the
|
||||
``k = -1`` branch is real for ``-1/e < z < 0``. All branches except
|
||||
``k = 0`` have a logarithmic singularity at ``z = 0``.
|
||||
|
||||
**Possible issues**
|
||||
|
||||
The evaluation can become inaccurate very close to the branch point
|
||||
at ``-1/e``. In some corner cases, `lambertw` might currently
|
||||
fail to converge, or can end up on the wrong branch.
|
||||
|
||||
**Algorithm**
|
||||
|
||||
Halley's iteration is used to invert ``w * exp(w)``, using a first-order
|
||||
asymptotic approximation (O(log(w)) or `O(w)`) as the initial estimate.
|
||||
|
||||
The definition, implementation and choice of branches is based on [2]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Lambert_W_function
|
||||
.. [2] Corless et al, "On the Lambert W function", Adv. Comp. Math. 5
|
||||
(1996) 329-359.
|
||||
https://cs.uwaterloo.ca/research/tr/1993/03/W.pdf
|
||||
|
||||
Examples
|
||||
--------
|
||||
The Lambert W function is the inverse of ``w exp(w)``:
|
||||
|
||||
>>> import numpy as np
|
||||
>>> from scipy.special import lambertw
|
||||
>>> w = lambertw(1)
|
||||
>>> w
|
||||
(0.56714329040978384+0j)
|
||||
>>> w * np.exp(w)
|
||||
(1.0+0j)
|
||||
|
||||
Any branch gives a valid inverse:
|
||||
|
||||
>>> w = lambertw(1, k=3)
|
||||
>>> w
|
||||
(-2.8535817554090377+17.113535539412148j)
|
||||
>>> w*np.exp(w)
|
||||
(1.0000000000000002+1.609823385706477e-15j)
|
||||
|
||||
**Applications to equation-solving**
|
||||
|
||||
The Lambert W function may be used to solve various kinds of
|
||||
equations. We give two examples here.
|
||||
|
||||
First, the function can be used to solve implicit equations of the
|
||||
form
|
||||
|
||||
:math:`x = a + b e^{c x}`
|
||||
|
||||
for :math:`x`. We assume :math:`c` is not zero. After a little
|
||||
algebra, the equation may be written
|
||||
|
||||
:math:`z e^z = -b c e^{a c}`
|
||||
|
||||
where :math:`z = c (a - x)`. :math:`z` may then be expressed using
|
||||
the Lambert W function
|
||||
|
||||
:math:`z = W(-b c e^{a c})`
|
||||
|
||||
giving
|
||||
|
||||
:math:`x = a - W(-b c e^{a c})/c`
|
||||
|
||||
For example,
|
||||
|
||||
>>> a = 3
|
||||
>>> b = 2
|
||||
>>> c = -0.5
|
||||
|
||||
The solution to :math:`x = a + b e^{c x}` is:
|
||||
|
||||
>>> x = a - lambertw(-b*c*np.exp(a*c))/c
|
||||
>>> x
|
||||
(3.3707498368978794+0j)
|
||||
|
||||
Verify that it solves the equation:
|
||||
|
||||
>>> a + b*np.exp(c*x)
|
||||
(3.37074983689788+0j)
|
||||
|
||||
The Lambert W function may also be used find the value of the infinite
|
||||
power tower :math:`z^{z^{z^{\ldots}}}`:
|
||||
|
||||
>>> def tower(z, n):
|
||||
... if n == 0:
|
||||
... return z
|
||||
... return z ** tower(z, n-1)
|
||||
...
|
||||
>>> tower(0.5, 100)
|
||||
0.641185744504986
|
||||
>>> -lambertw(-np.log(0.5)) / np.log(0.5)
|
||||
(0.64118574450498589+0j)
|
||||
"""
|
||||
# TODO: special expert should inspect this
|
||||
# interception; better place to do it?
|
||||
k = np.asarray(k, dtype=np.dtype("long"))
|
||||
return _lambertw(z, k, tol)
|
||||
@@ -0,0 +1,426 @@
|
||||
import numpy as np
|
||||
from scipy._lib._array_api import (
|
||||
array_namespace,
|
||||
xp_capabilities,
|
||||
xp_device,
|
||||
xp_size,
|
||||
xp_promote,
|
||||
xp_float_to_complex,
|
||||
)
|
||||
from scipy._lib import array_api_extra as xpx
|
||||
|
||||
__all__ = ["logsumexp", "softmax", "log_softmax"]
|
||||
|
||||
|
||||
@xp_capabilities()
|
||||
def logsumexp(a, axis=None, b=None, keepdims=False, return_sign=False):
|
||||
"""Compute the log of the sum of exponentials of input elements.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : array_like
|
||||
Input array.
|
||||
axis : None or int or tuple of ints, optional
|
||||
Axis or axes over which the sum is taken. By default `axis` is None,
|
||||
and all elements are summed.
|
||||
|
||||
.. versionadded:: 0.11.0
|
||||
b : array-like, optional
|
||||
Scaling factor for exp(`a`) must be of the same shape as `a` or
|
||||
broadcastable to `a`. These values may be negative in order to
|
||||
implement subtraction.
|
||||
|
||||
.. versionadded:: 0.12.0
|
||||
keepdims : bool, optional
|
||||
If this is set to True, the axes which are reduced are left in the
|
||||
result as dimensions with size one. With this option, the result
|
||||
will broadcast correctly against the original array.
|
||||
|
||||
.. versionadded:: 0.15.0
|
||||
return_sign : bool, optional
|
||||
If this is set to True, the result will be a pair containing sign
|
||||
information; if False, results that are negative will be returned
|
||||
as NaN. Default is False (no sign information).
|
||||
|
||||
.. versionadded:: 0.16.0
|
||||
|
||||
Returns
|
||||
-------
|
||||
res : ndarray
|
||||
The result, ``np.log(np.sum(np.exp(a)))`` calculated in a numerically
|
||||
more stable way. If `b` is given then ``np.log(np.sum(b*np.exp(a)))``
|
||||
is returned. If ``return_sign`` is True, ``res`` contains the log of
|
||||
the absolute value of the argument.
|
||||
sgn : ndarray
|
||||
If ``return_sign`` is True, this will be an array of floating-point
|
||||
numbers matching res containing +1, 0, -1 (for real-valued inputs)
|
||||
or a complex phase (for complex inputs). This gives the sign of the
|
||||
argument of the logarithm in ``res``.
|
||||
If ``return_sign`` is False, only one result is returned.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:data:`numpy.logaddexp`
|
||||
:data:`numpy.logaddexp2`
|
||||
|
||||
Notes
|
||||
-----
|
||||
NumPy has a logaddexp function which is very similar to `logsumexp`, but
|
||||
only handles two arguments. `logaddexp.reduce` is similar to this
|
||||
function, but may be less stable.
|
||||
|
||||
The logarithm is a multivalued function: for each :math:`x` there is an
|
||||
infinite number of :math:`z` such that :math:`exp(z) = x`. The convention
|
||||
is to return the :math:`z` whose imaginary part lies in :math:`(-pi, pi]`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from scipy.special import logsumexp
|
||||
>>> a = np.arange(10)
|
||||
>>> logsumexp(a)
|
||||
9.4586297444267107
|
||||
>>> np.log(np.sum(np.exp(a)))
|
||||
9.4586297444267107
|
||||
|
||||
With weights
|
||||
|
||||
>>> a = np.arange(10)
|
||||
>>> b = np.arange(10, 0, -1)
|
||||
>>> logsumexp(a, b=b)
|
||||
9.9170178533034665
|
||||
>>> np.log(np.sum(b*np.exp(a)))
|
||||
9.9170178533034647
|
||||
|
||||
Returning a sign flag
|
||||
|
||||
>>> logsumexp([1,2],b=[1,-1],return_sign=True)
|
||||
(1.5413248546129181, -1.0)
|
||||
|
||||
Notice that `logsumexp` does not directly support masked arrays. To use it
|
||||
on a masked array, convert the mask into zero weights:
|
||||
|
||||
>>> a = np.ma.array([np.log(2), 2, np.log(3)],
|
||||
... mask=[False, True, False])
|
||||
>>> b = (~a.mask).astype(int)
|
||||
>>> logsumexp(a.data, b=b), np.log(5)
|
||||
1.6094379124341005, 1.6094379124341005
|
||||
|
||||
"""
|
||||
xp = array_namespace(a, b)
|
||||
a, b = xp_promote(a, b, broadcast=True, force_floating=True, xp=xp)
|
||||
a = xpx.atleast_nd(a, ndim=1, xp=xp)
|
||||
b = xpx.atleast_nd(b, ndim=1, xp=xp) if b is not None else b
|
||||
axis = tuple(range(a.ndim)) if axis is None else axis
|
||||
|
||||
if xp_size(a) != 0:
|
||||
with np.errstate(divide='ignore', invalid='ignore', over='ignore'):
|
||||
# Where result is infinite, we use the direct logsumexp calculation to
|
||||
# delegate edge case handling to the behavior of `xp.log` and `xp.exp`,
|
||||
# which should follow the C99 standard for complex values.
|
||||
b_exp_a = xp.exp(a) if b is None else b * xp.exp(a)
|
||||
sum_ = xp.sum(b_exp_a, axis=axis, keepdims=True)
|
||||
sgn_inf = _sign(sum_, xp=xp) if return_sign else None
|
||||
sum_ = xp.abs(sum_) if return_sign else sum_
|
||||
out_inf = xp.log(sum_)
|
||||
|
||||
with np.errstate(divide='ignore', invalid='ignore'): # log of zero is OK
|
||||
out, sgn = _logsumexp(a, b, axis=axis, return_sign=return_sign, xp=xp)
|
||||
|
||||
# Replace infinite results. This probably could be done with an
|
||||
# `apply_where`-like strategy to avoid redundant calculation, but currently
|
||||
# `apply_where` itself is only for elementwise functions.
|
||||
out_finite = xp.isfinite(out)
|
||||
out = xp.where(out_finite, out, out_inf)
|
||||
sgn = xp.where(out_finite, sgn, sgn_inf) if return_sign else sgn
|
||||
else:
|
||||
shape = np.asarray(a.shape) # NumPy is convenient for shape manipulation
|
||||
shape[axis] = 1
|
||||
out = xp.full(tuple(shape), -xp.inf, dtype=a.dtype, device=xp_device(a))
|
||||
sgn = xp.sign(out)
|
||||
|
||||
if xp.isdtype(out.dtype, 'complex floating'):
|
||||
if return_sign:
|
||||
real = xp.real(sgn)
|
||||
imag = xp_float_to_complex(_wrap_radians(xp.imag(sgn), xp=xp), xp=xp)
|
||||
sgn = real + imag*1j
|
||||
else:
|
||||
real = xp.real(out)
|
||||
imag = xp_float_to_complex(_wrap_radians(xp.imag(out), xp=xp), xp=xp)
|
||||
out = real + imag*1j
|
||||
|
||||
# Deal with shape details - reducing dimensions and convert 0-D to scalar for NumPy
|
||||
out = xp.squeeze(out, axis=axis) if not keepdims else out
|
||||
sgn = xp.squeeze(sgn, axis=axis) if (sgn is not None and not keepdims) else sgn
|
||||
out = out[()] if out.ndim == 0 else out
|
||||
sgn = sgn[()] if (sgn is not None and sgn.ndim == 0) else sgn
|
||||
|
||||
return (out, sgn) if return_sign else out
|
||||
|
||||
|
||||
def _wrap_radians(x, *, xp):
|
||||
# Wrap radians to (-pi, pi] interval
|
||||
wrapped = -((-x + xp.pi) % (2 * xp.pi) - xp.pi)
|
||||
# preserve relative precision
|
||||
no_wrap = xp.abs(x) < xp.pi
|
||||
return xp.where(no_wrap, x, wrapped)
|
||||
|
||||
|
||||
def _elements_and_indices_with_max_real(a, *, axis=-1, xp):
|
||||
# This is an array-API compatible `max` function that works something
|
||||
# like `np.max` for complex input. The important part is that it finds
|
||||
# the element with maximum real part. When there are multiple complex values
|
||||
# with this real part, it doesn't matter which we choose.
|
||||
# We could use `argmax` on real component, but array API doesn't yet have
|
||||
# `take_along_axis`, and even if it did, we would have problems with axis tuples.
|
||||
# Feel free to rewrite! It's ugly, but it's not the purpose of the PR, and
|
||||
# it gets the job done.
|
||||
|
||||
if xp.isdtype(a.dtype, "complex floating"):
|
||||
# select all elements with max real part.
|
||||
real_a = xp.real(a)
|
||||
max_ = xp.max(real_a, axis=axis, keepdims=True)
|
||||
mask = real_a == max_
|
||||
|
||||
# Of those, choose one arbitrarily. This is a reasonably
|
||||
# simple, array-API compatible way of doing so that doesn't
|
||||
# have a problem with `axis` being a tuple or None.
|
||||
i = xp.reshape(xp.arange(xp_size(a), device=xp_device(a)), a.shape)
|
||||
i = xpx.at(i, ~mask).set(-1)
|
||||
max_i = xp.max(i, axis=axis, keepdims=True)
|
||||
mask = i == max_i
|
||||
a = xp.where(mask, a, 0.)
|
||||
max_ = xp.sum(a, axis=axis, dtype=a.dtype, keepdims=True)
|
||||
else:
|
||||
max_ = xp.max(a, axis=axis, keepdims=True)
|
||||
mask = a == max_
|
||||
|
||||
return max_, mask
|
||||
|
||||
|
||||
def _sign(x, *, xp):
|
||||
return x / xp.where(x == 0, 1., xp.abs(x))
|
||||
|
||||
|
||||
def _logsumexp(a, b, *, axis, return_sign, xp):
|
||||
# This has been around for about a decade, so let's consider it a feature:
|
||||
# Even if element of `a` is infinite or NaN, it adds nothing to the sum if
|
||||
# the corresponding weight is zero.
|
||||
if b is not None:
|
||||
a = xpx.at(a, b == 0).set(-xp.inf, copy=True)
|
||||
|
||||
# Find element with maximum real part, since this is what affects the magnitude
|
||||
# of the exponential. Possible enhancement: include log of `b` magnitude in `a`.
|
||||
a_max, i_max = _elements_and_indices_with_max_real(a, axis=axis, xp=xp)
|
||||
|
||||
# for precision, these terms are separated out of the main sum.
|
||||
a = xpx.at(a, i_max).set(-xp.inf, copy=True if b is None else None)
|
||||
i_max_dt = xp.astype(i_max, a.dtype)
|
||||
# This is an inefficient way of getting `m` because it is the sum of a sparse
|
||||
# array; however, this is the simplest way I can think of to get the right shape.
|
||||
b_i_max = i_max_dt if b is None else b * i_max_dt
|
||||
m = xp.sum(b_i_max, axis=axis, keepdims=True, dtype=a.dtype)
|
||||
|
||||
# Shift, exponentiate, scale, and sum
|
||||
exp = b * xp.exp(a - a_max) if b is not None else xp.exp(a - a_max)
|
||||
s = xp.sum(exp, axis=axis, keepdims=True, dtype=exp.dtype)
|
||||
s = xp.where(s == 0, s, s/m)
|
||||
|
||||
# Separate sign/magnitude information
|
||||
# Originally, this was only performed if `return_sign=True`.
|
||||
# However, this is also needed if any elements of `m < 0` or `s < -1`.
|
||||
# An improvement would be to perform the calculations only on these entries.
|
||||
|
||||
# Use the numpy>=2.0 convention for sign.
|
||||
# When all array libraries agree, this can become sng = xp.sign(s).
|
||||
sgn = _sign(s + 1, xp=xp) * _sign(m, xp=xp)
|
||||
|
||||
if xp.isdtype(s.dtype, "real floating"):
|
||||
# The log functions need positive arguments
|
||||
s = xp.where(s < -1, -s - 2, s)
|
||||
m = xp.abs(m)
|
||||
else:
|
||||
# `a_max` can have a sign component for complex input
|
||||
sgn = sgn * xp.exp(xp.imag(a_max) * 1.0j)
|
||||
|
||||
# Take log and undo shift
|
||||
out = xp.log1p(s) + xp.log(m) + a_max
|
||||
|
||||
if return_sign:
|
||||
out = xp.real(out)
|
||||
elif xp.isdtype(out.dtype, 'real floating'):
|
||||
out = xpx.at(out)[sgn < 0].set(xp.nan)
|
||||
|
||||
return out, sgn
|
||||
|
||||
|
||||
@xp_capabilities()
|
||||
def softmax(x, axis=None):
|
||||
r"""Compute the softmax function.
|
||||
|
||||
The softmax function transforms each element of a collection by
|
||||
computing the exponential of each element divided by the sum of the
|
||||
exponentials of all the elements. That is, if `x` is a one-dimensional
|
||||
numpy array::
|
||||
|
||||
softmax(x) = np.exp(x)/sum(np.exp(x))
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x : array_like
|
||||
Input array.
|
||||
axis : int or tuple of ints, optional
|
||||
Axis to compute values along. Default is None and softmax will be
|
||||
computed over the entire array `x`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
s : ndarray
|
||||
An array the same shape as `x`. The result will sum to 1 along the
|
||||
specified axis.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The formula for the softmax function :math:`\sigma(x)` for a vector
|
||||
:math:`x = \{x_0, x_1, ..., x_{n-1}\}` is
|
||||
|
||||
.. math:: \sigma(x)_j = \frac{e^{x_j}}{\sum_k e^{x_k}}
|
||||
|
||||
The `softmax` function is the gradient of `logsumexp`.
|
||||
|
||||
The implementation uses shifting to avoid overflow. See [1]_ for more
|
||||
details.
|
||||
|
||||
.. versionadded:: 1.2.0
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] P. Blanchard, D.J. Higham, N.J. Higham, "Accurately computing the
|
||||
log-sum-exp and softmax functions", IMA Journal of Numerical Analysis,
|
||||
Vol.41(4), :doi:`10.1093/imanum/draa038`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from scipy.special import softmax
|
||||
>>> np.set_printoptions(precision=5)
|
||||
|
||||
>>> x = np.array([[1, 0.5, 0.2, 3],
|
||||
... [1, -1, 7, 3],
|
||||
... [2, 12, 13, 3]])
|
||||
...
|
||||
|
||||
Compute the softmax transformation over the entire array.
|
||||
|
||||
>>> m = softmax(x)
|
||||
>>> m
|
||||
array([[ 4.48309e-06, 2.71913e-06, 2.01438e-06, 3.31258e-05],
|
||||
[ 4.48309e-06, 6.06720e-07, 1.80861e-03, 3.31258e-05],
|
||||
[ 1.21863e-05, 2.68421e-01, 7.29644e-01, 3.31258e-05]])
|
||||
|
||||
>>> m.sum()
|
||||
1.0
|
||||
|
||||
Compute the softmax transformation along the first axis (i.e., the
|
||||
columns).
|
||||
|
||||
>>> m = softmax(x, axis=0)
|
||||
|
||||
>>> m
|
||||
array([[ 2.11942e-01, 1.01300e-05, 2.75394e-06, 3.33333e-01],
|
||||
[ 2.11942e-01, 2.26030e-06, 2.47262e-03, 3.33333e-01],
|
||||
[ 5.76117e-01, 9.99988e-01, 9.97525e-01, 3.33333e-01]])
|
||||
|
||||
>>> m.sum(axis=0)
|
||||
array([ 1., 1., 1., 1.])
|
||||
|
||||
Compute the softmax transformation along the second axis (i.e., the rows).
|
||||
|
||||
>>> m = softmax(x, axis=1)
|
||||
>>> m
|
||||
array([[ 1.05877e-01, 6.42177e-02, 4.75736e-02, 7.82332e-01],
|
||||
[ 2.42746e-03, 3.28521e-04, 9.79307e-01, 1.79366e-02],
|
||||
[ 1.22094e-05, 2.68929e-01, 7.31025e-01, 3.31885e-05]])
|
||||
|
||||
>>> m.sum(axis=1)
|
||||
array([ 1., 1., 1.])
|
||||
|
||||
"""
|
||||
xp = array_namespace(x)
|
||||
x = xp.asarray(x)
|
||||
x_max = xp.max(x, axis=axis, keepdims=True)
|
||||
exp_x_shifted = xp.exp(x - x_max)
|
||||
return exp_x_shifted / xp.sum(exp_x_shifted, axis=axis, keepdims=True)
|
||||
|
||||
|
||||
@xp_capabilities()
|
||||
def log_softmax(x, axis=None):
|
||||
r"""Compute the logarithm of the softmax function.
|
||||
|
||||
In principle::
|
||||
|
||||
log_softmax(x) = log(softmax(x))
|
||||
|
||||
but using a more accurate implementation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x : array_like
|
||||
Input array.
|
||||
axis : int or tuple of ints, optional
|
||||
Axis to compute values along. Default is None and softmax will be
|
||||
computed over the entire array `x`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
s : ndarray or scalar
|
||||
An array with the same shape as `x`. Exponential of the result will
|
||||
sum to 1 along the specified axis. If `x` is a scalar, a scalar is
|
||||
returned.
|
||||
|
||||
Notes
|
||||
-----
|
||||
`log_softmax` is more accurate than ``np.log(softmax(x))`` with inputs that
|
||||
make `softmax` saturate (see examples below).
|
||||
|
||||
.. versionadded:: 1.5.0
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from scipy.special import log_softmax
|
||||
>>> from scipy.special import softmax
|
||||
>>> np.set_printoptions(precision=5)
|
||||
|
||||
>>> x = np.array([1000.0, 1.0])
|
||||
|
||||
>>> y = log_softmax(x)
|
||||
>>> y
|
||||
array([ 0., -999.])
|
||||
|
||||
>>> with np.errstate(divide='ignore'):
|
||||
... y = np.log(softmax(x))
|
||||
...
|
||||
>>> y
|
||||
array([ 0., -inf])
|
||||
|
||||
"""
|
||||
xp = array_namespace(x)
|
||||
x = xp.asarray(x)
|
||||
|
||||
x_max = xp.max(x, axis=axis, keepdims=True)
|
||||
|
||||
if x_max.ndim > 0:
|
||||
x_max = xpx.at(x_max, ~xp.isfinite(x_max)).set(0)
|
||||
elif not xp.isfinite(x_max):
|
||||
x_max = 0
|
||||
|
||||
tmp = x - x_max
|
||||
exp_tmp = xp.exp(tmp)
|
||||
|
||||
# suppress warnings about log of zero
|
||||
with np.errstate(divide='ignore'):
|
||||
s = xp.sum(exp_tmp, axis=axis, keepdims=True)
|
||||
out = xp.log(s)
|
||||
|
||||
return tmp - out
|
||||
@@ -0,0 +1,453 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from itertools import zip_longest
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_
|
||||
import pytest
|
||||
|
||||
from scipy.special._testutils import assert_func_equal
|
||||
|
||||
try:
|
||||
import mpmath
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Machinery for systematic tests with mpmath
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
class Arg:
|
||||
"""Generate a set of numbers on the real axis, concentrating on
|
||||
'interesting' regions and covering all orders of magnitude.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, a=-np.inf, b=np.inf, inclusive_a=True, inclusive_b=True):
|
||||
if a > b:
|
||||
raise ValueError("a should be less than or equal to b")
|
||||
if a == -np.inf:
|
||||
a = -0.5*np.finfo(float).max
|
||||
if b == np.inf:
|
||||
b = 0.5*np.finfo(float).max
|
||||
self.a, self.b = a, b
|
||||
|
||||
self.inclusive_a, self.inclusive_b = inclusive_a, inclusive_b
|
||||
|
||||
def _positive_values(self, a, b, n):
|
||||
if a < 0:
|
||||
raise ValueError("a should be positive")
|
||||
|
||||
# Try to put half of the points into a linspace between a and
|
||||
# 10 the other half in a logspace.
|
||||
if n % 2 == 0:
|
||||
nlogpts = n//2
|
||||
nlinpts = nlogpts
|
||||
else:
|
||||
nlogpts = n//2
|
||||
nlinpts = nlogpts + 1
|
||||
|
||||
if a >= 10:
|
||||
# Outside of linspace range; just return a logspace.
|
||||
pts = np.logspace(np.log10(a), np.log10(b), n)
|
||||
elif a > 0 and b < 10:
|
||||
# Outside of logspace range; just return a linspace
|
||||
pts = np.linspace(a, b, n)
|
||||
elif a > 0:
|
||||
# Linspace between a and 10 and a logspace between 10 and
|
||||
# b.
|
||||
linpts = np.linspace(a, 10, nlinpts, endpoint=False)
|
||||
logpts = np.logspace(1, np.log10(b), nlogpts)
|
||||
pts = np.hstack((linpts, logpts))
|
||||
elif a == 0 and b <= 10:
|
||||
# Linspace between 0 and b and a logspace between 0 and
|
||||
# the smallest positive point of the linspace
|
||||
linpts = np.linspace(0, b, nlinpts)
|
||||
if linpts.size > 1:
|
||||
right = np.log10(linpts[1])
|
||||
else:
|
||||
right = -30
|
||||
logpts = np.logspace(-30, right, nlogpts, endpoint=False)
|
||||
pts = np.hstack((logpts, linpts))
|
||||
else:
|
||||
# Linspace between 0 and 10, logspace between 0 and the
|
||||
# smallest positive point of the linspace, and a logspace
|
||||
# between 10 and b.
|
||||
if nlogpts % 2 == 0:
|
||||
nlogpts1 = nlogpts//2
|
||||
nlogpts2 = nlogpts1
|
||||
else:
|
||||
nlogpts1 = nlogpts//2
|
||||
nlogpts2 = nlogpts1 + 1
|
||||
linpts = np.linspace(0, 10, nlinpts, endpoint=False)
|
||||
if linpts.size > 1:
|
||||
right = np.log10(linpts[1])
|
||||
else:
|
||||
right = -30
|
||||
logpts1 = np.logspace(-30, right, nlogpts1, endpoint=False)
|
||||
logpts2 = np.logspace(1, np.log10(b), nlogpts2)
|
||||
pts = np.hstack((logpts1, linpts, logpts2))
|
||||
|
||||
return np.sort(pts)
|
||||
|
||||
def values(self, n):
|
||||
"""Return an array containing n numbers."""
|
||||
a, b = self.a, self.b
|
||||
if a == b:
|
||||
return np.zeros(n)
|
||||
|
||||
if not self.inclusive_a:
|
||||
n += 1
|
||||
if not self.inclusive_b:
|
||||
n += 1
|
||||
|
||||
if n % 2 == 0:
|
||||
n1 = n//2
|
||||
n2 = n1
|
||||
else:
|
||||
n1 = n//2
|
||||
n2 = n1 + 1
|
||||
|
||||
if a >= 0:
|
||||
pospts = self._positive_values(a, b, n)
|
||||
negpts = []
|
||||
elif b <= 0:
|
||||
pospts = []
|
||||
negpts = -self._positive_values(-b, -a, n)
|
||||
else:
|
||||
pospts = self._positive_values(0, b, n1)
|
||||
negpts = -self._positive_values(0, -a, n2 + 1)
|
||||
# Don't want to get zero twice
|
||||
negpts = negpts[1:]
|
||||
pts = np.hstack((negpts[::-1], pospts))
|
||||
|
||||
if not self.inclusive_a:
|
||||
pts = pts[1:]
|
||||
if not self.inclusive_b:
|
||||
pts = pts[:-1]
|
||||
return pts
|
||||
|
||||
|
||||
class FixedArg:
|
||||
def __init__(self, values):
|
||||
self._values = np.asarray(values)
|
||||
|
||||
def values(self, n):
|
||||
return self._values
|
||||
|
||||
|
||||
class ComplexArg:
|
||||
def __init__(self, a=complex(-np.inf, -np.inf), b=complex(np.inf, np.inf)):
|
||||
self.real = Arg(a.real, b.real)
|
||||
self.imag = Arg(a.imag, b.imag)
|
||||
|
||||
def values(self, n):
|
||||
m = int(np.floor(np.sqrt(n)))
|
||||
x = self.real.values(m)
|
||||
y = self.imag.values(m + 1)
|
||||
return (x[:,None] + 1j*y[None,:]).ravel()
|
||||
|
||||
|
||||
class IntArg:
|
||||
def __init__(self, a=-1000, b=1000):
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
def values(self, n):
|
||||
v1 = Arg(self.a, self.b).values(max(1 + n//2, n-5)).astype(int)
|
||||
v2 = np.arange(-5, 5)
|
||||
v = np.unique(np.r_[v1, v2])
|
||||
v = v[(v >= self.a) & (v < self.b)]
|
||||
return v
|
||||
|
||||
|
||||
def get_args(argspec, n):
|
||||
if isinstance(argspec, np.ndarray):
|
||||
args = argspec.copy()
|
||||
else:
|
||||
nargs = len(argspec)
|
||||
ms = np.asarray(
|
||||
[1.5 if isinstance(spec, ComplexArg) else 1.0 for spec in argspec]
|
||||
)
|
||||
ms = (n**(ms/sum(ms))).astype(int) + 1
|
||||
|
||||
args = [spec.values(m) for spec, m in zip(argspec, ms)]
|
||||
args = np.array(np.broadcast_arrays(*np.ix_(*args))).reshape(nargs, -1).T
|
||||
|
||||
return args
|
||||
|
||||
|
||||
class MpmathData:
|
||||
def __init__(self, scipy_func, mpmath_func, arg_spec, name=None,
|
||||
dps=None, prec=None, n=None, rtol=1e-7, atol=1e-300,
|
||||
ignore_inf_sign=False, distinguish_nan_and_inf=True,
|
||||
nan_ok=True, param_filter=None):
|
||||
|
||||
# mpmath tests are really slow (see gh-6989). Use a small number of
|
||||
# points by default, increase back to 5000 (old default) if XSLOW is
|
||||
# set
|
||||
if n is None:
|
||||
try:
|
||||
is_xslow = int(os.environ.get('SCIPY_XSLOW', '0'))
|
||||
except ValueError:
|
||||
is_xslow = False
|
||||
|
||||
n = 5000 if is_xslow else 500
|
||||
|
||||
self.scipy_func = scipy_func
|
||||
self.mpmath_func = mpmath_func
|
||||
self.arg_spec = arg_spec
|
||||
self.dps = dps
|
||||
self.prec = prec
|
||||
self.n = n
|
||||
self.rtol = rtol
|
||||
self.atol = atol
|
||||
self.ignore_inf_sign = ignore_inf_sign
|
||||
self.nan_ok = nan_ok
|
||||
if isinstance(self.arg_spec, np.ndarray):
|
||||
self.is_complex = np.issubdtype(self.arg_spec.dtype, np.complexfloating)
|
||||
else:
|
||||
self.is_complex = any(
|
||||
[isinstance(arg, ComplexArg) for arg in self.arg_spec]
|
||||
)
|
||||
self.ignore_inf_sign = ignore_inf_sign
|
||||
self.distinguish_nan_and_inf = distinguish_nan_and_inf
|
||||
if not name or name == '<lambda>':
|
||||
name = getattr(scipy_func, '__name__', None)
|
||||
if not name or name == '<lambda>':
|
||||
name = getattr(mpmath_func, '__name__', None)
|
||||
self.name = name
|
||||
self.param_filter = param_filter
|
||||
|
||||
def check(self):
|
||||
np.random.seed(1234)
|
||||
|
||||
# Generate values for the arguments
|
||||
argarr = get_args(self.arg_spec, self.n)
|
||||
|
||||
# Check
|
||||
old_dps, old_prec = mpmath.mp.dps, mpmath.mp.prec
|
||||
try:
|
||||
if self.dps is not None:
|
||||
dps_list = [self.dps]
|
||||
else:
|
||||
dps_list = [20]
|
||||
if self.prec is not None:
|
||||
mpmath.mp.prec = self.prec
|
||||
|
||||
# Proper casting of mpmath input and output types. Using
|
||||
# native mpmath types as inputs gives improved precision
|
||||
# in some cases.
|
||||
if np.issubdtype(argarr.dtype, np.complexfloating):
|
||||
pytype = mpc2complex
|
||||
|
||||
def mptype(x):
|
||||
return mpmath.mpc(complex(x))
|
||||
else:
|
||||
def mptype(x):
|
||||
return mpmath.mpf(float(x))
|
||||
|
||||
def pytype(x):
|
||||
if abs(x.imag) > 1e-16*(1 + abs(x.real)):
|
||||
return np.nan
|
||||
else:
|
||||
return mpf2float(x.real)
|
||||
|
||||
# Try out different dps until one (or none) works
|
||||
for j, dps in enumerate(dps_list):
|
||||
mpmath.mp.dps = dps
|
||||
|
||||
try:
|
||||
assert_func_equal(
|
||||
self.scipy_func,
|
||||
lambda *a: pytype(self.mpmath_func(*map(mptype, a))),
|
||||
argarr,
|
||||
vectorized=False,
|
||||
rtol=self.rtol,
|
||||
atol=self.atol,
|
||||
ignore_inf_sign=self.ignore_inf_sign,
|
||||
distinguish_nan_and_inf=self.distinguish_nan_and_inf,
|
||||
nan_ok=self.nan_ok,
|
||||
param_filter=self.param_filter
|
||||
)
|
||||
break
|
||||
except AssertionError:
|
||||
if j >= len(dps_list)-1:
|
||||
# reraise the Exception
|
||||
tp, value, tb = sys.exc_info()
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
finally:
|
||||
mpmath.mp.dps, mpmath.mp.prec = old_dps, old_prec
|
||||
|
||||
def __repr__(self):
|
||||
if self.is_complex:
|
||||
return f"<MpmathData: {self.name} (complex)>"
|
||||
else:
|
||||
return f"<MpmathData: {self.name}>"
|
||||
|
||||
|
||||
def assert_mpmath_equal(*a, **kw):
|
||||
d = MpmathData(*a, **kw)
|
||||
d.check()
|
||||
|
||||
|
||||
def nonfunctional_tooslow(func):
|
||||
return pytest.mark.skip(
|
||||
reason=" Test not yet functional (too slow), needs more work."
|
||||
)(func)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Tools for dealing with mpmath quirks
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
def mpf2float(x):
|
||||
"""
|
||||
Convert an mpf to the nearest floating point number. Just using
|
||||
float directly doesn't work because of results like this:
|
||||
|
||||
with mp.workdps(50):
|
||||
float(mpf("0.99999999999999999")) = 0.9999999999999999
|
||||
|
||||
"""
|
||||
return float(mpmath.nstr(x, 17, min_fixed=0, max_fixed=0))
|
||||
|
||||
|
||||
def mpc2complex(x):
|
||||
return complex(mpf2float(x.real), mpf2float(x.imag))
|
||||
|
||||
|
||||
def trace_args(func):
|
||||
def tofloat(x):
|
||||
if isinstance(x, mpmath.mpc):
|
||||
return complex(x)
|
||||
else:
|
||||
return float(x)
|
||||
|
||||
def wrap(*a, **kw):
|
||||
sys.stderr.write(f"{tuple(map(tofloat, a))!r}: ")
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
r = func(*a, **kw)
|
||||
sys.stderr.write(f"-> {r!r}")
|
||||
finally:
|
||||
sys.stderr.write("\n")
|
||||
sys.stderr.flush()
|
||||
return r
|
||||
return wrap
|
||||
|
||||
|
||||
try:
|
||||
import signal
|
||||
POSIX = ('setitimer' in dir(signal))
|
||||
except ImportError:
|
||||
POSIX = False
|
||||
|
||||
|
||||
class TimeoutError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def time_limited(timeout=0.5, return_val=np.nan, use_sigalrm=True):
|
||||
"""
|
||||
Decorator for setting a timeout for pure-Python functions.
|
||||
|
||||
If the function does not return within `timeout` seconds, the
|
||||
value `return_val` is returned instead.
|
||||
|
||||
On POSIX this uses SIGALRM by default. On non-POSIX, settrace is
|
||||
used. Do not use this with threads: the SIGALRM implementation
|
||||
does probably not work well. The settrace implementation only
|
||||
traces the current thread.
|
||||
|
||||
The settrace implementation slows down execution speed. Slowdown
|
||||
by a factor around 10 is probably typical.
|
||||
"""
|
||||
if POSIX and use_sigalrm:
|
||||
def sigalrm_handler(signum, frame):
|
||||
raise TimeoutError()
|
||||
|
||||
def deco(func):
|
||||
def wrap(*a, **kw):
|
||||
old_handler = signal.signal(signal.SIGALRM, sigalrm_handler)
|
||||
signal.setitimer(signal.ITIMER_REAL, timeout)
|
||||
try:
|
||||
return func(*a, **kw)
|
||||
except TimeoutError:
|
||||
return return_val
|
||||
finally:
|
||||
signal.setitimer(signal.ITIMER_REAL, 0)
|
||||
signal.signal(signal.SIGALRM, old_handler)
|
||||
return wrap
|
||||
else:
|
||||
def deco(func):
|
||||
def wrap(*a, **kw):
|
||||
start_time = time.time()
|
||||
|
||||
def trace(frame, event, arg):
|
||||
if time.time() - start_time > timeout:
|
||||
raise TimeoutError()
|
||||
return trace
|
||||
sys.settrace(trace)
|
||||
try:
|
||||
return func(*a, **kw)
|
||||
except TimeoutError:
|
||||
sys.settrace(None)
|
||||
return return_val
|
||||
finally:
|
||||
sys.settrace(None)
|
||||
return wrap
|
||||
return deco
|
||||
|
||||
|
||||
def exception_to_nan(func):
|
||||
"""Decorate function to return nan if it raises an exception"""
|
||||
def wrap(*a, **kw):
|
||||
try:
|
||||
return func(*a, **kw)
|
||||
except Exception:
|
||||
return np.nan
|
||||
return wrap
|
||||
|
||||
|
||||
def inf_to_nan(func):
|
||||
"""Decorate function to return nan if it returns inf"""
|
||||
def wrap(*a, **kw):
|
||||
v = func(*a, **kw)
|
||||
if not np.isfinite(v):
|
||||
return np.nan
|
||||
return v
|
||||
return wrap
|
||||
|
||||
|
||||
def mp_assert_allclose(res, std, atol=0, rtol=1e-17):
|
||||
"""
|
||||
Compare lists of mpmath.mpf's or mpmath.mpc's directly so that it
|
||||
can be done to higher precision than double.
|
||||
"""
|
||||
failures = []
|
||||
for k, (resval, stdval) in enumerate(zip_longest(res, std)):
|
||||
if resval is None or stdval is None:
|
||||
raise ValueError('Lengths of inputs res and std are not equal.')
|
||||
if mpmath.fabs(resval - stdval) > atol + rtol*mpmath.fabs(stdval):
|
||||
failures.append((k, resval, stdval))
|
||||
|
||||
nfail = len(failures)
|
||||
if nfail > 0:
|
||||
ndigits = int(abs(np.log10(rtol)))
|
||||
msg = [""]
|
||||
msg.append(f"Bad results ({nfail} out of {k + 1}) for the following points:")
|
||||
for k, resval, stdval in failures:
|
||||
resrep = mpmath.nstr(resval, ndigits, min_fixed=0, max_fixed=0)
|
||||
stdrep = mpmath.nstr(stdval, ndigits, min_fixed=0, max_fixed=0)
|
||||
if stdval == 0:
|
||||
rdiff = "inf"
|
||||
else:
|
||||
rdiff = mpmath.fabs((resval - stdval)/stdval)
|
||||
rdiff = mpmath.nstr(rdiff, 3)
|
||||
msg.append(f"{k}: {resrep} != {stdrep} (rdiff {rdiff})")
|
||||
assert_(False, "\n".join(msg))
|
||||
@@ -0,0 +1,610 @@
|
||||
import collections
|
||||
import numbers
|
||||
import numpy as np
|
||||
|
||||
from ._input_validation import _nonneg_int_or_fail
|
||||
|
||||
from ._special_ufuncs import (legendre_p, assoc_legendre_p,
|
||||
sph_legendre_p, sph_harm_y)
|
||||
from ._gufuncs import (legendre_p_all, assoc_legendre_p_all,
|
||||
sph_legendre_p_all, sph_harm_y_all)
|
||||
|
||||
__all__ = [
|
||||
"assoc_legendre_p",
|
||||
"assoc_legendre_p_all",
|
||||
"legendre_p",
|
||||
"legendre_p_all",
|
||||
"sph_harm_y",
|
||||
"sph_harm_y_all",
|
||||
"sph_legendre_p",
|
||||
"sph_legendre_p_all",
|
||||
]
|
||||
|
||||
|
||||
class MultiUFunc:
|
||||
def __init__(self, ufunc_or_ufuncs, doc=None, *,
|
||||
force_complex_output=False, **default_kwargs):
|
||||
if not isinstance(ufunc_or_ufuncs, np.ufunc):
|
||||
if isinstance(ufunc_or_ufuncs, collections.abc.Mapping):
|
||||
ufuncs_iter = ufunc_or_ufuncs.values()
|
||||
elif isinstance(ufunc_or_ufuncs, collections.abc.Iterable):
|
||||
ufuncs_iter = ufunc_or_ufuncs
|
||||
else:
|
||||
raise ValueError("ufunc_or_ufuncs should be a ufunc or a"
|
||||
" ufunc collection")
|
||||
|
||||
# Perform input validation to ensure all ufuncs in ufuncs are
|
||||
# actually ufuncs and all take the same input types.
|
||||
seen_input_types = set()
|
||||
for ufunc in ufuncs_iter:
|
||||
if not isinstance(ufunc, np.ufunc):
|
||||
raise ValueError("All ufuncs must have type `numpy.ufunc`."
|
||||
f" Received {ufunc_or_ufuncs}")
|
||||
seen_input_types.add(frozenset(x.split("->")[0] for x in ufunc.types))
|
||||
if len(seen_input_types) > 1:
|
||||
raise ValueError("All ufuncs must take the same input types.")
|
||||
|
||||
self._ufunc_or_ufuncs = ufunc_or_ufuncs
|
||||
self.__doc = doc
|
||||
self.__force_complex_output = force_complex_output
|
||||
self._default_kwargs = default_kwargs
|
||||
self._resolve_out_shapes = None
|
||||
self._finalize_out = None
|
||||
self._key = None
|
||||
self._ufunc_default_args = lambda *args, **kwargs: ()
|
||||
self._ufunc_default_kwargs = lambda *args, **kwargs: {}
|
||||
|
||||
@property
|
||||
def __doc__(self):
|
||||
return self.__doc
|
||||
|
||||
def _override_key(self, func):
|
||||
"""Set `key` method by decorating a function.
|
||||
"""
|
||||
self._key = func
|
||||
|
||||
def _override_ufunc_default_args(self, func):
|
||||
self._ufunc_default_args = func
|
||||
|
||||
def _override_ufunc_default_kwargs(self, func):
|
||||
self._ufunc_default_kwargs = func
|
||||
|
||||
def _override_resolve_out_shapes(self, func):
|
||||
"""Set `resolve_out_shapes` method by decorating a function."""
|
||||
if func.__doc__ is None:
|
||||
func.__doc__ = \
|
||||
"""Resolve to output shapes based on relevant inputs."""
|
||||
func.__name__ = "resolve_out_shapes"
|
||||
self._resolve_out_shapes = func
|
||||
|
||||
def _override_finalize_out(self, func):
|
||||
self._finalize_out = func
|
||||
|
||||
def _resolve_ufunc(self, **kwargs):
|
||||
"""Resolve to a ufunc based on keyword arguments."""
|
||||
|
||||
if isinstance(self._ufunc_or_ufuncs, np.ufunc):
|
||||
return self._ufunc_or_ufuncs
|
||||
|
||||
ufunc_key = self._key(**kwargs)
|
||||
return self._ufunc_or_ufuncs[ufunc_key]
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
kwargs = self._default_kwargs | kwargs
|
||||
|
||||
args += self._ufunc_default_args(**kwargs)
|
||||
|
||||
ufunc = self._resolve_ufunc(**kwargs)
|
||||
|
||||
# array arguments to be passed to the ufunc
|
||||
ufunc_args = [np.asarray(arg) for arg in args[-ufunc.nin:]]
|
||||
|
||||
ufunc_kwargs = self._ufunc_default_kwargs(**kwargs)
|
||||
|
||||
if (self._resolve_out_shapes is not None):
|
||||
ufunc_arg_shapes = tuple(np.shape(ufunc_arg) for ufunc_arg in ufunc_args)
|
||||
ufunc_out_shapes = self._resolve_out_shapes(*args[:-ufunc.nin],
|
||||
*ufunc_arg_shapes, ufunc.nout,
|
||||
**kwargs)
|
||||
|
||||
ufunc_arg_dtypes = tuple(ufunc_arg.dtype if hasattr(ufunc_arg, 'dtype')
|
||||
else np.dtype(type(ufunc_arg))
|
||||
for ufunc_arg in ufunc_args)
|
||||
|
||||
if hasattr(ufunc, 'resolve_dtypes'):
|
||||
ufunc_dtypes = ufunc_arg_dtypes + ufunc.nout * (None,)
|
||||
ufunc_dtypes = ufunc.resolve_dtypes(ufunc_dtypes)
|
||||
ufunc_out_dtypes = ufunc_dtypes[-ufunc.nout:]
|
||||
else:
|
||||
ufunc_out_dtype = np.result_type(*ufunc_arg_dtypes)
|
||||
if (not np.issubdtype(ufunc_out_dtype, np.inexact)):
|
||||
ufunc_out_dtype = np.float64
|
||||
|
||||
ufunc_out_dtypes = ufunc.nout * (ufunc_out_dtype,)
|
||||
|
||||
if self.__force_complex_output:
|
||||
ufunc_out_dtypes = tuple(np.result_type(1j, ufunc_out_dtype)
|
||||
for ufunc_out_dtype in ufunc_out_dtypes)
|
||||
|
||||
out = tuple(np.empty(ufunc_out_shape, dtype=ufunc_out_dtype)
|
||||
for ufunc_out_shape, ufunc_out_dtype
|
||||
in zip(ufunc_out_shapes, ufunc_out_dtypes))
|
||||
|
||||
ufunc_kwargs['out'] = out
|
||||
|
||||
out = ufunc(*ufunc_args, **ufunc_kwargs)
|
||||
if (self._finalize_out is not None):
|
||||
out = self._finalize_out(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
sph_legendre_p = MultiUFunc(
|
||||
sph_legendre_p,
|
||||
r"""sph_legendre_p(n, m, theta, *, diff_n=0)
|
||||
|
||||
Spherical Legendre polynomial of the first kind.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : ArrayLike[int]
|
||||
Degree of the spherical Legendre polynomial. Must have ``n >= 0``.
|
||||
m : ArrayLike[int]
|
||||
Order of the spherical Legendre polynomial.
|
||||
theta : ArrayLike[float]
|
||||
Input value.
|
||||
diff_n : Optional[int]
|
||||
A non-negative integer. Compute and return all derivatives up
|
||||
to order ``diff_n``. Default is 0.
|
||||
|
||||
Returns
|
||||
-------
|
||||
p : ndarray or tuple[ndarray]
|
||||
Spherical Legendre polynomial with ``diff_n`` derivatives.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The spherical counterpart of an (unnormalized) associated Legendre polynomial has
|
||||
the additional factor
|
||||
|
||||
.. math::
|
||||
|
||||
\sqrt{\frac{(2 n + 1) (n - m)!}{4 \pi (n + m)!}}
|
||||
|
||||
It is the same as the spherical harmonic :math:`Y_{n}^{m}(\theta, \phi)`
|
||||
with :math:`\phi = 0`.
|
||||
""", diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@sph_legendre_p._override_key
|
||||
def _(diff_n):
|
||||
diff_n = _nonneg_int_or_fail(diff_n, "diff_n", strict=False)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise ValueError(
|
||||
"diff_n is currently only implemented for orders 0, 1, and 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return diff_n
|
||||
|
||||
|
||||
@sph_legendre_p._override_finalize_out
|
||||
def _(out):
|
||||
return np.moveaxis(out, -1, 0)
|
||||
|
||||
|
||||
sph_legendre_p_all = MultiUFunc(
|
||||
sph_legendre_p_all,
|
||||
"""sph_legendre_p_all(n, m, theta, *, diff_n=0)
|
||||
|
||||
All spherical Legendre polynomials of the first kind up to the
|
||||
specified degree ``n`` and order ``m``.
|
||||
|
||||
Output shape is ``(n + 1, 2 * m + 1, ...)``. The entry at ``(j, i)``
|
||||
corresponds to degree ``j`` and order ``i`` for all ``0 <= j <= n``
|
||||
and ``-m <= i <= m``.
|
||||
|
||||
See Also
|
||||
--------
|
||||
sph_legendre_p
|
||||
""", diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@sph_legendre_p_all._override_key
|
||||
def _(diff_n):
|
||||
diff_n = _nonneg_int_or_fail(diff_n, "diff_n", strict=False)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise ValueError(
|
||||
"diff_n is currently only implemented for orders 0, 1, and 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return diff_n
|
||||
|
||||
|
||||
@sph_legendre_p_all._override_ufunc_default_kwargs
|
||||
def _(diff_n):
|
||||
return {'axes': [()] + [(0, 1, -1)]}
|
||||
|
||||
|
||||
@sph_legendre_p_all._override_resolve_out_shapes
|
||||
def _(n, m, theta_shape, nout, diff_n):
|
||||
if not isinstance(n, numbers.Integral) or (n < 0):
|
||||
raise ValueError("n must be a non-negative integer.")
|
||||
|
||||
return ((n + 1, 2 * abs(m) + 1) + theta_shape + (diff_n + 1,),)
|
||||
|
||||
|
||||
@sph_legendre_p_all._override_finalize_out
|
||||
def _(out):
|
||||
return np.moveaxis(out, -1, 0)
|
||||
|
||||
|
||||
assoc_legendre_p = MultiUFunc(
|
||||
assoc_legendre_p,
|
||||
r"""assoc_legendre_p(n, m, z, *, branch_cut=2, norm=False, diff_n=0)
|
||||
|
||||
Associated Legendre polynomial of the first kind.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : ArrayLike[int]
|
||||
Degree of the associated Legendre polynomial. Must have ``n >= 0``.
|
||||
m : ArrayLike[int]
|
||||
order of the associated Legendre polynomial.
|
||||
z : ArrayLike[float | complex]
|
||||
Input value.
|
||||
branch_cut : Optional[ArrayLike[int]]
|
||||
Selects branch cut. Must be 2 (default) or 3.
|
||||
2: cut on the real axis ``|z| > 1``
|
||||
3: cut on the real axis ``-1 < z < 1``
|
||||
norm : Optional[bool]
|
||||
If ``True``, compute the normalized associated Legendre polynomial.
|
||||
Default is ``False``.
|
||||
diff_n : Optional[int]
|
||||
A non-negative integer. Compute and return all derivatives up
|
||||
to order ``diff_n``. Default is 0.
|
||||
|
||||
Returns
|
||||
-------
|
||||
p : ndarray or tuple[ndarray]
|
||||
Associated Legendre polynomial with ``diff_n`` derivatives.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The normalized counterpart of an (unnormalized) associated Legendre
|
||||
polynomial has the additional factor
|
||||
|
||||
.. math::
|
||||
|
||||
\sqrt{\frac{(2 n + 1) (n - m)!}{2 (n + m)!}}
|
||||
""", branch_cut=2, norm=False, diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@assoc_legendre_p._override_key
|
||||
def _(branch_cut, norm, diff_n):
|
||||
diff_n = _nonneg_int_or_fail(diff_n, "diff_n", strict=False)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise ValueError(
|
||||
"diff_n is currently only implemented for orders 0, 1, and 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return norm, diff_n
|
||||
|
||||
|
||||
@assoc_legendre_p._override_ufunc_default_args
|
||||
def _(branch_cut, norm, diff_n):
|
||||
return branch_cut,
|
||||
|
||||
|
||||
@assoc_legendre_p._override_finalize_out
|
||||
def _(out):
|
||||
return np.moveaxis(out, -1, 0)
|
||||
|
||||
|
||||
assoc_legendre_p_all = MultiUFunc(
|
||||
assoc_legendre_p_all,
|
||||
"""assoc_legendre_p_all(n, m, z, *, branch_cut=2, norm=False, diff_n=0)
|
||||
|
||||
All associated Legendre polynomials of the first kind up to the
|
||||
specified degree ``n`` and order ``m``.
|
||||
|
||||
Output shape is ``(n + 1, 2 * m + 1, ...)``. The entry at ``(j, i)``
|
||||
corresponds to degree ``j`` and order ``i`` for all ``0 <= j <= n``
|
||||
and ``-m <= i <= m``.
|
||||
|
||||
See Also
|
||||
--------
|
||||
assoc_legendre_p
|
||||
""", branch_cut=2, norm=False, diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@assoc_legendre_p_all._override_key
|
||||
def _(branch_cut, norm, diff_n):
|
||||
if not ((isinstance(diff_n, numbers.Integral))
|
||||
and diff_n >= 0):
|
||||
raise ValueError(
|
||||
f"diff_n must be a non-negative integer, received: {diff_n}."
|
||||
)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise ValueError(
|
||||
"diff_n is currently only implemented for orders 0, 1, and 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return norm, diff_n
|
||||
|
||||
|
||||
@assoc_legendre_p_all._override_ufunc_default_args
|
||||
def _(branch_cut, norm, diff_n):
|
||||
return branch_cut,
|
||||
|
||||
|
||||
@assoc_legendre_p_all._override_ufunc_default_kwargs
|
||||
def _(branch_cut, norm, diff_n):
|
||||
return {'axes': [(), ()] + [(0, 1, -1)]}
|
||||
|
||||
|
||||
@assoc_legendre_p_all._override_resolve_out_shapes
|
||||
def _(n, m, z_shape, branch_cut_shape, nout, **kwargs):
|
||||
diff_n = kwargs['diff_n']
|
||||
|
||||
if not isinstance(n, numbers.Integral) or (n < 0):
|
||||
raise ValueError("n must be a non-negative integer.")
|
||||
if not isinstance(m, numbers.Integral) or (m < 0):
|
||||
raise ValueError("m must be a non-negative integer.")
|
||||
|
||||
return ((n + 1, 2 * abs(m) + 1) +
|
||||
np.broadcast_shapes(z_shape, branch_cut_shape) + (diff_n + 1,),)
|
||||
|
||||
|
||||
@assoc_legendre_p_all._override_finalize_out
|
||||
def _(out):
|
||||
return np.moveaxis(out, -1, 0)
|
||||
|
||||
|
||||
legendre_p = MultiUFunc(
|
||||
legendre_p,
|
||||
"""legendre_p(n, z, *, diff_n=0)
|
||||
|
||||
Legendre polynomial of the first kind.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : ArrayLike[int]
|
||||
Degree of the Legendre polynomial. Must have ``n >= 0``.
|
||||
z : ArrayLike[float]
|
||||
Input value.
|
||||
diff_n : Optional[int]
|
||||
A non-negative integer. Compute and return all derivatives up
|
||||
to order ``diff_n``. Default is 0.
|
||||
|
||||
Returns
|
||||
-------
|
||||
p : ndarray or tuple[ndarray]
|
||||
Legendre polynomial with ``diff_n`` derivatives.
|
||||
|
||||
See Also
|
||||
--------
|
||||
legendre
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Zhang, Shanjie and Jin, Jianming. "Computation of Special
|
||||
Functions", John Wiley and Sons, 1996.
|
||||
https://people.sc.fsu.edu/~jburkardt/f77_src/special_functions/special_functions.html
|
||||
""", diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@legendre_p._override_key
|
||||
def _(diff_n):
|
||||
if (not isinstance(diff_n, numbers.Integral)) or (diff_n < 0):
|
||||
raise ValueError(
|
||||
f"diff_n must be a non-negative integer, received: {diff_n}."
|
||||
)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise NotImplementedError(
|
||||
"diff_n is currently only implemented for orders 0, 1, and 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return diff_n
|
||||
|
||||
|
||||
@legendre_p._override_finalize_out
|
||||
def _(out):
|
||||
return np.moveaxis(out, -1, 0)
|
||||
|
||||
|
||||
legendre_p_all = MultiUFunc(
|
||||
legendre_p_all,
|
||||
"""legendre_p_all(n, z, *, diff_n=0)
|
||||
|
||||
All Legendre polynomials of the first kind up to the
|
||||
specified degree ``n``.
|
||||
|
||||
Output shape is ``(n + 1, ...)``. The entry at ``j``
|
||||
corresponds to degree ``j`` for all ``0 <= j <= n``.
|
||||
|
||||
See Also
|
||||
--------
|
||||
legendre_p
|
||||
""", diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@legendre_p_all._override_key
|
||||
def _(diff_n):
|
||||
diff_n = _nonneg_int_or_fail(diff_n, "diff_n", strict=False)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise ValueError(
|
||||
"diff_n is currently only implemented for orders 0, 1, and 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return diff_n
|
||||
|
||||
|
||||
@legendre_p_all._override_ufunc_default_kwargs
|
||||
def _(diff_n):
|
||||
return {'axes': [(), (0, -1)]}
|
||||
|
||||
|
||||
@legendre_p_all._override_resolve_out_shapes
|
||||
def _(n, z_shape, nout, diff_n):
|
||||
n = _nonneg_int_or_fail(n, 'n', strict=False)
|
||||
|
||||
return nout * ((n + 1,) + z_shape + (diff_n + 1,),)
|
||||
|
||||
|
||||
@legendre_p_all._override_finalize_out
|
||||
def _(out):
|
||||
return np.moveaxis(out, -1, 0)
|
||||
|
||||
|
||||
sph_harm_y = MultiUFunc(
|
||||
sph_harm_y,
|
||||
r"""sph_harm_y(n, m, theta, phi, *, diff_n=0)
|
||||
|
||||
Spherical harmonics. They are defined as
|
||||
|
||||
.. math::
|
||||
|
||||
Y_n^m(\theta,\phi) = \sqrt{\frac{2 n + 1}{4 \pi} \frac{(n - m)!}{(n + m)!}}
|
||||
P_n^m(\cos(\theta)) e^{i m \phi}
|
||||
|
||||
where :math:`P_n^m` are the (unnormalized) associated Legendre polynomials.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : ArrayLike[int]
|
||||
Degree of the harmonic. Must have ``n >= 0``. This is
|
||||
often denoted by ``l`` (lower case L) in descriptions of
|
||||
spherical harmonics.
|
||||
m : ArrayLike[int]
|
||||
Order of the harmonic.
|
||||
theta : ArrayLike[float]
|
||||
Polar (colatitudinal) coordinate; must be in ``[0, pi]``.
|
||||
phi : ArrayLike[float]
|
||||
Azimuthal (longitudinal) coordinate; must be in ``[0, 2*pi]``.
|
||||
diff_n : Optional[int]
|
||||
A non-negative integer. Compute and return all derivatives up
|
||||
to order ``diff_n``. Default is 0.
|
||||
|
||||
Returns
|
||||
-------
|
||||
y : ndarray[complex] or tuple[ndarray[complex]]
|
||||
Spherical harmonics with ``diff_n`` derivatives.
|
||||
|
||||
Notes
|
||||
-----
|
||||
There are different conventions for the meanings of the input
|
||||
arguments ``theta`` and ``phi``. In SciPy ``theta`` is the
|
||||
polar angle and ``phi`` is the azimuthal angle. It is common to
|
||||
see the opposite convention, that is, ``theta`` as the azimuthal angle
|
||||
and ``phi`` as the polar angle.
|
||||
|
||||
Note that SciPy's spherical harmonics include the Condon-Shortley
|
||||
phase [2]_ because it is part of `sph_legendre_p`.
|
||||
|
||||
With SciPy's conventions, the first several spherical harmonics
|
||||
are
|
||||
|
||||
.. math::
|
||||
|
||||
Y_0^0(\theta, \phi) &= \frac{1}{2} \sqrt{\frac{1}{\pi}} \\
|
||||
Y_1^{-1}(\theta, \phi) &= \frac{1}{2} \sqrt{\frac{3}{2\pi}}
|
||||
e^{-i\phi} \sin(\theta) \\
|
||||
Y_1^0(\theta, \phi) &= \frac{1}{2} \sqrt{\frac{3}{\pi}}
|
||||
\cos(\theta) \\
|
||||
Y_1^1(\theta, \phi) &= -\frac{1}{2} \sqrt{\frac{3}{2\pi}}
|
||||
e^{i\phi} \sin(\theta).
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Digital Library of Mathematical Functions, 14.30.
|
||||
https://dlmf.nist.gov/14.30
|
||||
.. [2] https://en.wikipedia.org/wiki/Spherical_harmonics#Condon.E2.80.93Shortley_phase
|
||||
""", force_complex_output=True, diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@sph_harm_y._override_key
|
||||
def _(diff_n):
|
||||
diff_n = _nonneg_int_or_fail(diff_n, "diff_n", strict=False)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise ValueError(
|
||||
"diff_n is currently only implemented for orders 0, 1, and 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return diff_n
|
||||
|
||||
|
||||
@sph_harm_y._override_finalize_out
|
||||
def _(out):
|
||||
if (out.shape[-1] == 1):
|
||||
return out[..., 0, 0]
|
||||
|
||||
if (out.shape[-1] == 2):
|
||||
return out[..., 0, 0], out[..., [1, 0], [0, 1]]
|
||||
|
||||
if (out.shape[-1] == 3):
|
||||
return (out[..., 0, 0], out[..., [1, 0], [0, 1]],
|
||||
out[..., [[2, 1], [1, 0]], [[0, 1], [1, 2]]])
|
||||
|
||||
|
||||
sph_harm_y_all = MultiUFunc(
|
||||
sph_harm_y_all,
|
||||
"""sph_harm_y_all(n, m, theta, phi, *, diff_n=0)
|
||||
|
||||
All spherical harmonics up to the specified degree ``n`` and order ``m``.
|
||||
|
||||
Output shape is ``(n + 1, 2 * m + 1, ...)``. The entry at ``(j, i)``
|
||||
corresponds to degree ``j`` and order ``i`` for all ``0 <= j <= n``
|
||||
and ``-m <= i <= m``.
|
||||
|
||||
See Also
|
||||
--------
|
||||
sph_harm_y
|
||||
""", force_complex_output=True, diff_n=0
|
||||
)
|
||||
|
||||
|
||||
@sph_harm_y_all._override_key
|
||||
def _(diff_n):
|
||||
diff_n = _nonneg_int_or_fail(diff_n, "diff_n", strict=False)
|
||||
if not 0 <= diff_n <= 2:
|
||||
raise ValueError(
|
||||
"diff_n is currently only implemented for orders 2,"
|
||||
f" received: {diff_n}."
|
||||
)
|
||||
return diff_n
|
||||
|
||||
|
||||
@sph_harm_y_all._override_ufunc_default_kwargs
|
||||
def _(diff_n):
|
||||
return {'axes': [(), ()] + [(0, 1, -2, -1)]}
|
||||
|
||||
|
||||
@sph_harm_y_all._override_resolve_out_shapes
|
||||
def _(n, m, theta_shape, phi_shape, nout, **kwargs):
|
||||
diff_n = kwargs['diff_n']
|
||||
|
||||
if not isinstance(n, numbers.Integral) or (n < 0):
|
||||
raise ValueError("n must be a non-negative integer.")
|
||||
|
||||
return ((n + 1, 2 * abs(m) + 1) + np.broadcast_shapes(theta_shape, phi_shape) +
|
||||
(diff_n + 1, diff_n + 1),)
|
||||
|
||||
|
||||
@sph_harm_y_all._override_finalize_out
|
||||
def _(out):
|
||||
if (out.shape[-1] == 1):
|
||||
return out[..., 0, 0]
|
||||
|
||||
if (out.shape[-1] == 2):
|
||||
return out[..., 0, 0], out[..., [1, 0], [0, 1]]
|
||||
|
||||
if (out.shape[-1] == 3):
|
||||
return (out[..., 0, 0], out[..., [1, 0], [0, 1]],
|
||||
out[..., [[2, 1], [1, 0]], [[0, 1], [1, 2]]])
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,330 @@
|
||||
from typing import (
|
||||
Any,
|
||||
Literal,
|
||||
Optional,
|
||||
overload,
|
||||
)
|
||||
from collections.abc import Callable
|
||||
|
||||
import numpy as np
|
||||
|
||||
_IntegerType = int | np.integer
|
||||
_FloatingType = float | np.floating
|
||||
_PointsAndWeights = tuple[np.ndarray, np.ndarray]
|
||||
_PointsAndWeightsAndMu = tuple[np.ndarray, np.ndarray, float]
|
||||
|
||||
_ArrayLike0D = bool | int | float | complex | str | bytes | np.generic
|
||||
|
||||
__all__ = [
|
||||
'legendre',
|
||||
'chebyt',
|
||||
'chebyu',
|
||||
'chebyc',
|
||||
'chebys',
|
||||
'jacobi',
|
||||
'laguerre',
|
||||
'genlaguerre',
|
||||
'hermite',
|
||||
'hermitenorm',
|
||||
'gegenbauer',
|
||||
'sh_legendre',
|
||||
'sh_chebyt',
|
||||
'sh_chebyu',
|
||||
'sh_jacobi',
|
||||
'roots_legendre',
|
||||
'roots_chebyt',
|
||||
'roots_chebyu',
|
||||
'roots_chebyc',
|
||||
'roots_chebys',
|
||||
'roots_jacobi',
|
||||
'roots_laguerre',
|
||||
'roots_genlaguerre',
|
||||
'roots_hermite',
|
||||
'roots_hermitenorm',
|
||||
'roots_gegenbauer',
|
||||
'roots_sh_legendre',
|
||||
'roots_sh_chebyt',
|
||||
'roots_sh_chebyu',
|
||||
'roots_sh_jacobi',
|
||||
]
|
||||
|
||||
@overload
|
||||
def roots_jacobi(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
beta: _FloatingType,
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_jacobi(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
beta: _FloatingType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_jacobi(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
beta: _FloatingType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_sh_jacobi(
|
||||
n: _IntegerType,
|
||||
p1: _FloatingType,
|
||||
q1: _FloatingType,
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_jacobi(
|
||||
n: _IntegerType,
|
||||
p1: _FloatingType,
|
||||
q1: _FloatingType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_jacobi(
|
||||
n: _IntegerType,
|
||||
p1: _FloatingType,
|
||||
q1: _FloatingType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_genlaguerre(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_genlaguerre(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_genlaguerre(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_laguerre(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_laguerre(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_laguerre(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_hermite(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_hermite(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_hermite(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_hermitenorm(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_hermitenorm(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_hermitenorm(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_gegenbauer(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_gegenbauer(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_gegenbauer(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_chebyt(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebyt(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebyt(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_chebyu(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebyu(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebyu(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_chebyc(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebyc(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebyc(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_chebys(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebys(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_chebys(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_sh_chebyt(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_chebyt(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_chebyt(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_sh_chebyu(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_chebyu(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_chebyu(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_legendre(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_legendre(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_legendre(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
@overload
|
||||
def roots_sh_legendre(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_legendre(
|
||||
n: _IntegerType,
|
||||
mu: Literal[False],
|
||||
) -> _PointsAndWeights: ...
|
||||
@overload
|
||||
def roots_sh_legendre(
|
||||
n: _IntegerType,
|
||||
mu: Literal[True],
|
||||
) -> _PointsAndWeightsAndMu: ...
|
||||
|
||||
class orthopoly1d(np.poly1d):
|
||||
def __init__(
|
||||
self,
|
||||
roots: np.typing.ArrayLike,
|
||||
weights: np.typing.ArrayLike | None,
|
||||
hn: float = ...,
|
||||
kn: float = ...,
|
||||
wfunc = Optional[Callable[[float], float]], # noqa: UP007
|
||||
limits = tuple[float, float] | None,
|
||||
monic: bool = ...,
|
||||
eval_func: np.ufunc = ...,
|
||||
) -> None: ...
|
||||
@property
|
||||
def limits(self) -> tuple[float, float]: ...
|
||||
def weight_func(self, x: float) -> float: ...
|
||||
@overload
|
||||
def __call__(self, x: _ArrayLike0D) -> Any: ...
|
||||
@overload
|
||||
def __call__(self, x: np.poly1d) -> np.poly1d: ... # type: ignore[overload-overlap]
|
||||
@overload
|
||||
def __call__(self, x: np.typing.ArrayLike) -> np.ndarray: ...
|
||||
|
||||
def legendre(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def chebyt(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def chebyu(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def chebyc(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def chebys(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def jacobi(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
beta: _FloatingType,
|
||||
monic: bool = ...,
|
||||
) -> orthopoly1d: ...
|
||||
def laguerre(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def genlaguerre(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
monic: bool = ...,
|
||||
) -> orthopoly1d: ...
|
||||
def hermite(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def hermitenorm(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def gegenbauer(
|
||||
n: _IntegerType,
|
||||
alpha: _FloatingType,
|
||||
monic: bool = ...,
|
||||
) -> orthopoly1d: ...
|
||||
def sh_legendre(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def sh_chebyt(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def sh_chebyu(n: _IntegerType, monic: bool = ...) -> orthopoly1d: ...
|
||||
def sh_jacobi(
|
||||
n: _IntegerType,
|
||||
p: _FloatingType,
|
||||
q: _FloatingType,
|
||||
monic: bool = ...,
|
||||
) -> orthopoly1d: ...
|
||||
|
||||
# These functions are not public, but still need stubs because they
|
||||
# get checked in the tests.
|
||||
def _roots_hermite_asy(n: _IntegerType) -> _PointsAndWeights: ...
|
||||
@@ -0,0 +1,17 @@
|
||||
import mpmath
|
||||
|
||||
|
||||
def f(x):
|
||||
return (mpmath.pi + x + mpmath.sin(x)) / (2*mpmath.pi)
|
||||
|
||||
|
||||
# Note: 40 digits might be overkill; a few more digits than the default
|
||||
# might be sufficient.
|
||||
mpmath.mp.dps = 40
|
||||
ts = mpmath.taylor(f, -mpmath.pi, 20)
|
||||
p, q = mpmath.pade(ts, 9, 10)
|
||||
|
||||
p = [float(c) for c in p]
|
||||
q = [float(c) for c in q]
|
||||
print('p =', p)
|
||||
print('q =', q)
|
||||
@@ -0,0 +1,54 @@
|
||||
"""Precompute the polynomials for the asymptotic expansion of the
|
||||
generalized exponential integral.
|
||||
|
||||
Sources
|
||||
-------
|
||||
[1] NIST, Digital Library of Mathematical Functions,
|
||||
https://dlmf.nist.gov/8.20#ii
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
try:
|
||||
import sympy
|
||||
from sympy import Poly
|
||||
x = sympy.symbols('x')
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def generate_A(K):
|
||||
A = [Poly(1, x)]
|
||||
for k in range(K):
|
||||
A.append(Poly(1 - 2*k*x, x)*A[k] + Poly(x*(x + 1))*A[k].diff())
|
||||
return A
|
||||
|
||||
|
||||
WARNING = """\
|
||||
/* This file was automatically generated by _precompute/expn_asy.py.
|
||||
* Do not edit it manually!
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
print(__doc__)
|
||||
fn = os.path.join('..', 'cephes', 'expn.h')
|
||||
|
||||
K = 12
|
||||
A = generate_A(K)
|
||||
with open(fn + '.new', 'w') as f:
|
||||
f.write(WARNING)
|
||||
f.write(f"#define nA {len(A)}\n")
|
||||
for k, Ak in enumerate(A):
|
||||
', '.join([str(x.evalf(18)) for x in Ak.coeffs()])
|
||||
f.write(f"static const double A{k}[] = {{tmp}};\n")
|
||||
", ".join([f"A{k}" for k in range(K + 1)])
|
||||
f.write("static const double *A[] = {{tmp}};\n")
|
||||
", ".join([str(Ak.degree()) for Ak in A])
|
||||
f.write("static const int Adegs[] = {{tmp}};\n")
|
||||
os.rename(fn + '.new', fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
Precompute coefficients of Temme's asymptotic expansion for gammainc.
|
||||
|
||||
This takes about 8 hours to run on a 2.3 GHz Macbook Pro with 4GB ram.
|
||||
|
||||
Sources:
|
||||
[1] NIST, "Digital Library of Mathematical Functions",
|
||||
https://dlmf.nist.gov/
|
||||
|
||||
"""
|
||||
import os
|
||||
from scipy.special._precompute.utils import lagrange_inversion
|
||||
|
||||
try:
|
||||
import mpmath as mp
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def compute_a(n):
|
||||
"""a_k from DLMF 5.11.6"""
|
||||
a = [mp.sqrt(2)/2]
|
||||
for k in range(1, n):
|
||||
ak = a[-1]/k
|
||||
for j in range(1, len(a)):
|
||||
ak -= a[j]*a[-j]/(j + 1)
|
||||
ak /= a[0]*(1 + mp.mpf(1)/(k + 1))
|
||||
a.append(ak)
|
||||
return a
|
||||
|
||||
|
||||
def compute_g(n):
|
||||
"""g_k from DLMF 5.11.3/5.11.5"""
|
||||
a = compute_a(2*n)
|
||||
g = [mp.sqrt(2)*mp.rf(0.5, k)*a[2*k] for k in range(n)]
|
||||
return g
|
||||
|
||||
|
||||
def eta(lam):
|
||||
"""Function from DLMF 8.12.1 shifted to be centered at 0."""
|
||||
if lam > 0:
|
||||
return mp.sqrt(2*(lam - mp.log(lam + 1)))
|
||||
elif lam < 0:
|
||||
return -mp.sqrt(2*(lam - mp.log(lam + 1)))
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def compute_alpha(n):
|
||||
"""alpha_n from DLMF 8.12.13"""
|
||||
coeffs = mp.taylor(eta, 0, n - 1)
|
||||
return lagrange_inversion(coeffs)
|
||||
|
||||
|
||||
def compute_d(K, N):
|
||||
"""d_{k, n} from DLMF 8.12.12"""
|
||||
M = N + 2*K
|
||||
d0 = [-mp.mpf(1)/3]
|
||||
alpha = compute_alpha(M + 2)
|
||||
for n in range(1, M):
|
||||
d0.append((n + 2)*alpha[n+2])
|
||||
d = [d0]
|
||||
g = compute_g(K)
|
||||
for k in range(1, K):
|
||||
dk = []
|
||||
for n in range(M - 2*k):
|
||||
dk.append((-1)**k*g[k]*d[0][n] + (n + 2)*d[k-1][n+2])
|
||||
d.append(dk)
|
||||
for k in range(K):
|
||||
d[k] = d[k][:N]
|
||||
return d
|
||||
|
||||
|
||||
header = \
|
||||
r"""/* This file was automatically generated by _precomp/gammainc.py.
|
||||
* Do not edit it manually!
|
||||
*/
|
||||
|
||||
#ifndef IGAM_H
|
||||
#define IGAM_H
|
||||
|
||||
#define K {}
|
||||
#define N {}
|
||||
|
||||
static const double d[K][N] =
|
||||
{{"""
|
||||
|
||||
footer = \
|
||||
r"""
|
||||
#endif
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
print(__doc__)
|
||||
K = 25
|
||||
N = 25
|
||||
with mp.workdps(50):
|
||||
d = compute_d(K, N)
|
||||
fn = os.path.join(os.path.dirname(__file__), '..', 'cephes', 'igam.h')
|
||||
with open(fn + '.new', 'w') as f:
|
||||
f.write(header.format(K, N))
|
||||
for k, row in enumerate(d):
|
||||
row = [mp.nstr(x, 17, min_fixed=0, max_fixed=0) for x in row]
|
||||
f.write('{')
|
||||
f.write(", ".join(row))
|
||||
if k < K - 1:
|
||||
f.write('},\n')
|
||||
else:
|
||||
f.write('}};\n')
|
||||
f.write(footer)
|
||||
os.rename(fn + '.new', fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,124 @@
|
||||
"""Compute gammainc and gammaincc for large arguments and parameters
|
||||
and save the values to data files for use in tests. We can't just
|
||||
compare to mpmath's gammainc in test_mpmath.TestSystematic because it
|
||||
would take too long.
|
||||
|
||||
Note that mpmath's gammainc is computed using hypercomb, but since it
|
||||
doesn't allow the user to increase the maximum number of terms used in
|
||||
the series it doesn't converge for many arguments. To get around this
|
||||
we copy the mpmath implementation but use more terms.
|
||||
|
||||
This takes about 17 minutes to run on a 2.3 GHz Macbook Pro with 4GB
|
||||
ram.
|
||||
|
||||
Sources:
|
||||
[1] Fredrik Johansson and others. mpmath: a Python library for
|
||||
arbitrary-precision floating-point arithmetic (version 0.19),
|
||||
December 2013. http://mpmath.org/.
|
||||
|
||||
"""
|
||||
import os
|
||||
from time import time
|
||||
import numpy as np
|
||||
from numpy import pi
|
||||
|
||||
from scipy.special._mptestutils import mpf2float
|
||||
|
||||
try:
|
||||
import mpmath as mp
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def gammainc(a, x, dps=50, maxterms=10**8):
|
||||
"""Compute gammainc exactly like mpmath does but allow for more
|
||||
summands in hypercomb. See
|
||||
|
||||
mpmath/functions/expintegrals.py#L134
|
||||
|
||||
in the mpmath GitHub repository.
|
||||
|
||||
"""
|
||||
with mp.workdps(dps):
|
||||
z, a, b = mp.mpf(a), mp.mpf(x), mp.mpf(x)
|
||||
G = [z]
|
||||
negb = mp.fneg(b, exact=True)
|
||||
|
||||
def h(z):
|
||||
T1 = [mp.exp(negb), b, z], [1, z, -1], [], G, [1], [1+z], b
|
||||
return (T1,)
|
||||
|
||||
res = mp.hypercomb(h, [z], maxterms=maxterms)
|
||||
return mpf2float(res)
|
||||
|
||||
|
||||
def gammaincc(a, x, dps=50, maxterms=10**8):
|
||||
"""Compute gammaincc exactly like mpmath does but allow for more
|
||||
terms in hypercomb. See
|
||||
|
||||
mpmath/functions/expintegrals.py#L187
|
||||
|
||||
in the mpmath GitHub repository.
|
||||
|
||||
"""
|
||||
with mp.workdps(dps):
|
||||
z, a = a, x
|
||||
|
||||
if mp.isint(z):
|
||||
try:
|
||||
# mpmath has a fast integer path
|
||||
return mpf2float(mp.gammainc(z, a=a, regularized=True))
|
||||
except mp.libmp.NoConvergence:
|
||||
pass
|
||||
nega = mp.fneg(a, exact=True)
|
||||
G = [z]
|
||||
# Use 2F0 series when possible; fall back to lower gamma representation
|
||||
try:
|
||||
def h(z):
|
||||
r = z-1
|
||||
return [([mp.exp(nega), a], [1, r], [], G, [1, -r], [], 1/nega)]
|
||||
return mpf2float(mp.hypercomb(h, [z], force_series=True))
|
||||
except mp.libmp.NoConvergence:
|
||||
def h(z):
|
||||
T1 = [], [1, z-1], [z], G, [], [], 0
|
||||
T2 = [-mp.exp(nega), a, z], [1, z, -1], [], G, [1], [1+z], a
|
||||
return T1, T2
|
||||
return mpf2float(mp.hypercomb(h, [z], maxterms=maxterms))
|
||||
|
||||
|
||||
def main():
|
||||
t0 = time()
|
||||
# It would be nice to have data for larger values, but either this
|
||||
# requires prohibitively large precision (dps > 800) or mpmath has
|
||||
# a bug. For example, gammainc(1e20, 1e20, dps=800) returns a
|
||||
# value around 0.03, while the true value should be close to 0.5
|
||||
# (DLMF 8.12.15).
|
||||
print(__doc__)
|
||||
pwd = os.path.dirname(__file__)
|
||||
r = np.logspace(4, 14, 30)
|
||||
ltheta = np.logspace(np.log10(pi/4), np.log10(np.arctan(0.6)), 30)
|
||||
utheta = np.logspace(np.log10(pi/4), np.log10(np.arctan(1.4)), 30)
|
||||
|
||||
regimes = [(gammainc, ltheta), (gammaincc, utheta)]
|
||||
for func, theta in regimes:
|
||||
rg, thetag = np.meshgrid(r, theta)
|
||||
a, x = rg*np.cos(thetag), rg*np.sin(thetag)
|
||||
a, x = a.flatten(), x.flatten()
|
||||
dataset = []
|
||||
for i, (a0, x0) in enumerate(zip(a, x)):
|
||||
if func == gammaincc:
|
||||
# Exploit the fast integer path in gammaincc whenever
|
||||
# possible so that the computation doesn't take too
|
||||
# long
|
||||
a0, x0 = np.floor(a0), np.floor(x0)
|
||||
dataset.append((a0, x0, func(a0, x0)))
|
||||
dataset = np.array(dataset)
|
||||
filename = os.path.join(pwd, '..', 'tests', 'data', 'local',
|
||||
f'{func.__name__}.txt')
|
||||
np.savetxt(filename, dataset)
|
||||
|
||||
print(f"{(time() - t0)/60} minutes elapsed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,484 @@
|
||||
"""This script evaluates scipy's implementation of hyp2f1 against mpmath's.
|
||||
|
||||
Author: Albert Steppi
|
||||
|
||||
This script is long running and generates a large output file. With default
|
||||
arguments, the generated file is roughly 700MB in size and it takes around
|
||||
40 minutes using an Intel(R) Core(TM) i5-8250U CPU with n_jobs set to 8
|
||||
(full utilization). There are optional arguments which can be used to restrict
|
||||
(or enlarge) the computations performed. These are described below.
|
||||
The output of this script can be analyzed to identify suitable test cases and
|
||||
to find parameter and argument regions where hyp2f1 needs to be improved.
|
||||
|
||||
The script has one mandatory positional argument for specifying the path to
|
||||
the location where the output file is to be placed, and 4 optional arguments
|
||||
--n_jobs, --grid_size, --regions, and --parameter_groups. --n_jobs specifies
|
||||
the number of processes to use if running in parallel. The default value is 1.
|
||||
The other optional arguments are explained below.
|
||||
|
||||
Produces a tab separated values file with 11 columns. The first four columns
|
||||
contain the parameters a, b, c and the argument z. The next two contain |z| and
|
||||
a region code for which region of the complex plane belongs to. The regions are
|
||||
|
||||
0) z == 1
|
||||
1) |z| < 0.9 and real(z) >= 0
|
||||
2) |z| <= 1 and real(z) < 0
|
||||
3) 0.9 <= |z| <= 1 and |1 - z| < 0.9:
|
||||
4) 0.9 <= |z| <= 1 and |1 - z| >= 0.9 and real(z) >= 0:
|
||||
5) 1 < |z| < 1.1 and |1 - z| >= 0.9 and real(z) >= 0
|
||||
6) |z| > 1 and not in 5)
|
||||
|
||||
The --regions optional argument allows the user to specify a list of regions
|
||||
to which computation will be restricted.
|
||||
|
||||
Parameters a, b, c are taken from a 10 * 10 * 10 grid with values at
|
||||
|
||||
-16, -8, -4, -2, -1, 1, 2, 4, 8, 16
|
||||
|
||||
with random perturbations applied.
|
||||
|
||||
There are 9 parameter groups handling the following cases.
|
||||
|
||||
1) A, B, C, B - A, C - A, C - B, C - A - B all non-integral.
|
||||
2) B - A integral
|
||||
3) C - A integral
|
||||
4) C - B integral
|
||||
5) C - A - B integral
|
||||
6) A integral
|
||||
7) B integral
|
||||
8) C integral
|
||||
9) Wider range with c - a - b > 0.
|
||||
|
||||
The seventh column of the output file is an integer between 1 and 8 specifying
|
||||
the parameter group as above.
|
||||
|
||||
The --parameter_groups optional argument allows the user to specify a list of
|
||||
parameter groups to which computation will be restricted.
|
||||
|
||||
The argument z is taken from a grid in the box
|
||||
-box_size <= real(z) <= box_size, -box_size <= imag(z) <= box_size.
|
||||
with grid size specified using the optional command line argument --grid_size,
|
||||
and box_size specified with the command line argument --box_size.
|
||||
The default value of grid_size is 20 and the default value of box_size is 2.0,
|
||||
yielding a 20 * 20 grid in the box with corners -2-2j, -2+2j, 2-2j, 2+2j.
|
||||
|
||||
The final four columns have the expected value of hyp2f1 for the given
|
||||
parameters and argument as calculated with mpmath, the observed value
|
||||
calculated with scipy's hyp2f1, the relative error, and the absolute error.
|
||||
|
||||
As special cases of hyp2f1 are moved from the original Fortran implementation
|
||||
into Cython, this script can be used to ensure that no regressions occur and
|
||||
to point out where improvements are needed.
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import csv
|
||||
import argparse
|
||||
import numpy as np
|
||||
from itertools import product
|
||||
from multiprocessing import Pool
|
||||
|
||||
|
||||
from scipy.special import hyp2f1
|
||||
from scipy.special.tests.test_hyp2f1 import mp_hyp2f1
|
||||
|
||||
|
||||
def get_region(z):
|
||||
"""Assign numbers for regions where hyp2f1 must be handled differently."""
|
||||
if z == 1 + 0j:
|
||||
return 0
|
||||
elif abs(z) < 0.9 and z.real >= 0:
|
||||
return 1
|
||||
elif abs(z) <= 1 and z.real < 0:
|
||||
return 2
|
||||
elif 0.9 <= abs(z) <= 1 and abs(1 - z) < 0.9:
|
||||
return 3
|
||||
elif 0.9 <= abs(z) <= 1 and abs(1 - z) >= 0.9:
|
||||
return 4
|
||||
elif 1 < abs(z) < 1.1 and abs(1 - z) >= 0.9 and z.real >= 0:
|
||||
return 5
|
||||
else:
|
||||
return 6
|
||||
|
||||
|
||||
def get_result(a, b, c, z, group):
|
||||
"""Get results for given parameter and value combination."""
|
||||
expected, observed = mp_hyp2f1(a, b, c, z), hyp2f1(a, b, c, z)
|
||||
if (
|
||||
np.isnan(observed) and np.isnan(expected) or
|
||||
expected == observed
|
||||
):
|
||||
relative_error = 0.0
|
||||
absolute_error = 0.0
|
||||
elif np.isnan(observed):
|
||||
# Set error to infinity if result is nan when not expected to be.
|
||||
# Makes results easier to interpret.
|
||||
relative_error = float("inf")
|
||||
absolute_error = float("inf")
|
||||
else:
|
||||
absolute_error = abs(expected - observed)
|
||||
relative_error = absolute_error / abs(expected)
|
||||
|
||||
return (
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
z,
|
||||
abs(z),
|
||||
get_region(z),
|
||||
group,
|
||||
expected,
|
||||
observed,
|
||||
relative_error,
|
||||
absolute_error,
|
||||
)
|
||||
|
||||
|
||||
def get_result_no_mp(a, b, c, z, group):
|
||||
"""Get results for given parameter and value combination."""
|
||||
expected, observed = complex('nan'), hyp2f1(a, b, c, z)
|
||||
relative_error, absolute_error = float('nan'), float('nan')
|
||||
return (
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
z,
|
||||
abs(z),
|
||||
get_region(z),
|
||||
group,
|
||||
expected,
|
||||
observed,
|
||||
relative_error,
|
||||
absolute_error,
|
||||
)
|
||||
|
||||
|
||||
def get_results(params, Z, n_jobs=1, compute_mp=True):
|
||||
"""Batch compute results for multiple parameter and argument values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
params : iterable
|
||||
iterable of tuples of floats (a, b, c) specifying parameter values
|
||||
a, b, c for hyp2f1
|
||||
Z : iterable of complex
|
||||
Arguments at which to evaluate hyp2f1
|
||||
n_jobs : Optional[int]
|
||||
Number of jobs for parallel execution.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
List of tuples of results values. See return value in source code
|
||||
of `get_result`.
|
||||
"""
|
||||
input_ = (
|
||||
(a, b, c, z, group) for (a, b, c, group), z in product(params, Z)
|
||||
)
|
||||
|
||||
with Pool(n_jobs) as pool:
|
||||
rows = pool.starmap(
|
||||
get_result if compute_mp else get_result_no_mp,
|
||||
input_
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def _make_hyp2f1_test_case(a, b, c, z, rtol):
|
||||
"""Generate string for single test case as used in test_hyp2f1.py."""
|
||||
expected = mp_hyp2f1(a, b, c, z)
|
||||
return (
|
||||
" pytest.param(\n"
|
||||
" Hyp2f1TestCase(\n"
|
||||
f" a={a},\n"
|
||||
f" b={b},\n"
|
||||
f" c={c},\n"
|
||||
f" z={z},\n"
|
||||
f" expected={expected},\n"
|
||||
f" rtol={rtol},\n"
|
||||
" ),\n"
|
||||
" ),"
|
||||
)
|
||||
|
||||
|
||||
def make_hyp2f1_test_cases(rows):
|
||||
"""Generate string for a list of test cases for test_hyp2f1.py.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rows : list
|
||||
List of lists of the form [a, b, c, z, rtol] where a, b, c, z are
|
||||
parameters and the argument for hyp2f1 and rtol is an expected
|
||||
relative error for the associated test case.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
String for a list of test cases. The output string can be printed
|
||||
or saved to a file and then copied into an argument for
|
||||
`pytest.mark.parameterize` within `scipy.special.tests.test_hyp2f1.py`.
|
||||
"""
|
||||
result = "[\n"
|
||||
result += '\n'.join(
|
||||
_make_hyp2f1_test_case(a, b, c, z, rtol)
|
||||
for a, b, c, z, rtol in rows
|
||||
)
|
||||
result += "\n]"
|
||||
return result
|
||||
|
||||
|
||||
def main(
|
||||
outpath,
|
||||
n_jobs=1,
|
||||
box_size=2.0,
|
||||
grid_size=20,
|
||||
regions=None,
|
||||
parameter_groups=None,
|
||||
compute_mp=True,
|
||||
):
|
||||
outpath = os.path.realpath(os.path.expanduser(outpath))
|
||||
|
||||
random_state = np.random.RandomState(1234)
|
||||
# Parameters a, b, c selected near these values.
|
||||
root_params = np.array(
|
||||
[-16, -8, -4, -2, -1, 1, 2, 4, 8, 16]
|
||||
)
|
||||
# Perturbations to apply to root values.
|
||||
perturbations = 0.1 * random_state.random_sample(
|
||||
size=(3, len(root_params))
|
||||
)
|
||||
|
||||
params = []
|
||||
# Parameter group 1
|
||||
# -----------------
|
||||
# No integer differences. This has been confirmed for the above seed.
|
||||
A = root_params + perturbations[0, :]
|
||||
B = root_params + perturbations[1, :]
|
||||
C = root_params + perturbations[2, :]
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 1) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 2
|
||||
# -----------------
|
||||
# B - A an integer
|
||||
A = root_params + 0.5
|
||||
B = root_params + 0.5
|
||||
C = root_params + perturbations[1, :]
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 2) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 3
|
||||
# -----------------
|
||||
# C - A an integer
|
||||
A = root_params + 0.5
|
||||
B = root_params + perturbations[1, :]
|
||||
C = root_params + 0.5
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 3) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 4
|
||||
# -----------------
|
||||
# C - B an integer
|
||||
A = root_params + perturbations[0, :]
|
||||
B = root_params + 0.5
|
||||
C = root_params + 0.5
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 4) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 5
|
||||
# -----------------
|
||||
# C - A - B an integer
|
||||
A = root_params + 0.25
|
||||
B = root_params + 0.25
|
||||
C = root_params + 0.5
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 5) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 6
|
||||
# -----------------
|
||||
# A an integer
|
||||
A = root_params
|
||||
B = root_params + perturbations[0, :]
|
||||
C = root_params + perturbations[1, :]
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 6) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 7
|
||||
# -----------------
|
||||
# B an integer
|
||||
A = root_params + perturbations[0, :]
|
||||
B = root_params
|
||||
C = root_params + perturbations[1, :]
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 7) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 8
|
||||
# -----------------
|
||||
# C an integer
|
||||
A = root_params + perturbations[0, :]
|
||||
B = root_params + perturbations[1, :]
|
||||
C = root_params
|
||||
params.extend(
|
||||
sorted(
|
||||
((a, b, c, 8) for a, b, c in product(A, B, C)),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
)
|
||||
|
||||
# Parameter group 9
|
||||
# -----------------
|
||||
# Wide range of magnitudes, c - a - b > 0.
|
||||
phi = (1 + np.sqrt(5))/2
|
||||
P = phi**np.arange(16)
|
||||
P = np.hstack([-P, P])
|
||||
group_9_params = sorted(
|
||||
(
|
||||
(a, b, c, 9) for a, b, c in product(P, P, P) if c - a - b > 0
|
||||
),
|
||||
key=lambda x: max(abs(x[0]), abs(x[1])),
|
||||
)
|
||||
|
||||
if parameter_groups is not None:
|
||||
# Group 9 params only used if specified in arguments.
|
||||
params.extend(group_9_params)
|
||||
params = [
|
||||
(a, b, c, group) for a, b, c, group in params
|
||||
if group in parameter_groups
|
||||
]
|
||||
|
||||
# grid_size * grid_size grid in box with corners
|
||||
# -2 - 2j, -2 + 2j, 2 - 2j, 2 + 2j
|
||||
X, Y = np.meshgrid(
|
||||
np.linspace(-box_size, box_size, grid_size),
|
||||
np.linspace(-box_size, box_size, grid_size)
|
||||
)
|
||||
Z = X + Y * 1j
|
||||
Z = Z.flatten().tolist()
|
||||
# Add z = 1 + 0j (region 0).
|
||||
Z.append(1 + 0j)
|
||||
if regions is not None:
|
||||
Z = [z for z in Z if get_region(z) in regions]
|
||||
|
||||
# Evaluate scipy and mpmath's hyp2f1 for all parameter combinations
|
||||
# above against all arguments in the grid Z
|
||||
rows = get_results(params, Z, n_jobs=n_jobs, compute_mp=compute_mp)
|
||||
|
||||
with open(outpath, "w", newline="") as f:
|
||||
writer = csv.writer(f, delimiter="\t")
|
||||
writer.writerow(
|
||||
[
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"z",
|
||||
"|z|",
|
||||
"region",
|
||||
"parameter_group",
|
||||
"expected", # mpmath's hyp2f1
|
||||
"observed", # scipy's hyp2f1
|
||||
"relative_error",
|
||||
"absolute_error",
|
||||
]
|
||||
)
|
||||
for row in rows:
|
||||
writer.writerow(row)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Test scipy's hyp2f1 against mpmath's on a grid in the"
|
||||
" complex plane over a grid of parameter values. Saves output to file"
|
||||
" specified in positional argument \"outpath\"."
|
||||
" Caution: With default arguments, the generated output file is"
|
||||
" roughly 700MB in size. Script may take several hours to finish if"
|
||||
" \"--n_jobs\" is set to 1."
|
||||
)
|
||||
parser.add_argument(
|
||||
"outpath", type=str, help="Path to output tsv file."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--n_jobs",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Number of jobs for multiprocessing.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--box_size",
|
||||
type=float,
|
||||
default=2.0,
|
||||
help="hyp2f1 is evaluated in box of side_length 2*box_size centered"
|
||||
" at the origin."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--grid_size",
|
||||
type=int,
|
||||
default=20,
|
||||
help="hyp2f1 is evaluated on grid_size * grid_size grid in box of side"
|
||||
" length 2*box_size centered at the origin."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--parameter_groups",
|
||||
type=int,
|
||||
nargs='+',
|
||||
default=None,
|
||||
help="Restrict to supplied parameter groups. See the Docstring for"
|
||||
" this module for more info on parameter groups. Calculate for all"
|
||||
" parameter groups by default."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--regions",
|
||||
type=int,
|
||||
nargs='+',
|
||||
default=None,
|
||||
help="Restrict to argument z only within the supplied regions. See"
|
||||
" the Docstring for this module for more info on regions. Calculate"
|
||||
" for all regions by default."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no_mp",
|
||||
action='store_true',
|
||||
help="If this flag is set, do not compute results with mpmath. Saves"
|
||||
" time if results have already been computed elsewhere. Fills in"
|
||||
" \"expected\" column with None values."
|
||||
)
|
||||
args = parser.parse_args()
|
||||
compute_mp = not args.no_mp
|
||||
print(args.parameter_groups)
|
||||
main(
|
||||
args.outpath,
|
||||
n_jobs=args.n_jobs,
|
||||
box_size=args.box_size,
|
||||
grid_size=args.grid_size,
|
||||
parameter_groups=args.parameter_groups,
|
||||
regions=args.regions,
|
||||
compute_mp=compute_mp,
|
||||
)
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Compute a Pade approximation for the principal branch of the
|
||||
Lambert W function around 0 and compare it to various other
|
||||
approximations.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
import mpmath
|
||||
import matplotlib.pyplot as plt
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def lambertw_pade():
|
||||
derivs = [mpmath.diff(mpmath.lambertw, 0, n=n) for n in range(6)]
|
||||
p, q = mpmath.pade(derivs, 3, 2)
|
||||
return p, q
|
||||
|
||||
|
||||
def main():
|
||||
print(__doc__)
|
||||
with mpmath.workdps(50):
|
||||
p, q = lambertw_pade()
|
||||
p, q = p[::-1], q[::-1]
|
||||
print(f"p = {p}")
|
||||
print(f"q = {q}")
|
||||
|
||||
x, y = np.linspace(-1.5, 1.5, 75), np.linspace(-1.5, 1.5, 75)
|
||||
x, y = np.meshgrid(x, y)
|
||||
z = x + 1j*y
|
||||
lambertw_std = []
|
||||
for z0 in z.flatten():
|
||||
lambertw_std.append(complex(mpmath.lambertw(z0)))
|
||||
lambertw_std = np.array(lambertw_std).reshape(x.shape)
|
||||
|
||||
fig, axes = plt.subplots(nrows=3, ncols=1)
|
||||
# Compare Pade approximation to true result
|
||||
p = np.array([float(p0) for p0 in p])
|
||||
q = np.array([float(q0) for q0 in q])
|
||||
pade_approx = np.polyval(p, z)/np.polyval(q, z)
|
||||
pade_err = abs(pade_approx - lambertw_std)
|
||||
axes[0].pcolormesh(x, y, pade_err)
|
||||
# Compare two terms of asymptotic series to true result
|
||||
asy_approx = np.log(z) - np.log(np.log(z))
|
||||
asy_err = abs(asy_approx - lambertw_std)
|
||||
axes[1].pcolormesh(x, y, asy_err)
|
||||
# Compare two terms of the series around the branch point to the
|
||||
# true result
|
||||
p = np.sqrt(2*(np.exp(1)*z + 1))
|
||||
series_approx = -1 + p - p**2/3
|
||||
series_err = abs(series_approx - lambertw_std)
|
||||
im = axes[2].pcolormesh(x, y, series_err)
|
||||
|
||||
fig.colorbar(im, ax=axes.ravel().tolist())
|
||||
plt.show()
|
||||
|
||||
fig, ax = plt.subplots(nrows=1, ncols=1)
|
||||
pade_better = pade_err < asy_err
|
||||
im = ax.pcolormesh(x, y, pade_better)
|
||||
t = np.linspace(-0.3, 0.3)
|
||||
ax.plot(-2.5*abs(t) - 0.2, t, 'r')
|
||||
fig.colorbar(im, ax=ax)
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,43 @@
|
||||
"""Precompute series coefficients for log-Gamma."""
|
||||
|
||||
try:
|
||||
import mpmath
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def stirling_series(N):
|
||||
with mpmath.workdps(100):
|
||||
coeffs = [mpmath.bernoulli(2*n)/(2*n*(2*n - 1))
|
||||
for n in range(1, N + 1)]
|
||||
return coeffs
|
||||
|
||||
|
||||
def taylor_series_at_1(N):
|
||||
coeffs = []
|
||||
with mpmath.workdps(100):
|
||||
coeffs.append(-mpmath.euler)
|
||||
for n in range(2, N + 1):
|
||||
coeffs.append((-1)**n*mpmath.zeta(n)/n)
|
||||
return coeffs
|
||||
|
||||
|
||||
def main():
|
||||
print(__doc__)
|
||||
print()
|
||||
stirling_coeffs = [mpmath.nstr(x, 20, min_fixed=0, max_fixed=0)
|
||||
for x in stirling_series(8)[::-1]]
|
||||
taylor_coeffs = [mpmath.nstr(x, 20, min_fixed=0, max_fixed=0)
|
||||
for x in taylor_series_at_1(23)[::-1]]
|
||||
print("Stirling series coefficients")
|
||||
print("----------------------------")
|
||||
print("\n".join(stirling_coeffs))
|
||||
print()
|
||||
print("Taylor series coefficients")
|
||||
print("--------------------------")
|
||||
print("\n".join(taylor_coeffs))
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
Convergence regions of the expansions used in ``struve.c``
|
||||
|
||||
Note that for v >> z both functions tend rapidly to 0,
|
||||
and for v << -z, they tend to infinity.
|
||||
|
||||
The floating-point functions over/underflow in the lower left and right
|
||||
corners of the figure.
|
||||
|
||||
|
||||
Figure legend
|
||||
=============
|
||||
|
||||
Red region
|
||||
Power series is close (1e-12) to the mpmath result
|
||||
|
||||
Blue region
|
||||
Asymptotic series is close to the mpmath result
|
||||
|
||||
Green region
|
||||
Bessel series is close to the mpmath result
|
||||
|
||||
Dotted colored lines
|
||||
Boundaries of the regions
|
||||
|
||||
Solid colored lines
|
||||
Boundaries estimated by the routine itself. These will be used
|
||||
for determining which of the results to use.
|
||||
|
||||
Black dashed line
|
||||
The line z = 0.7*|v| + 12
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
import mpmath
|
||||
|
||||
|
||||
def err_metric(a, b, atol=1e-290):
|
||||
m = abs(a - b) / (atol + abs(b))
|
||||
m[np.isinf(b) & (a == b)] = 0
|
||||
return m
|
||||
|
||||
|
||||
def do_plot(is_h=True):
|
||||
from scipy.special._ufuncs import (_struve_power_series,
|
||||
_struve_asymp_large_z,
|
||||
_struve_bessel_series)
|
||||
|
||||
vs = np.linspace(-1000, 1000, 91)
|
||||
zs = np.sort(np.r_[1e-5, 1.0, np.linspace(0, 700, 91)[1:]])
|
||||
|
||||
rp = _struve_power_series(vs[:,None], zs[None,:], is_h)
|
||||
ra = _struve_asymp_large_z(vs[:,None], zs[None,:], is_h)
|
||||
rb = _struve_bessel_series(vs[:,None], zs[None,:], is_h)
|
||||
|
||||
mpmath.mp.dps = 50
|
||||
if is_h:
|
||||
def sh(v, z):
|
||||
return float(mpmath.struveh(mpmath.mpf(v), mpmath.mpf(z)))
|
||||
else:
|
||||
def sh(v, z):
|
||||
return float(mpmath.struvel(mpmath.mpf(v), mpmath.mpf(z)))
|
||||
ex = np.vectorize(sh, otypes='d')(vs[:,None], zs[None,:])
|
||||
|
||||
err_a = err_metric(ra[0], ex) + 1e-300
|
||||
err_p = err_metric(rp[0], ex) + 1e-300
|
||||
err_b = err_metric(rb[0], ex) + 1e-300
|
||||
|
||||
err_est_a = abs(ra[1]/ra[0])
|
||||
err_est_p = abs(rp[1]/rp[0])
|
||||
err_est_b = abs(rb[1]/rb[0])
|
||||
|
||||
z_cutoff = 0.7*abs(vs) + 12
|
||||
|
||||
levels = [-1000, -12]
|
||||
|
||||
plt.cla()
|
||||
|
||||
plt.hold(1)
|
||||
plt.contourf(vs, zs, np.log10(err_p).T,
|
||||
levels=levels, colors=['r', 'r'], alpha=0.1)
|
||||
plt.contourf(vs, zs, np.log10(err_a).T,
|
||||
levels=levels, colors=['b', 'b'], alpha=0.1)
|
||||
plt.contourf(vs, zs, np.log10(err_b).T,
|
||||
levels=levels, colors=['g', 'g'], alpha=0.1)
|
||||
|
||||
plt.contour(vs, zs, np.log10(err_p).T,
|
||||
levels=levels, colors=['r', 'r'], linestyles=[':', ':'])
|
||||
plt.contour(vs, zs, np.log10(err_a).T,
|
||||
levels=levels, colors=['b', 'b'], linestyles=[':', ':'])
|
||||
plt.contour(vs, zs, np.log10(err_b).T,
|
||||
levels=levels, colors=['g', 'g'], linestyles=[':', ':'])
|
||||
|
||||
lp = plt.contour(vs, zs, np.log10(err_est_p).T,
|
||||
levels=levels, colors=['r', 'r'], linestyles=['-', '-'])
|
||||
la = plt.contour(vs, zs, np.log10(err_est_a).T,
|
||||
levels=levels, colors=['b', 'b'], linestyles=['-', '-'])
|
||||
lb = plt.contour(vs, zs, np.log10(err_est_b).T,
|
||||
levels=levels, colors=['g', 'g'], linestyles=['-', '-'])
|
||||
|
||||
plt.clabel(lp, fmt={-1000: 'P', -12: 'P'})
|
||||
plt.clabel(la, fmt={-1000: 'A', -12: 'A'})
|
||||
plt.clabel(lb, fmt={-1000: 'B', -12: 'B'})
|
||||
|
||||
plt.plot(vs, z_cutoff, 'k--')
|
||||
|
||||
plt.xlim(vs.min(), vs.max())
|
||||
plt.ylim(zs.min(), zs.max())
|
||||
|
||||
plt.xlabel('v')
|
||||
plt.ylabel('z')
|
||||
|
||||
|
||||
def main():
|
||||
plt.clf()
|
||||
plt.subplot(121)
|
||||
do_plot(True)
|
||||
plt.title('Struve H')
|
||||
|
||||
plt.subplot(122)
|
||||
do_plot(False)
|
||||
plt.title('Struve L')
|
||||
|
||||
plt.savefig('struve_convergence.png')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,38 @@
|
||||
try:
|
||||
import mpmath as mp
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
from sympy.abc import x
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def lagrange_inversion(a):
|
||||
"""Given a series
|
||||
|
||||
f(x) = a[1]*x + a[2]*x**2 + ... + a[n-1]*x**(n - 1),
|
||||
|
||||
use the Lagrange inversion formula to compute a series
|
||||
|
||||
g(x) = b[1]*x + b[2]*x**2 + ... + b[n-1]*x**(n - 1)
|
||||
|
||||
so that f(g(x)) = g(f(x)) = x mod x**n. We must have a[0] = 0, so
|
||||
necessarily b[0] = 0 too.
|
||||
|
||||
The algorithm is naive and could be improved, but speed isn't an
|
||||
issue here and it's easy to read.
|
||||
|
||||
"""
|
||||
n = len(a)
|
||||
f = sum(a[i]*x**i for i in range(n))
|
||||
h = (x/f).series(x, 0, n).removeO()
|
||||
hpower = [h**0]
|
||||
for k in range(n):
|
||||
hpower.append((hpower[-1]*h).expand())
|
||||
b = [mp.mpf(0)]
|
||||
for k in range(1, n):
|
||||
b.append(hpower[k].coeff(x, k - 1)/k)
|
||||
b = [mp.mpf(x) for x in b]
|
||||
return b
|
||||
@@ -0,0 +1,342 @@
|
||||
"""Precompute coefficients of several series expansions
|
||||
of Wright's generalized Bessel function Phi(a, b, x).
|
||||
|
||||
See https://dlmf.nist.gov/10.46.E1 with rho=a, beta=b, z=x.
|
||||
"""
|
||||
from argparse import ArgumentParser, RawTextHelpFormatter
|
||||
import numpy as np
|
||||
from scipy.integrate import quad
|
||||
from scipy.optimize import minimize_scalar, curve_fit
|
||||
from time import time
|
||||
|
||||
try:
|
||||
import sympy
|
||||
from sympy import EulerGamma, Rational, S, Sum, \
|
||||
factorial, gamma, gammasimp, pi, polygamma, symbols, zeta
|
||||
from sympy.polys.polyfuncs import horner
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def series_small_a():
|
||||
"""Tylor series expansion of Phi(a, b, x) in a=0 up to order 5.
|
||||
"""
|
||||
order = 5
|
||||
a, b, x, k = symbols("a b x k")
|
||||
A = [] # terms with a
|
||||
X = [] # terms with x
|
||||
B = [] # terms with b (polygammas)
|
||||
# Phi(a, b, x) = exp(x)/gamma(b) * sum(A[i] * X[i] * B[i])
|
||||
expression = Sum(x**k/factorial(k)/gamma(a*k+b), (k, 0, S.Infinity))
|
||||
expression = gamma(b)/sympy.exp(x) * expression
|
||||
|
||||
# nth term of taylor series in a=0: a^n/n! * (d^n Phi(a, b, x)/da^n at a=0)
|
||||
for n in range(0, order+1):
|
||||
term = expression.diff(a, n).subs(a, 0).simplify().doit()
|
||||
# set the whole bracket involving polygammas to 1
|
||||
x_part = (term.subs(polygamma(0, b), 1)
|
||||
.replace(polygamma, lambda *args: 0))
|
||||
# sign convention: x part always positive
|
||||
x_part *= (-1)**n
|
||||
|
||||
A.append(a**n/factorial(n))
|
||||
X.append(horner(x_part))
|
||||
B.append(horner((term/x_part).simplify()))
|
||||
|
||||
s = "Tylor series expansion of Phi(a, b, x) in a=0 up to order 5.\n"
|
||||
s += "Phi(a, b, x) = exp(x)/gamma(b) * sum(A[i] * X[i] * B[i], i=0..5)\n"
|
||||
for name, c in zip(['A', 'X', 'B'], [A, X, B]):
|
||||
for i in range(len(c)):
|
||||
s += f"\n{name}[{i}] = " + str(c[i])
|
||||
return s
|
||||
|
||||
|
||||
# expansion of digamma
|
||||
def dg_series(z, n):
|
||||
"""Symbolic expansion of digamma(z) in z=0 to order n.
|
||||
|
||||
See https://dlmf.nist.gov/5.7.E4 and with https://dlmf.nist.gov/5.5.E2
|
||||
"""
|
||||
k = symbols("k")
|
||||
return -1/z - EulerGamma + \
|
||||
sympy.summation((-1)**k * zeta(k) * z**(k-1), (k, 2, n+1))
|
||||
|
||||
|
||||
def pg_series(k, z, n):
|
||||
"""Symbolic expansion of polygamma(k, z) in z=0 to order n."""
|
||||
return sympy.diff(dg_series(z, n+k), z, k)
|
||||
|
||||
|
||||
def series_small_a_small_b():
|
||||
"""Tylor series expansion of Phi(a, b, x) in a=0 and b=0 up to order 5.
|
||||
|
||||
Be aware of cancellation of poles in b=0 of digamma(b)/Gamma(b) and
|
||||
polygamma functions.
|
||||
|
||||
digamma(b)/Gamma(b) = -1 - 2*M_EG*b + O(b^2)
|
||||
digamma(b)^2/Gamma(b) = 1/b + 3*M_EG + b*(-5/12*PI^2+7/2*M_EG^2) + O(b^2)
|
||||
polygamma(1, b)/Gamma(b) = 1/b + M_EG + b*(1/12*PI^2 + 1/2*M_EG^2) + O(b^2)
|
||||
and so on.
|
||||
"""
|
||||
order = 5
|
||||
a, b, x, k = symbols("a b x k")
|
||||
M_PI, M_EG, M_Z3 = symbols("M_PI M_EG M_Z3")
|
||||
c_subs = {pi: M_PI, EulerGamma: M_EG, zeta(3): M_Z3}
|
||||
A = [] # terms with a
|
||||
X = [] # terms with x
|
||||
B = [] # terms with b (polygammas expanded)
|
||||
C = [] # terms that generate B
|
||||
# Phi(a, b, x) = exp(x) * sum(A[i] * X[i] * B[i])
|
||||
# B[0] = 1
|
||||
# B[k] = sum(C[k] * b**k/k!, k=0..)
|
||||
# Note: C[k] can be obtained from a series expansion of 1/gamma(b).
|
||||
expression = gamma(b)/sympy.exp(x) * \
|
||||
Sum(x**k/factorial(k)/gamma(a*k+b), (k, 0, S.Infinity))
|
||||
|
||||
# nth term of taylor series in a=0: a^n/n! * (d^n Phi(a, b, x)/da^n at a=0)
|
||||
for n in range(0, order+1):
|
||||
term = expression.diff(a, n).subs(a, 0).simplify().doit()
|
||||
# set the whole bracket involving polygammas to 1
|
||||
x_part = (term.subs(polygamma(0, b), 1)
|
||||
.replace(polygamma, lambda *args: 0))
|
||||
# sign convention: x part always positive
|
||||
x_part *= (-1)**n
|
||||
# expansion of polygamma part with 1/gamma(b)
|
||||
pg_part = term/x_part/gamma(b)
|
||||
if n >= 1:
|
||||
# Note: highest term is digamma^n
|
||||
pg_part = pg_part.replace(polygamma,
|
||||
lambda k, x: pg_series(k, x, order+1+n))
|
||||
pg_part = (pg_part.series(b, 0, n=order+1-n)
|
||||
.removeO()
|
||||
.subs(polygamma(2, 1), -2*zeta(3))
|
||||
.simplify()
|
||||
)
|
||||
|
||||
A.append(a**n/factorial(n))
|
||||
X.append(horner(x_part))
|
||||
B.append(pg_part)
|
||||
|
||||
# Calculate C and put in the k!
|
||||
C = sympy.Poly(B[1].subs(c_subs), b).coeffs()
|
||||
C.reverse()
|
||||
for i in range(len(C)):
|
||||
C[i] = (C[i] * factorial(i)).simplify()
|
||||
|
||||
s = "Tylor series expansion of Phi(a, b, x) in a=0 and b=0 up to order 5."
|
||||
s += "\nPhi(a, b, x) = exp(x) * sum(A[i] * X[i] * B[i], i=0..5)\n"
|
||||
s += "B[0] = 1\n"
|
||||
s += "B[i] = sum(C[k+i-1] * b**k/k!, k=0..)\n"
|
||||
s += "\nM_PI = pi"
|
||||
s += "\nM_EG = EulerGamma"
|
||||
s += "\nM_Z3 = zeta(3)"
|
||||
for name, c in zip(['A', 'X'], [A, X]):
|
||||
for i in range(len(c)):
|
||||
s += f"\n{name}[{i}] = "
|
||||
s += str(c[i])
|
||||
# For C, do also compute the values numerically
|
||||
for i in range(len(C)):
|
||||
s += f"\n# C[{i}] = "
|
||||
s += str(C[i])
|
||||
s += f"\nC[{i}] = "
|
||||
s += str(C[i].subs({M_EG: EulerGamma, M_PI: pi, M_Z3: zeta(3)})
|
||||
.evalf(17))
|
||||
|
||||
# Does B have the assumed structure?
|
||||
s += "\n\nTest if B[i] does have the assumed structure."
|
||||
s += "\nC[i] are derived from B[1] alone."
|
||||
s += "\nTest B[2] == C[1] + b*C[2] + b^2/2*C[3] + b^3/6*C[4] + .."
|
||||
test = sum([b**k/factorial(k) * C[k+1] for k in range(order-1)])
|
||||
test = (test - B[2].subs(c_subs)).simplify()
|
||||
s += f"\ntest successful = {test==S(0)}"
|
||||
s += "\nTest B[3] == C[2] + b*C[3] + b^2/2*C[4] + .."
|
||||
test = sum([b**k/factorial(k) * C[k+2] for k in range(order-2)])
|
||||
test = (test - B[3].subs(c_subs)).simplify()
|
||||
s += f"\ntest successful = {test==S(0)}"
|
||||
return s
|
||||
|
||||
|
||||
def asymptotic_series():
|
||||
"""Asymptotic expansion for large x.
|
||||
|
||||
Phi(a, b, x) ~ Z^(1/2-b) * exp((1+a)/a * Z) * sum_k (-1)^k * C_k / Z^k
|
||||
Z = (a*x)^(1/(1+a))
|
||||
|
||||
Wright (1935) lists the coefficients C_0 and C_1 (he calls them a_0 and
|
||||
a_1). With slightly different notation, Paris (2017) lists coefficients
|
||||
c_k up to order k=3.
|
||||
Paris (2017) uses ZP = (1+a)/a * Z (ZP = Z of Paris) and
|
||||
C_k = C_0 * (-a/(1+a))^k * c_k
|
||||
"""
|
||||
order = 8
|
||||
|
||||
class g(sympy.Function):
|
||||
"""Helper function g according to Wright (1935)
|
||||
|
||||
g(n, rho, v) = (1 + (rho+2)/3 * v + (rho+2)*(rho+3)/(2*3) * v^2 + ...)
|
||||
|
||||
Note: Wright (1935) uses square root of above definition.
|
||||
"""
|
||||
nargs = 3
|
||||
|
||||
@classmethod
|
||||
def eval(cls, n, rho, v):
|
||||
if not n >= 0:
|
||||
raise ValueError("must have n >= 0")
|
||||
elif n == 0:
|
||||
return 1
|
||||
else:
|
||||
return g(n-1, rho, v) \
|
||||
+ gammasimp(gamma(rho+2+n)/gamma(rho+2)) \
|
||||
/ gammasimp(gamma(3+n)/gamma(3))*v**n
|
||||
|
||||
class coef_C(sympy.Function):
|
||||
"""Calculate coefficients C_m for integer m.
|
||||
|
||||
C_m is the coefficient of v^(2*m) in the Taylor expansion in v=0 of
|
||||
Gamma(m+1/2)/(2*pi) * (2/(rho+1))^(m+1/2) * (1-v)^(-b)
|
||||
* g(rho, v)^(-m-1/2)
|
||||
"""
|
||||
nargs = 3
|
||||
|
||||
@classmethod
|
||||
def eval(cls, m, rho, beta):
|
||||
if not m >= 0:
|
||||
raise ValueError("must have m >= 0")
|
||||
|
||||
v = symbols("v")
|
||||
expression = (1-v)**(-beta) * g(2*m, rho, v)**(-m-Rational(1, 2))
|
||||
res = expression.diff(v, 2*m).subs(v, 0) / factorial(2*m)
|
||||
res = res * (gamma(m + Rational(1, 2)) / (2*pi)
|
||||
* (2/(rho+1))**(m + Rational(1, 2)))
|
||||
return res
|
||||
|
||||
# in order to have nice ordering/sorting of expressions, we set a = xa.
|
||||
xa, b, xap1 = symbols("xa b xap1")
|
||||
C0 = coef_C(0, xa, b)
|
||||
# a1 = a(1, rho, beta)
|
||||
s = "Asymptotic expansion for large x\n"
|
||||
s += "Phi(a, b, x) = Z**(1/2-b) * exp((1+a)/a * Z) \n"
|
||||
s += " * sum((-1)**k * C[k]/Z**k, k=0..6)\n\n"
|
||||
s += "Z = pow(a * x, 1/(1+a))\n"
|
||||
s += "A[k] = pow(a, k)\n"
|
||||
s += "B[k] = pow(b, k)\n"
|
||||
s += "Ap1[k] = pow(1+a, k)\n\n"
|
||||
s += "C[0] = 1./sqrt(2. * M_PI * Ap1[1])\n"
|
||||
for i in range(1, order+1):
|
||||
expr = (coef_C(i, xa, b) / (C0/(1+xa)**i)).simplify()
|
||||
factor = [x.denominator() for x in sympy.Poly(expr).coeffs()]
|
||||
factor = sympy.lcm(factor)
|
||||
expr = (expr * factor).simplify().collect(b, sympy.factor)
|
||||
expr = expr.xreplace({xa+1: xap1})
|
||||
s += f"C[{i}] = C[0] / ({factor} * Ap1[{i}])\n"
|
||||
s += f"C[{i}] *= {str(expr)}\n\n"
|
||||
import re
|
||||
re_a = re.compile(r'xa\*\*(\d+)')
|
||||
s = re_a.sub(r'A[\1]', s)
|
||||
re_b = re.compile(r'b\*\*(\d+)')
|
||||
s = re_b.sub(r'B[\1]', s)
|
||||
s = s.replace('xap1', 'Ap1[1]')
|
||||
s = s.replace('xa', 'a')
|
||||
# max integer = 2^31-1 = 2,147,483,647. Solution: Put a point after 10
|
||||
# or more digits.
|
||||
re_digits = re.compile(r'(\d{10,})')
|
||||
s = re_digits.sub(r'\1.', s)
|
||||
return s
|
||||
|
||||
|
||||
def optimal_epsilon_integral():
|
||||
"""Fit optimal choice of epsilon for integral representation.
|
||||
|
||||
The integrand of
|
||||
int_0^pi P(eps, a, b, x, phi) * dphi
|
||||
can exhibit oscillatory behaviour. It stems from the cosine of P and can be
|
||||
minimized by minimizing the arc length of the argument
|
||||
f(phi) = eps * sin(phi) - x * eps^(-a) * sin(a * phi) + (1 - b) * phi
|
||||
of cos(f(phi)).
|
||||
We minimize the arc length in eps for a grid of values (a, b, x) and fit a
|
||||
parametric function to it.
|
||||
"""
|
||||
def fp(eps, a, b, x, phi):
|
||||
"""Derivative of f w.r.t. phi."""
|
||||
eps_a = np.power(1. * eps, -a)
|
||||
return eps * np.cos(phi) - a * x * eps_a * np.cos(a * phi) + 1 - b
|
||||
|
||||
def arclength(eps, a, b, x, epsrel=1e-2, limit=100):
|
||||
"""Compute Arc length of f.
|
||||
|
||||
Note that the arc length of a function f from t0 to t1 is given by
|
||||
int_t0^t1 sqrt(1 + f'(t)^2) dt
|
||||
"""
|
||||
return quad(lambda phi: np.sqrt(1 + fp(eps, a, b, x, phi)**2),
|
||||
0, np.pi,
|
||||
epsrel=epsrel, limit=100)[0]
|
||||
|
||||
# grid of minimal arc length values
|
||||
data_a = [1e-3, 0.1, 0.5, 0.9, 1, 2, 4, 5, 6, 8]
|
||||
data_b = [0, 1, 4, 7, 10]
|
||||
data_x = [1, 1.5, 2, 4, 10, 20, 50, 100, 200, 500, 1e3, 5e3, 1e4]
|
||||
data_a, data_b, data_x = np.meshgrid(data_a, data_b, data_x)
|
||||
data_a, data_b, data_x = (data_a.flatten(), data_b.flatten(),
|
||||
data_x.flatten())
|
||||
best_eps = []
|
||||
for i in range(data_x.size):
|
||||
best_eps.append(
|
||||
minimize_scalar(lambda eps: arclength(eps, data_a[i], data_b[i],
|
||||
data_x[i]),
|
||||
bounds=(1e-3, 1000),
|
||||
method='Bounded', options={'xatol': 1e-3}).x
|
||||
)
|
||||
best_eps = np.array(best_eps)
|
||||
# pandas would be nice, but here a dictionary is enough
|
||||
df = {'a': data_a,
|
||||
'b': data_b,
|
||||
'x': data_x,
|
||||
'eps': best_eps,
|
||||
}
|
||||
|
||||
def func(data, A0, A1, A2, A3, A4, A5):
|
||||
"""Compute parametric function to fit."""
|
||||
a = data['a']
|
||||
b = data['b']
|
||||
x = data['x']
|
||||
return (A0 * b * np.exp(-0.5 * a)
|
||||
+ np.exp(A1 + 1 / (1 + a) * np.log(x) - A2 * np.exp(-A3 * a)
|
||||
+ A4 / (1 + np.exp(A5 * a))))
|
||||
|
||||
func_params = list(curve_fit(func, df, df['eps'], method='trf')[0])
|
||||
|
||||
s = "Fit optimal eps for integrand P via minimal arc length\n"
|
||||
s += "with parametric function:\n"
|
||||
s += "optimal_eps = (A0 * b * exp(-a/2) + exp(A1 + 1 / (1 + a) * log(x)\n"
|
||||
s += " - A2 * exp(-A3 * a) + A4 / (1 + exp(A5 * a)))\n\n"
|
||||
s += "Fitted parameters A0 to A5 are:\n"
|
||||
s += ', '.join([f'{x:.5g}' for x in func_params])
|
||||
return s
|
||||
|
||||
|
||||
def main():
|
||||
t0 = time()
|
||||
parser = ArgumentParser(description=__doc__,
|
||||
formatter_class=RawTextHelpFormatter)
|
||||
parser.add_argument('action', type=int, choices=[1, 2, 3, 4],
|
||||
help='chose what expansion to precompute\n'
|
||||
'1 : Series for small a\n'
|
||||
'2 : Series for small a and small b\n'
|
||||
'3 : Asymptotic series for large x\n'
|
||||
' This may take some time (>4h).\n'
|
||||
'4 : Fit optimal eps for integral representation.'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
switch = {1: lambda: print(series_small_a()),
|
||||
2: lambda: print(series_small_a_small_b()),
|
||||
3: lambda: print(asymptotic_series()),
|
||||
4: lambda: print(optimal_epsilon_integral())
|
||||
}
|
||||
switch.get(args.action, lambda: print("Invalid input."))()
|
||||
print(f"\n{(time() - t0)/60:.1f} minutes elapsed.\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,152 @@
|
||||
"""Compute a grid of values for Wright's generalized Bessel function
|
||||
and save the values to data files for use in tests. Using mpmath directly in
|
||||
tests would take too long.
|
||||
|
||||
This takes about 10 minutes to run on a 2.7 GHz i7 Macbook Pro.
|
||||
"""
|
||||
from functools import lru_cache
|
||||
import os
|
||||
from time import time
|
||||
|
||||
import numpy as np
|
||||
from scipy.special._mptestutils import mpf2float
|
||||
|
||||
try:
|
||||
import mpmath as mp
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# exp_inf: smallest value x for which exp(x) == inf
|
||||
exp_inf = 709.78271289338403
|
||||
|
||||
|
||||
# 64 Byte per value
|
||||
@lru_cache(maxsize=100_000)
|
||||
def rgamma_cached(x, dps):
|
||||
with mp.workdps(dps):
|
||||
return mp.rgamma(x)
|
||||
|
||||
|
||||
def mp_wright_bessel(a, b, x, dps=50, maxterms=2000):
|
||||
"""Compute Wright's generalized Bessel function as Series with mpmath.
|
||||
"""
|
||||
with mp.workdps(dps):
|
||||
a, b, x = mp.mpf(a), mp.mpf(b), mp.mpf(x)
|
||||
res = mp.nsum(lambda k: x**k / mp.fac(k)
|
||||
* rgamma_cached(a * k + b, dps=dps),
|
||||
[0, mp.inf],
|
||||
tol=dps, method='s', steps=[maxterms]
|
||||
)
|
||||
return mpf2float(res)
|
||||
|
||||
|
||||
def main():
|
||||
t0 = time()
|
||||
print(__doc__)
|
||||
pwd = os.path.dirname(__file__)
|
||||
eps = np.finfo(float).eps * 100
|
||||
|
||||
a_range = np.array([eps,
|
||||
1e-4 * (1 - eps), 1e-4, 1e-4 * (1 + eps),
|
||||
1e-3 * (1 - eps), 1e-3, 1e-3 * (1 + eps),
|
||||
0.1, 0.5,
|
||||
1 * (1 - eps), 1, 1 * (1 + eps),
|
||||
1.5, 2, 4.999, 5, 10])
|
||||
b_range = np.array([0, eps, 1e-10, 1e-5, 0.1, 1, 2, 10, 20, 100])
|
||||
x_range = np.array([0, eps, 1 - eps, 1, 1 + eps,
|
||||
1.5,
|
||||
2 - eps, 2, 2 + eps,
|
||||
9 - eps, 9, 9 + eps,
|
||||
10 * (1 - eps), 10, 10 * (1 + eps),
|
||||
100 * (1 - eps), 100, 100 * (1 + eps),
|
||||
500, exp_inf, 1e3, 1e5, 1e10, 1e20])
|
||||
|
||||
a_range, b_range, x_range = np.meshgrid(a_range, b_range, x_range,
|
||||
indexing='ij')
|
||||
a_range = a_range.flatten()
|
||||
b_range = b_range.flatten()
|
||||
x_range = x_range.flatten()
|
||||
|
||||
# filter out some values, especially too large x
|
||||
bool_filter = ~((a_range < 5e-3) & (x_range >= exp_inf))
|
||||
bool_filter = bool_filter & ~((a_range < 0.2) & (x_range > exp_inf))
|
||||
bool_filter = bool_filter & ~((a_range < 0.5) & (x_range > 1e3))
|
||||
bool_filter = bool_filter & ~((a_range < 0.56) & (x_range > 5e3))
|
||||
bool_filter = bool_filter & ~((a_range < 1) & (x_range > 1e4))
|
||||
bool_filter = bool_filter & ~((a_range < 1.4) & (x_range > 1e5))
|
||||
bool_filter = bool_filter & ~((a_range < 1.8) & (x_range > 1e6))
|
||||
bool_filter = bool_filter & ~((a_range < 2.2) & (x_range > 1e7))
|
||||
bool_filter = bool_filter & ~((a_range < 2.5) & (x_range > 1e8))
|
||||
bool_filter = bool_filter & ~((a_range < 2.9) & (x_range > 1e9))
|
||||
bool_filter = bool_filter & ~((a_range < 3.3) & (x_range > 1e10))
|
||||
bool_filter = bool_filter & ~((a_range < 3.7) & (x_range > 1e11))
|
||||
bool_filter = bool_filter & ~((a_range < 4) & (x_range > 1e12))
|
||||
bool_filter = bool_filter & ~((a_range < 4.4) & (x_range > 1e13))
|
||||
bool_filter = bool_filter & ~((a_range < 4.7) & (x_range > 1e14))
|
||||
bool_filter = bool_filter & ~((a_range < 5.1) & (x_range > 1e15))
|
||||
bool_filter = bool_filter & ~((a_range < 5.4) & (x_range > 1e16))
|
||||
bool_filter = bool_filter & ~((a_range < 5.8) & (x_range > 1e17))
|
||||
bool_filter = bool_filter & ~((a_range < 6.2) & (x_range > 1e18))
|
||||
bool_filter = bool_filter & ~((a_range < 6.2) & (x_range > 1e18))
|
||||
bool_filter = bool_filter & ~((a_range < 6.5) & (x_range > 1e19))
|
||||
bool_filter = bool_filter & ~((a_range < 6.9) & (x_range > 1e20))
|
||||
|
||||
# filter out known values that do not meet the required numerical accuracy
|
||||
# see test test_wright_data_grid_failures
|
||||
failing = np.array([
|
||||
[0.1, 100, 709.7827128933841],
|
||||
[0.5, 10, 709.7827128933841],
|
||||
[0.5, 10, 1000],
|
||||
[0.5, 100, 1000],
|
||||
[1, 20, 100000],
|
||||
[1, 100, 100000],
|
||||
[1.0000000000000222, 20, 100000],
|
||||
[1.0000000000000222, 100, 100000],
|
||||
[1.5, 0, 500],
|
||||
[1.5, 2.220446049250313e-14, 500],
|
||||
[1.5, 1.e-10, 500],
|
||||
[1.5, 1.e-05, 500],
|
||||
[1.5, 0.1, 500],
|
||||
[1.5, 20, 100000],
|
||||
[1.5, 100, 100000],
|
||||
]).tolist()
|
||||
|
||||
does_fail = np.full_like(a_range, False, dtype=bool)
|
||||
for i in range(x_range.size):
|
||||
if [a_range[i], b_range[i], x_range[i]] in failing:
|
||||
does_fail[i] = True
|
||||
|
||||
# filter and flatten
|
||||
a_range = a_range[bool_filter]
|
||||
b_range = b_range[bool_filter]
|
||||
x_range = x_range[bool_filter]
|
||||
does_fail = does_fail[bool_filter]
|
||||
|
||||
dataset = []
|
||||
print(f"Computing {x_range.size} single points.")
|
||||
print("Tests will fail for the following data points:")
|
||||
for i in range(x_range.size):
|
||||
a = a_range[i]
|
||||
b = b_range[i]
|
||||
x = x_range[i]
|
||||
# take care of difficult corner cases
|
||||
maxterms = 1000
|
||||
if a < 1e-6 and x >= exp_inf/10:
|
||||
maxterms = 2000
|
||||
f = mp_wright_bessel(a, b, x, maxterms=maxterms)
|
||||
if does_fail[i]:
|
||||
print("failing data point a, b, x, value = "
|
||||
f"[{a}, {b}, {x}, {f}]")
|
||||
else:
|
||||
dataset.append((a, b, x, f))
|
||||
dataset = np.array(dataset)
|
||||
|
||||
filename = os.path.join(pwd, '..', 'tests', 'data', 'local',
|
||||
'wright_bessel.txt')
|
||||
np.savetxt(filename, dataset)
|
||||
|
||||
print(f"{(time() - t0)/60:.1f} minutes elapsed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,41 @@
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
import mpmath
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def mpmath_wrightomega(x):
|
||||
return mpmath.lambertw(mpmath.exp(x), mpmath.mpf('-0.5'))
|
||||
|
||||
|
||||
def wrightomega_series_error(x):
|
||||
series = x
|
||||
desired = mpmath_wrightomega(x)
|
||||
return abs(series - desired) / desired
|
||||
|
||||
|
||||
def wrightomega_exp_error(x):
|
||||
exponential_approx = mpmath.exp(x)
|
||||
desired = mpmath_wrightomega(x)
|
||||
return abs(exponential_approx - desired) / desired
|
||||
|
||||
|
||||
def main():
|
||||
desired_error = 2 * np.finfo(float).eps
|
||||
print('Series Error')
|
||||
for x in [1e5, 1e10, 1e15, 1e20]:
|
||||
with mpmath.workdps(100):
|
||||
error = wrightomega_series_error(x)
|
||||
print(x, error, error < desired_error)
|
||||
|
||||
print('Exp error')
|
||||
for x in [-10, -25, -50, -100, -200, -400, -700, -740]:
|
||||
with mpmath.workdps(100):
|
||||
error = wrightomega_exp_error(x)
|
||||
print(x, error, error < desired_error)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Compute the Taylor series for zeta(x) - 1 around x = 0."""
|
||||
try:
|
||||
import mpmath
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def zetac_series(N):
|
||||
coeffs = []
|
||||
with mpmath.workdps(100):
|
||||
coeffs.append(-1.5)
|
||||
for n in range(1, N):
|
||||
coeff = mpmath.diff(mpmath.zeta, 0, n)/mpmath.factorial(n)
|
||||
coeffs.append(coeff)
|
||||
return coeffs
|
||||
|
||||
|
||||
def main():
|
||||
print(__doc__)
|
||||
coeffs = zetac_series(10)
|
||||
coeffs = [mpmath.nstr(x, 20, min_fixed=0, max_fixed=0)
|
||||
for x in coeffs]
|
||||
print("\n".join(coeffs[::-1]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Warnings and Exceptions that can be raised by special functions."""
|
||||
import warnings
|
||||
|
||||
|
||||
class SpecialFunctionWarning(Warning):
|
||||
"""Warning that can be emitted by special functions."""
|
||||
pass
|
||||
|
||||
|
||||
warnings.simplefilter("always", category=SpecialFunctionWarning)
|
||||
|
||||
|
||||
class SpecialFunctionError(Exception):
|
||||
"""Exception that can be raised by special functions."""
|
||||
pass
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,106 @@
|
||||
# Last Change: Sat Mar 21 02:00 PM 2009 J
|
||||
|
||||
# Copyright (c) 2001, 2002 Enthought, Inc.
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# a. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# b. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# c. Neither the name of the Enthought nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
# DAMAGE.
|
||||
|
||||
"""Some more special functions which may be useful for multivariate statistical
|
||||
analysis."""
|
||||
|
||||
import numpy as np
|
||||
from scipy.special import gammaln as loggam
|
||||
|
||||
|
||||
__all__ = ['multigammaln']
|
||||
|
||||
|
||||
def multigammaln(a, d):
|
||||
r"""Returns the log of multivariate gamma, also sometimes called the
|
||||
generalized gamma.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : ndarray
|
||||
The multivariate gamma is computed for each item of `a`.
|
||||
d : int
|
||||
The dimension of the space of integration.
|
||||
|
||||
Returns
|
||||
-------
|
||||
res : ndarray
|
||||
The values of the log multivariate gamma at the given points `a`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The formal definition of the multivariate gamma of dimension d for a real
|
||||
`a` is
|
||||
|
||||
.. math::
|
||||
|
||||
\Gamma_d(a) = \int_{A>0} e^{-tr(A)} |A|^{a - (d+1)/2} dA
|
||||
|
||||
with the condition :math:`a > (d-1)/2`, and :math:`A > 0` being the set of
|
||||
all the positive definite matrices of dimension `d`. Note that `a` is a
|
||||
scalar: the integrand only is multivariate, the argument is not (the
|
||||
function is defined over a subset of the real set).
|
||||
|
||||
This can be proven to be equal to the much friendlier equation
|
||||
|
||||
.. math::
|
||||
|
||||
\Gamma_d(a) = \pi^{d(d-1)/4} \prod_{i=1}^{d} \Gamma(a - (i-1)/2).
|
||||
|
||||
References
|
||||
----------
|
||||
R. J. Muirhead, Aspects of multivariate statistical theory (Wiley Series in
|
||||
probability and mathematical statistics).
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from scipy.special import multigammaln, gammaln
|
||||
>>> a = 23.5
|
||||
>>> d = 10
|
||||
>>> multigammaln(a, d)
|
||||
454.1488605074416
|
||||
|
||||
Verify that the result agrees with the logarithm of the equation
|
||||
shown above:
|
||||
|
||||
>>> d*(d-1)/4*np.log(np.pi) + gammaln(a - 0.5*np.arange(0, d)).sum()
|
||||
454.1488605074416
|
||||
"""
|
||||
a = np.asarray(a)
|
||||
if not np.isscalar(d) or (np.floor(d) != d):
|
||||
raise ValueError("d should be a positive integer (dimension)")
|
||||
if np.any(a <= 0.5 * (d - 1)):
|
||||
raise ValueError(f"condition a ({a:f}) > 0.5 * (d-1) ({0.5 * (d-1):f}) not met")
|
||||
|
||||
res = (d * (d-1) * 0.25) * np.log(np.pi)
|
||||
res += np.sum(loggam([(a - (j - 1.)/2) for j in range(1, d+1)]), axis=0)
|
||||
return res
|
||||
@@ -0,0 +1,397 @@
|
||||
from functools import wraps
|
||||
import scipy._lib.array_api_extra as xpx
|
||||
import numpy as np
|
||||
from ._ufuncs import (_spherical_jn, _spherical_yn, _spherical_in,
|
||||
_spherical_kn, _spherical_jn_d, _spherical_yn_d,
|
||||
_spherical_in_d, _spherical_kn_d)
|
||||
|
||||
|
||||
def use_reflection(sign_n_even=None, reflection_fun=None):
|
||||
# - If reflection_fun is not specified, reflects negative `z` and multiplies
|
||||
# output by appropriate sign (indicated by `sign_n_even`).
|
||||
# - If reflection_fun is specified, calls `reflection_fun` instead of `fun`.
|
||||
# See DLMF 10.47(v) https://dlmf.nist.gov/10.47
|
||||
def decorator(fun):
|
||||
def standard_reflection(n, z, derivative):
|
||||
# sign_n_even indicates the sign when the order `n` is even
|
||||
sign = np.where(n % 2 == 0, sign_n_even, -sign_n_even)
|
||||
# By the chain rule, differentiation at `-z` adds a minus sign
|
||||
sign = -sign if derivative else sign
|
||||
# Evaluate at positive z (minus negative z) and adjust the sign
|
||||
return fun(n, -z, derivative) * sign
|
||||
|
||||
@wraps(fun)
|
||||
def wrapper(n, z, derivative=False):
|
||||
z = np.asarray(z)
|
||||
|
||||
if np.issubdtype(z.dtype, np.complexfloating):
|
||||
return fun(n, z, derivative) # complex dtype just works
|
||||
|
||||
f2 = standard_reflection if reflection_fun is None else reflection_fun
|
||||
return xpx.apply_where(z.real >= 0, (n, z),
|
||||
lambda n, z: fun(n, z, derivative),
|
||||
lambda n, z: f2(n, z, derivative))[()]
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
@use_reflection(+1) # See DLMF 10.47(v) https://dlmf.nist.gov/10.47
|
||||
def spherical_jn(n, z, derivative=False):
|
||||
r"""Spherical Bessel function of the first kind or its derivative.
|
||||
|
||||
Defined as [1]_,
|
||||
|
||||
.. math:: j_n(z) = \sqrt{\frac{\pi}{2z}} J_{n + 1/2}(z),
|
||||
|
||||
where :math:`J_n` is the Bessel function of the first kind.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int, array_like
|
||||
Order of the Bessel function (n >= 0).
|
||||
z : complex or float, array_like
|
||||
Argument of the Bessel function.
|
||||
derivative : bool, optional
|
||||
If True, the value of the derivative (rather than the function
|
||||
itself) is returned.
|
||||
|
||||
Returns
|
||||
-------
|
||||
jn : ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
For real arguments greater than the order, the function is computed
|
||||
using the ascending recurrence [2]_. For small real or complex
|
||||
arguments, the definitional relation to the cylindrical Bessel function
|
||||
of the first kind is used.
|
||||
|
||||
The derivative is computed using the relations [3]_,
|
||||
|
||||
.. math::
|
||||
j_n'(z) = j_{n-1}(z) - \frac{n + 1}{z} j_n(z).
|
||||
|
||||
j_0'(z) = -j_1(z)
|
||||
|
||||
|
||||
.. versionadded:: 0.18.0
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://dlmf.nist.gov/10.47.E3
|
||||
.. [2] https://dlmf.nist.gov/10.51.E1
|
||||
.. [3] https://dlmf.nist.gov/10.51.E2
|
||||
.. [AS] Milton Abramowitz and Irene A. Stegun, eds.
|
||||
Handbook of Mathematical Functions with Formulas,
|
||||
Graphs, and Mathematical Tables. New York: Dover, 1972.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The spherical Bessel functions of the first kind :math:`j_n` accept
|
||||
both real and complex second argument. They can return a complex type:
|
||||
|
||||
>>> from scipy.special import spherical_jn
|
||||
>>> spherical_jn(0, 3+5j)
|
||||
(-9.878987731663194-8.021894345786002j)
|
||||
>>> type(spherical_jn(0, 3+5j))
|
||||
<class 'numpy.complex128'>
|
||||
|
||||
We can verify the relation for the derivative from the Notes
|
||||
for :math:`n=3` in the interval :math:`[1, 2]`:
|
||||
|
||||
>>> import numpy as np
|
||||
>>> x = np.arange(1.0, 2.0, 0.01)
|
||||
>>> np.allclose(spherical_jn(3, x, True),
|
||||
... spherical_jn(2, x) - 4/x * spherical_jn(3, x))
|
||||
True
|
||||
|
||||
The first few :math:`j_n` with real argument:
|
||||
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> x = np.arange(0.0, 10.0, 0.01)
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.set_ylim(-0.5, 1.5)
|
||||
>>> ax.set_title(r'Spherical Bessel functions $j_n$')
|
||||
>>> for n in np.arange(0, 4):
|
||||
... ax.plot(x, spherical_jn(n, x), label=rf'$j_{n}$')
|
||||
>>> plt.legend(loc='best')
|
||||
>>> plt.show()
|
||||
|
||||
"""
|
||||
n = np.asarray(n, dtype=np.dtype("long"))
|
||||
if derivative:
|
||||
return _spherical_jn_d(n, z)
|
||||
else:
|
||||
return _spherical_jn(n, z)
|
||||
|
||||
|
||||
@use_reflection(-1) # See DLMF 10.47(v) https://dlmf.nist.gov/10.47
|
||||
def spherical_yn(n, z, derivative=False):
|
||||
r"""Spherical Bessel function of the second kind or its derivative.
|
||||
|
||||
Defined as [1]_,
|
||||
|
||||
.. math:: y_n(z) = \sqrt{\frac{\pi}{2z}} Y_{n + 1/2}(z),
|
||||
|
||||
where :math:`Y_n` is the Bessel function of the second kind.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int, array_like
|
||||
Order of the Bessel function (n >= 0).
|
||||
z : complex or float, array_like
|
||||
Argument of the Bessel function.
|
||||
derivative : bool, optional
|
||||
If True, the value of the derivative (rather than the function
|
||||
itself) is returned.
|
||||
|
||||
Returns
|
||||
-------
|
||||
yn : ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
For real arguments, the function is computed using the ascending
|
||||
recurrence [2]_. For complex arguments, the definitional relation to
|
||||
the cylindrical Bessel function of the second kind is used.
|
||||
|
||||
The derivative is computed using the relations [3]_,
|
||||
|
||||
.. math::
|
||||
y_n' = y_{n-1} - \frac{n + 1}{z} y_n.
|
||||
|
||||
y_0' = -y_1
|
||||
|
||||
|
||||
.. versionadded:: 0.18.0
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://dlmf.nist.gov/10.47.E4
|
||||
.. [2] https://dlmf.nist.gov/10.51.E1
|
||||
.. [3] https://dlmf.nist.gov/10.51.E2
|
||||
.. [AS] Milton Abramowitz and Irene A. Stegun, eds.
|
||||
Handbook of Mathematical Functions with Formulas,
|
||||
Graphs, and Mathematical Tables. New York: Dover, 1972.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The spherical Bessel functions of the second kind :math:`y_n` accept
|
||||
both real and complex second argument. They can return a complex type:
|
||||
|
||||
>>> from scipy.special import spherical_yn
|
||||
>>> spherical_yn(0, 3+5j)
|
||||
(8.022343088587197-9.880052589376795j)
|
||||
>>> type(spherical_yn(0, 3+5j))
|
||||
<class 'numpy.complex128'>
|
||||
|
||||
We can verify the relation for the derivative from the Notes
|
||||
for :math:`n=3` in the interval :math:`[1, 2]`:
|
||||
|
||||
>>> import numpy as np
|
||||
>>> x = np.arange(1.0, 2.0, 0.01)
|
||||
>>> np.allclose(spherical_yn(3, x, True),
|
||||
... spherical_yn(2, x) - 4/x * spherical_yn(3, x))
|
||||
True
|
||||
|
||||
The first few :math:`y_n` with real argument:
|
||||
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> x = np.arange(0.0, 10.0, 0.01)
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.set_ylim(-2.0, 1.0)
|
||||
>>> ax.set_title(r'Spherical Bessel functions $y_n$')
|
||||
>>> for n in np.arange(0, 4):
|
||||
... ax.plot(x, spherical_yn(n, x), label=rf'$y_{n}$')
|
||||
>>> plt.legend(loc='best')
|
||||
>>> plt.show()
|
||||
|
||||
"""
|
||||
n = np.asarray(n, dtype=np.dtype("long"))
|
||||
if derivative:
|
||||
return _spherical_yn_d(n, z)
|
||||
else:
|
||||
return _spherical_yn(n, z)
|
||||
|
||||
|
||||
@use_reflection(+1) # See DLMF 10.47(v) https://dlmf.nist.gov/10.47
|
||||
def spherical_in(n, z, derivative=False):
|
||||
r"""Modified spherical Bessel function of the first kind or its derivative.
|
||||
|
||||
Defined as [1]_,
|
||||
|
||||
.. math:: i_n(z) = \sqrt{\frac{\pi}{2z}} I_{n + 1/2}(z),
|
||||
|
||||
where :math:`I_n` is the modified Bessel function of the first kind.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int, array_like
|
||||
Order of the Bessel function (n >= 0).
|
||||
z : complex or float, array_like
|
||||
Argument of the Bessel function.
|
||||
derivative : bool, optional
|
||||
If True, the value of the derivative (rather than the function
|
||||
itself) is returned.
|
||||
|
||||
Returns
|
||||
-------
|
||||
in : ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
The function is computed using its definitional relation to the
|
||||
modified cylindrical Bessel function of the first kind.
|
||||
|
||||
The derivative is computed using the relations [2]_,
|
||||
|
||||
.. math::
|
||||
i_n' = i_{n-1} - \frac{n + 1}{z} i_n.
|
||||
|
||||
i_1' = i_0
|
||||
|
||||
|
||||
.. versionadded:: 0.18.0
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://dlmf.nist.gov/10.47.E7
|
||||
.. [2] https://dlmf.nist.gov/10.51.E5
|
||||
.. [AS] Milton Abramowitz and Irene A. Stegun, eds.
|
||||
Handbook of Mathematical Functions with Formulas,
|
||||
Graphs, and Mathematical Tables. New York: Dover, 1972.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The modified spherical Bessel functions of the first kind :math:`i_n`
|
||||
accept both real and complex second argument.
|
||||
They can return a complex type:
|
||||
|
||||
>>> from scipy.special import spherical_in
|
||||
>>> spherical_in(0, 3+5j)
|
||||
(-1.1689867793369182-1.2697305267234222j)
|
||||
>>> type(spherical_in(0, 3+5j))
|
||||
<class 'numpy.complex128'>
|
||||
|
||||
We can verify the relation for the derivative from the Notes
|
||||
for :math:`n=3` in the interval :math:`[1, 2]`:
|
||||
|
||||
>>> import numpy as np
|
||||
>>> x = np.arange(1.0, 2.0, 0.01)
|
||||
>>> np.allclose(spherical_in(3, x, True),
|
||||
... spherical_in(2, x) - 4/x * spherical_in(3, x))
|
||||
True
|
||||
|
||||
The first few :math:`i_n` with real argument:
|
||||
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> x = np.arange(0.0, 6.0, 0.01)
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.set_ylim(-0.5, 5.0)
|
||||
>>> ax.set_title(r'Modified spherical Bessel functions $i_n$')
|
||||
>>> for n in np.arange(0, 4):
|
||||
... ax.plot(x, spherical_in(n, x), label=rf'$i_{n}$')
|
||||
>>> plt.legend(loc='best')
|
||||
>>> plt.show()
|
||||
|
||||
"""
|
||||
n = np.asarray(n, dtype=np.dtype("long"))
|
||||
if derivative:
|
||||
return _spherical_in_d(n, z)
|
||||
else:
|
||||
return _spherical_in(n, z)
|
||||
|
||||
|
||||
def spherical_kn_reflection(n, z, derivative=False):
|
||||
# More complex than the other cases, and this will likely be re-implemented
|
||||
# in C++ anyway. Would require multiple function evaluations. Probably about
|
||||
# as fast to just resort to complex math, and much simpler.
|
||||
return spherical_kn(n, z + 0j, derivative=derivative).real
|
||||
|
||||
|
||||
@use_reflection(reflection_fun=spherical_kn_reflection)
|
||||
def spherical_kn(n, z, derivative=False):
|
||||
r"""Modified spherical Bessel function of the second kind or its derivative.
|
||||
|
||||
Defined as [1]_,
|
||||
|
||||
.. math:: k_n(z) = \sqrt{\frac{\pi}{2z}} K_{n + 1/2}(z),
|
||||
|
||||
where :math:`K_n` is the modified Bessel function of the second kind.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int, array_like
|
||||
Order of the Bessel function (n >= 0).
|
||||
z : complex or float, array_like
|
||||
Argument of the Bessel function.
|
||||
derivative : bool, optional
|
||||
If True, the value of the derivative (rather than the function
|
||||
itself) is returned.
|
||||
|
||||
Returns
|
||||
-------
|
||||
kn : ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
The function is computed using its definitional relation to the
|
||||
modified cylindrical Bessel function of the second kind.
|
||||
|
||||
The derivative is computed using the relations [2]_,
|
||||
|
||||
.. math::
|
||||
k_n' = -k_{n-1} - \frac{n + 1}{z} k_n.
|
||||
|
||||
k_0' = -k_1
|
||||
|
||||
|
||||
.. versionadded:: 0.18.0
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://dlmf.nist.gov/10.47.E9
|
||||
.. [2] https://dlmf.nist.gov/10.51.E5
|
||||
.. [AS] Milton Abramowitz and Irene A. Stegun, eds.
|
||||
Handbook of Mathematical Functions with Formulas,
|
||||
Graphs, and Mathematical Tables. New York: Dover, 1972.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The modified spherical Bessel functions of the second kind :math:`k_n`
|
||||
accept both real and complex second argument.
|
||||
They can return a complex type:
|
||||
|
||||
>>> from scipy.special import spherical_kn
|
||||
>>> spherical_kn(0, 3+5j)
|
||||
(0.012985785614001561+0.003354691603137546j)
|
||||
>>> type(spherical_kn(0, 3+5j))
|
||||
<class 'numpy.complex128'>
|
||||
|
||||
We can verify the relation for the derivative from the Notes
|
||||
for :math:`n=3` in the interval :math:`[1, 2]`:
|
||||
|
||||
>>> import numpy as np
|
||||
>>> x = np.arange(1.0, 2.0, 0.01)
|
||||
>>> np.allclose(spherical_kn(3, x, True),
|
||||
... - 4/x * spherical_kn(3, x) - spherical_kn(2, x))
|
||||
True
|
||||
|
||||
The first few :math:`k_n` with real argument:
|
||||
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> x = np.arange(0.0, 4.0, 0.01)
|
||||
>>> fig, ax = plt.subplots()
|
||||
>>> ax.set_ylim(0.0, 5.0)
|
||||
>>> ax.set_title(r'Modified spherical Bessel functions $k_n$')
|
||||
>>> for n in np.arange(0, 4):
|
||||
... ax.plot(x, spherical_kn(n, x), label=rf'$k_{n}$')
|
||||
>>> plt.legend(loc='best')
|
||||
>>> plt.show()
|
||||
|
||||
"""
|
||||
n = np.asarray(n, dtype=np.dtype("long"))
|
||||
if derivative:
|
||||
return _spherical_kn_d(n, z)
|
||||
else:
|
||||
return _spherical_kn(n, z)
|
||||
@@ -0,0 +1,295 @@
|
||||
import functools
|
||||
import operator
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from types import ModuleType
|
||||
|
||||
import numpy as np
|
||||
from scipy._lib._array_api import (
|
||||
array_namespace, scipy_namespace_for, is_numpy, is_dask, is_marray,
|
||||
xp_promote, xp_capabilities, SCIPY_ARRAY_API
|
||||
)
|
||||
import scipy._lib.array_api_extra as xpx
|
||||
from . import _ufuncs
|
||||
|
||||
|
||||
@dataclass
|
||||
class _FuncInfo:
|
||||
# NumPy-only function. IT MUST BE ELEMENTWISE.
|
||||
func: Callable
|
||||
# Number of arguments, not counting out=
|
||||
# This is for testing purposes only, due to the fact that
|
||||
# inspect.signature() just returns *args for ufuncs.
|
||||
n_args: int
|
||||
# @xp_capabilities decorator, for the purpose of
|
||||
# documentation and unit testing. Omit to indicate
|
||||
# full support for all backends.
|
||||
xp_capabilities: Callable[[Callable], Callable] | None = None
|
||||
# Generic implementation to fall back on if there is no native dispatch
|
||||
# available. This is a function that accepts (main namespace, scipy namespace)
|
||||
# and returns the final callable, or None if not available.
|
||||
generic_impl: Callable[
|
||||
[ModuleType, ModuleType | None], Callable | None
|
||||
] | None = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.func.__name__
|
||||
|
||||
# These are needed by @lru_cache below
|
||||
def __hash__(self):
|
||||
return hash(self.func)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, _FuncInfo) and self.func == other.func
|
||||
|
||||
@property
|
||||
def wrapper(self):
|
||||
if self.name in globals():
|
||||
# Already initialised. We are likely in a unit test.
|
||||
# Return function potentially overridden by xpx.testing.lazy_xp_function.
|
||||
import scipy.special
|
||||
return getattr(scipy.special, self.name)
|
||||
|
||||
if SCIPY_ARRAY_API:
|
||||
@functools.wraps(self.func)
|
||||
def wrapped(*args, **kwargs):
|
||||
xp = array_namespace(*args)
|
||||
return self._wrapper_for(xp)(*args, **kwargs)
|
||||
|
||||
# Allow pickling the function. Normally this is done by @wraps,
|
||||
# but in this case it doesn't work because self.func is a ufunc.
|
||||
wrapped.__module__ = "scipy.special"
|
||||
wrapped.__qualname__ = self.name
|
||||
func = wrapped
|
||||
else:
|
||||
func = self.func
|
||||
|
||||
capabilities = self.xp_capabilities or xp_capabilities()
|
||||
# In order to retain a naked ufunc when SCIPY_ARRAY_API is
|
||||
# disabled, xp_capabilities must apply its changes in place.
|
||||
cap_func = capabilities(func)
|
||||
assert cap_func is func
|
||||
return func
|
||||
|
||||
@functools.lru_cache(1000)
|
||||
def _wrapper_for(self, xp):
|
||||
if is_numpy(xp):
|
||||
return self.func
|
||||
|
||||
# If a native implementation is available, use that
|
||||
spx = scipy_namespace_for(xp)
|
||||
f = _get_native_func(xp, spx, self.name)
|
||||
if f is not None:
|
||||
return f
|
||||
|
||||
# If generic Array API implementation is available, use that
|
||||
if self.generic_impl is not None:
|
||||
f = self.generic_impl(xp, spx)
|
||||
if f is not None:
|
||||
return f
|
||||
|
||||
if is_marray(xp):
|
||||
# Unwrap the array, apply the function on the wrapped namespace,
|
||||
# and then re-wrap it.
|
||||
# IMPORTANT: this only works because all functions in this module
|
||||
# are elementwise. Otherwise, we would not be able to define a
|
||||
# general rule for mask propagation.
|
||||
|
||||
_f = globals()[self.name] # Allow nested wrapping
|
||||
def f(*args, _f=_f, xp=xp, **kwargs):
|
||||
data_args = [arg.data for arg in args]
|
||||
out = _f(*data_args, **kwargs)
|
||||
mask = functools.reduce(operator.or_, (arg.mask for arg in args))
|
||||
return xp.asarray(out, mask=mask)
|
||||
|
||||
return f
|
||||
|
||||
if is_dask(xp):
|
||||
# Apply the function to each block of the Dask array.
|
||||
# IMPORTANT: map_blocks works only because all functions in this module
|
||||
# are elementwise. It would be a grave mistake to apply this to gufuncs
|
||||
# or any other function with reductions, as they would change their
|
||||
# output depending on chunking!
|
||||
|
||||
_f = globals()[self.name] # Allow nested wrapping
|
||||
def f(*args, _f=_f, xp=xp, **kwargs):
|
||||
# Hide dtype kwarg from map_blocks
|
||||
return xp.map_blocks(functools.partial(_f, **kwargs), *args)
|
||||
|
||||
return f
|
||||
|
||||
# As a final resort, use the NumPy/SciPy implementation
|
||||
_f = self.func
|
||||
def f(*args, _f=_f, xp=xp, **kwargs):
|
||||
# TODO use xpx.lazy_apply to add jax.jit support
|
||||
# (but dtype propagation can be non-trivial)
|
||||
args = [np.asarray(arg) for arg in args]
|
||||
out = _f(*args, **kwargs)
|
||||
return xp.asarray(out)
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def _get_native_func(xp, spx, f_name):
|
||||
f = getattr(spx.special, f_name, None) if spx else None
|
||||
if f is None and hasattr(xp, 'special'):
|
||||
# Currently dead branch, in anticipation of 'special' Array API extension
|
||||
# https://github.com/data-apis/array-api/issues/725
|
||||
f = getattr(xp.special, f_name, None)
|
||||
return f
|
||||
|
||||
|
||||
def _rel_entr(xp, spx):
|
||||
def __rel_entr(x, y, *, xp=xp):
|
||||
# https://github.com/data-apis/array-api-extra/issues/160
|
||||
mxp = array_namespace(x._meta, y._meta) if is_dask(xp) else xp
|
||||
x, y = xp_promote(x, y, broadcast=True, force_floating=True, xp=xp)
|
||||
xy_pos = (x > 0) & (y > 0)
|
||||
xy_inf = xp.isinf(x) & xp.isinf(y)
|
||||
res = xpx.apply_where(
|
||||
xy_pos & ~xy_inf,
|
||||
(x, y),
|
||||
# Note: for very large x, this can overflow.
|
||||
lambda x, y: x * (mxp.log(x) - mxp.log(y)),
|
||||
fill_value=xp.inf
|
||||
)
|
||||
res = xpx.at(res)[(x == 0) & (y >= 0)].set(0)
|
||||
res = xpx.at(res)[xp.isnan(x) | xp.isnan(y) | (xy_pos & xy_inf)].set(xp.nan)
|
||||
return res
|
||||
|
||||
return __rel_entr
|
||||
|
||||
|
||||
def _xlogy(xp, spx):
|
||||
def __xlogy(x, y, *, xp=xp):
|
||||
x, y = xp_promote(x, y, force_floating=True, xp=xp)
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
temp = x * xp.log(y)
|
||||
return xp.where(x == 0., 0., temp)
|
||||
return __xlogy
|
||||
|
||||
|
||||
|
||||
def _chdtr(xp, spx):
|
||||
# The difference between this and just using `gammainc`
|
||||
# defined by `get_array_special_func` is that if `gammainc`
|
||||
# isn't found, we don't want to use the SciPy version; we'll
|
||||
# return None here and use the SciPy version of `chdtr`.
|
||||
gammainc = _get_native_func(xp, spx, 'gammainc')
|
||||
if gammainc is None:
|
||||
return None
|
||||
|
||||
def __chdtr(v, x):
|
||||
res = gammainc(v / 2, x / 2) # this is almost all we need
|
||||
# The rest can be removed when google/jax#20507 is resolved
|
||||
mask = (v == 0) & (x > 0) # JAX returns NaN
|
||||
res = xp.where(mask, 1., res)
|
||||
mask = xp.isinf(v) & xp.isinf(x) # JAX returns 1.0
|
||||
return xp.where(mask, xp.nan, res)
|
||||
return __chdtr
|
||||
|
||||
|
||||
def _chdtrc(xp, spx):
|
||||
# The difference between this and just using `gammaincc`
|
||||
# defined by `get_array_special_func` is that if `gammaincc`
|
||||
# isn't found, we don't want to use the SciPy version; we'll
|
||||
# return None here and use the SciPy version of `chdtrc`.
|
||||
gammaincc = _get_native_func(xp, spx, 'gammaincc')
|
||||
if gammaincc is None:
|
||||
return None
|
||||
|
||||
def __chdtrc(v, x):
|
||||
res = xp.where(x >= 0, gammaincc(v/2, x/2), 1)
|
||||
i_nan = ((x == 0) & (v == 0)) | xp.isnan(x) | xp.isnan(v) | (v <= 0)
|
||||
res = xp.where(i_nan, xp.nan, res)
|
||||
return res
|
||||
return __chdtrc
|
||||
|
||||
|
||||
def _betaincc(xp, spx):
|
||||
betainc = _get_native_func(xp, spx, 'betainc')
|
||||
if betainc is None:
|
||||
return None
|
||||
|
||||
def __betaincc(a, b, x):
|
||||
# not perfect; might want to just rely on SciPy
|
||||
return betainc(b, a, 1-x)
|
||||
return __betaincc
|
||||
|
||||
|
||||
def _stdtr(xp, spx):
|
||||
betainc = _get_native_func(xp, spx, 'betainc')
|
||||
if betainc is None:
|
||||
return None
|
||||
|
||||
def __stdtr(df, t):
|
||||
x = df / (t ** 2 + df)
|
||||
tail = betainc(df / 2, 0.5, x) / 2
|
||||
return xp.where(t < 0, tail, 1 - tail)
|
||||
|
||||
return __stdtr
|
||||
|
||||
|
||||
def _stdtrit(xp, spx):
|
||||
# Need either native stdtr or native betainc
|
||||
stdtr = _get_native_func(xp, spx, 'stdtr') or _stdtr(xp, spx)
|
||||
# If betainc is not defined, the root-finding would be done with `xp`
|
||||
# despite `stdtr` being evaluated with SciPy/NumPy `stdtr`. Save the
|
||||
# conversions: in this case, just evaluate `stdtrit` with SciPy/NumPy.
|
||||
if stdtr is None:
|
||||
return None
|
||||
|
||||
from scipy.optimize.elementwise import bracket_root, find_root
|
||||
|
||||
def __stdtrit(df, p):
|
||||
def fun(t, df, p): return stdtr(df, t) - p
|
||||
res_bracket = bracket_root(fun, xp.zeros_like(p), args=(df, p))
|
||||
res_root = find_root(fun, res_bracket.bracket, args=(df, p))
|
||||
return res_root.x
|
||||
|
||||
return __stdtrit
|
||||
|
||||
|
||||
# Inventory of automatically dispatched functions
|
||||
# IMPORTANT: these must all be **elementwise** functions!
|
||||
|
||||
# PyTorch doesn't implement `betainc`.
|
||||
# On torch CPU we can fall back to NumPy, but on GPU it won't work.
|
||||
_needs_betainc = xp_capabilities(cpu_only=True, exceptions=['jax.numpy', 'cupy'])
|
||||
|
||||
_special_funcs = (
|
||||
_FuncInfo(_ufuncs.betainc, 3, _needs_betainc),
|
||||
_FuncInfo(_ufuncs.betaincc, 3, _needs_betainc, generic_impl=_betaincc),
|
||||
_FuncInfo(_ufuncs.chdtr, 2, generic_impl=_chdtr),
|
||||
_FuncInfo(_ufuncs.chdtrc, 2, generic_impl=_chdtrc),
|
||||
_FuncInfo(_ufuncs.erf, 1),
|
||||
_FuncInfo(_ufuncs.erfc, 1),
|
||||
_FuncInfo(_ufuncs.entr, 1),
|
||||
_FuncInfo(_ufuncs.expit, 1),
|
||||
_FuncInfo(_ufuncs.i0, 1),
|
||||
_FuncInfo(_ufuncs.i0e, 1),
|
||||
_FuncInfo(_ufuncs.i1, 1),
|
||||
_FuncInfo(_ufuncs.i1e, 1),
|
||||
_FuncInfo(_ufuncs.log_ndtr, 1),
|
||||
_FuncInfo(_ufuncs.logit, 1),
|
||||
_FuncInfo(_ufuncs.gammaln, 1),
|
||||
_FuncInfo(_ufuncs.gammainc, 2),
|
||||
_FuncInfo(_ufuncs.gammaincc, 2),
|
||||
_FuncInfo(_ufuncs.ndtr, 1),
|
||||
_FuncInfo(_ufuncs.ndtri, 1),
|
||||
_FuncInfo(_ufuncs.rel_entr, 2, generic_impl=_rel_entr),
|
||||
_FuncInfo(_ufuncs.stdtr, 2, _needs_betainc, generic_impl=_stdtr),
|
||||
_FuncInfo(_ufuncs.stdtrit, 2,
|
||||
xp_capabilities(
|
||||
cpu_only=True, exceptions=['cupy'], # needs betainc
|
||||
skip_backends=[("jax.numpy", "no scipy.optimize support")]),
|
||||
generic_impl=_stdtrit),
|
||||
_FuncInfo(_ufuncs.xlogy, 2, generic_impl=_xlogy),
|
||||
)
|
||||
|
||||
# Override ufuncs.
|
||||
# When SCIPY_ARRAY_API is disabled, this exclusively updates the docstrings in place
|
||||
# and populates the xp_capabilities table, while retaining the original ufuncs.
|
||||
globals().update({nfo.func.__name__: nfo.wrapper for nfo in _special_funcs})
|
||||
__all__ = [nfo.func.__name__ for nfo in _special_funcs]
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
import numpy as np
|
||||
|
||||
def have_fenv() -> bool: ...
|
||||
def random_double(size: int, rng: np.random.RandomState) -> np.float64: ...
|
||||
def test_add_round(size: int, mode: str, rng: np.random.RandomState): ...
|
||||
|
||||
def _dd_exp(xhi: float, xlo: float) -> tuple[float, float]: ...
|
||||
def _dd_log(xhi: float, xlo: float) -> tuple[float, float]: ...
|
||||
def _dd_expm1(xhi: float, xlo: float) -> tuple[float, float]: ...
|
||||
@@ -0,0 +1,321 @@
|
||||
import os
|
||||
import functools
|
||||
import operator
|
||||
from scipy._lib import _pep440
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_
|
||||
import pytest
|
||||
|
||||
import scipy.special as sc
|
||||
|
||||
__all__ = ['with_special_errors', 'assert_func_equal', 'FuncData']
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Check if a module is present to be used in tests
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
class MissingModule:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
def check_version(module, min_ver):
|
||||
if type(module) is MissingModule:
|
||||
return pytest.mark.skip(reason=f"{module.name} is not installed")
|
||||
return pytest.mark.skipif(
|
||||
_pep440.parse(module.__version__) < _pep440.Version(min_ver),
|
||||
reason=f"{module.__name__} version >= {min_ver} required"
|
||||
)
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Enable convergence and loss of precision warnings -- turn off one by one
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def with_special_errors(func):
|
||||
"""
|
||||
Enable special function errors (such as underflow, overflow,
|
||||
loss of precision, etc.)
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def wrapper(*a, **kw):
|
||||
with sc.errstate(all='raise'):
|
||||
res = func(*a, **kw)
|
||||
return res
|
||||
return wrapper
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Comparing function values at many data points at once, with helpful
|
||||
# error reports
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def assert_func_equal(func, results, points, rtol=None, atol=None,
|
||||
param_filter=None, knownfailure=None,
|
||||
vectorized=True, dtype=None, nan_ok=False,
|
||||
ignore_inf_sign=False, distinguish_nan_and_inf=True):
|
||||
if hasattr(points, 'next'):
|
||||
# it's a generator
|
||||
points = list(points)
|
||||
|
||||
points = np.asarray(points)
|
||||
if points.ndim == 1:
|
||||
points = points[:,None]
|
||||
nparams = points.shape[1]
|
||||
|
||||
if hasattr(results, '__name__'):
|
||||
# function
|
||||
data = points
|
||||
result_columns = None
|
||||
result_func = results
|
||||
else:
|
||||
# dataset
|
||||
data = np.c_[points, results]
|
||||
result_columns = list(range(nparams, data.shape[1]))
|
||||
result_func = None
|
||||
|
||||
fdata = FuncData(func, data, list(range(nparams)),
|
||||
result_columns=result_columns, result_func=result_func,
|
||||
rtol=rtol, atol=atol, param_filter=param_filter,
|
||||
knownfailure=knownfailure, nan_ok=nan_ok, vectorized=vectorized,
|
||||
ignore_inf_sign=ignore_inf_sign,
|
||||
distinguish_nan_and_inf=distinguish_nan_and_inf)
|
||||
fdata.check()
|
||||
|
||||
|
||||
class FuncData:
|
||||
"""
|
||||
Data set for checking a special function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : function
|
||||
Function to test
|
||||
data : numpy array
|
||||
columnar data to use for testing
|
||||
param_columns : int or tuple of ints
|
||||
Columns indices in which the parameters to `func` lie.
|
||||
Can be imaginary integers to indicate that the parameter
|
||||
should be cast to complex.
|
||||
result_columns : int or tuple of ints, optional
|
||||
Column indices for expected results from `func`.
|
||||
result_func : callable, optional
|
||||
Function to call to obtain results.
|
||||
rtol : float, optional
|
||||
Required relative tolerance. Default is 5*eps.
|
||||
atol : float, optional
|
||||
Required absolute tolerance. Default is 5*tiny.
|
||||
param_filter : function, or tuple of functions/Nones, optional
|
||||
Filter functions to exclude some parameter ranges.
|
||||
If omitted, no filtering is done.
|
||||
knownfailure : str, optional
|
||||
Known failure error message to raise when the test is run.
|
||||
If omitted, no exception is raised.
|
||||
nan_ok : bool, optional
|
||||
If nan is always an accepted result.
|
||||
vectorized : bool, optional
|
||||
Whether all functions passed in are vectorized.
|
||||
ignore_inf_sign : bool, optional
|
||||
Whether to ignore signs of infinities.
|
||||
(Doesn't matter for complex-valued functions.)
|
||||
distinguish_nan_and_inf : bool, optional
|
||||
If True, treat numbers which contain nans or infs as
|
||||
equal. Sets ignore_inf_sign to be True.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, func, data, param_columns, result_columns=None,
|
||||
result_func=None, rtol=None, atol=None, param_filter=None,
|
||||
knownfailure=None, dataname=None, nan_ok=False, vectorized=True,
|
||||
ignore_inf_sign=False, distinguish_nan_and_inf=True):
|
||||
self.func = func
|
||||
self.data = data
|
||||
self.dataname = dataname
|
||||
if not hasattr(param_columns, '__len__'):
|
||||
param_columns = (param_columns,)
|
||||
self.param_columns = tuple(param_columns)
|
||||
if result_columns is not None:
|
||||
if not hasattr(result_columns, '__len__'):
|
||||
result_columns = (result_columns,)
|
||||
self.result_columns = tuple(result_columns)
|
||||
if result_func is not None:
|
||||
message = "Only result_func or result_columns should be provided"
|
||||
raise ValueError(message)
|
||||
elif result_func is not None:
|
||||
self.result_columns = None
|
||||
else:
|
||||
raise ValueError("Either result_func or result_columns should be provided")
|
||||
self.result_func = result_func
|
||||
self.rtol = rtol
|
||||
self.atol = atol
|
||||
if not hasattr(param_filter, '__len__'):
|
||||
param_filter = (param_filter,)
|
||||
self.param_filter = param_filter
|
||||
self.knownfailure = knownfailure
|
||||
self.nan_ok = nan_ok
|
||||
self.vectorized = vectorized
|
||||
self.ignore_inf_sign = ignore_inf_sign
|
||||
self.distinguish_nan_and_inf = distinguish_nan_and_inf
|
||||
if not self.distinguish_nan_and_inf:
|
||||
self.ignore_inf_sign = True
|
||||
|
||||
def get_tolerances(self, dtype):
|
||||
if not np.issubdtype(dtype, np.inexact):
|
||||
dtype = np.dtype(float)
|
||||
info = np.finfo(dtype)
|
||||
rtol, atol = self.rtol, self.atol
|
||||
if rtol is None:
|
||||
rtol = 5*info.eps
|
||||
if atol is None:
|
||||
atol = 5*info.tiny
|
||||
return rtol, atol
|
||||
|
||||
def check(self, data=None, dtype=None, dtypes=None):
|
||||
"""Check the special function against the data."""
|
||||
__tracebackhide__ = operator.methodcaller(
|
||||
'errisinstance', AssertionError
|
||||
)
|
||||
|
||||
if self.knownfailure:
|
||||
pytest.xfail(reason=self.knownfailure)
|
||||
|
||||
if data is None:
|
||||
data = self.data
|
||||
|
||||
if dtype is None:
|
||||
dtype = data.dtype
|
||||
else:
|
||||
data = data.astype(dtype)
|
||||
|
||||
rtol, atol = self.get_tolerances(dtype)
|
||||
|
||||
# Apply given filter functions
|
||||
if self.param_filter:
|
||||
param_mask = np.ones((data.shape[0],), np.bool_)
|
||||
for j, filter in zip(self.param_columns, self.param_filter):
|
||||
if filter:
|
||||
param_mask &= list(filter(data[:,j]))
|
||||
data = data[param_mask]
|
||||
|
||||
# Pick parameters from the correct columns
|
||||
params = []
|
||||
for idx, j in enumerate(self.param_columns):
|
||||
if np.iscomplexobj(j):
|
||||
j = int(j.imag)
|
||||
params.append(data[:,j].astype(complex))
|
||||
elif dtypes and idx < len(dtypes):
|
||||
params.append(data[:, j].astype(dtypes[idx]))
|
||||
else:
|
||||
params.append(data[:,j])
|
||||
|
||||
# Helper for evaluating results
|
||||
def eval_func_at_params(func, skip_mask=None):
|
||||
if self.vectorized:
|
||||
got = func(*params)
|
||||
else:
|
||||
got = []
|
||||
for j in range(len(params[0])):
|
||||
if skip_mask is not None and skip_mask[j]:
|
||||
got.append(np.nan)
|
||||
continue
|
||||
got.append(func(*tuple([params[i][j] for i in range(len(params))])))
|
||||
got = np.asarray(got)
|
||||
if not isinstance(got, tuple):
|
||||
got = (got,)
|
||||
return got
|
||||
|
||||
# Evaluate function to be tested
|
||||
got = eval_func_at_params(self.func)
|
||||
|
||||
# Grab the correct results
|
||||
if self.result_columns is not None:
|
||||
# Correct results passed in with the data
|
||||
wanted = tuple([data[:,icol] for icol in self.result_columns])
|
||||
else:
|
||||
# Function producing correct results passed in
|
||||
skip_mask = None
|
||||
if self.nan_ok and len(got) == 1:
|
||||
# Don't spend time evaluating what doesn't need to be evaluated
|
||||
skip_mask = np.isnan(got[0])
|
||||
wanted = eval_func_at_params(self.result_func, skip_mask=skip_mask)
|
||||
|
||||
# Check the validity of each output returned
|
||||
assert_(len(got) == len(wanted))
|
||||
|
||||
for output_num, (x, y) in enumerate(zip(got, wanted)):
|
||||
if np.issubdtype(x.dtype, np.complexfloating) or self.ignore_inf_sign:
|
||||
pinf_x = np.isinf(x)
|
||||
pinf_y = np.isinf(y)
|
||||
minf_x = np.isinf(x)
|
||||
minf_y = np.isinf(y)
|
||||
else:
|
||||
pinf_x = np.isposinf(x)
|
||||
pinf_y = np.isposinf(y)
|
||||
minf_x = np.isneginf(x)
|
||||
minf_y = np.isneginf(y)
|
||||
nan_x = np.isnan(x)
|
||||
nan_y = np.isnan(y)
|
||||
|
||||
with np.errstate(all='ignore'):
|
||||
abs_y = np.absolute(y)
|
||||
abs_y[~np.isfinite(abs_y)] = 0
|
||||
diff = np.absolute(x - y)
|
||||
diff[~np.isfinite(diff)] = 0
|
||||
|
||||
rdiff = diff / np.absolute(y)
|
||||
rdiff[~np.isfinite(rdiff)] = 0
|
||||
|
||||
tol_mask = (diff <= atol + rtol*abs_y)
|
||||
pinf_mask = (pinf_x == pinf_y)
|
||||
minf_mask = (minf_x == minf_y)
|
||||
|
||||
nan_mask = (nan_x == nan_y)
|
||||
|
||||
bad_j = ~(tol_mask & pinf_mask & minf_mask & nan_mask)
|
||||
|
||||
point_count = bad_j.size
|
||||
if self.nan_ok:
|
||||
bad_j &= ~nan_x
|
||||
bad_j &= ~nan_y
|
||||
point_count -= (nan_x | nan_y).sum()
|
||||
|
||||
if not self.distinguish_nan_and_inf and not self.nan_ok:
|
||||
# If nan's are okay we've already covered all these cases
|
||||
inf_x = np.isinf(x)
|
||||
inf_y = np.isinf(y)
|
||||
both_nonfinite = (inf_x & nan_y) | (nan_x & inf_y)
|
||||
bad_j &= ~both_nonfinite
|
||||
point_count -= both_nonfinite.sum()
|
||||
|
||||
if np.any(bad_j):
|
||||
# Some bad results: inform what, where, and how bad
|
||||
msg = [""]
|
||||
msg.append(f"Max |adiff|: {diff[bad_j].max():g}")
|
||||
msg.append(f"Max |rdiff|: {rdiff[bad_j].max():g}")
|
||||
msg.append(f"Bad results ({np.sum(bad_j)} out of "
|
||||
f"{point_count}) for the following points "
|
||||
f"(in output {output_num}):")
|
||||
for j in np.nonzero(bad_j)[0]:
|
||||
j = int(j)
|
||||
def fmt(x):
|
||||
return f'{np.array2string(x[j], precision=18):30s}'
|
||||
a = " ".join(map(fmt, params))
|
||||
b = " ".join(map(fmt, got))
|
||||
c = " ".join(map(fmt, wanted))
|
||||
d = fmt(rdiff)
|
||||
msg.append(f"{a} => {b} != {c} (rdiff {d})")
|
||||
assert_(False, "\n".join(msg))
|
||||
|
||||
def __repr__(self):
|
||||
"""Pretty-printing"""
|
||||
if np.any(list(map(np.iscomplexobj, self.param_columns))):
|
||||
is_complex = " (complex)"
|
||||
else:
|
||||
is_complex = ""
|
||||
if self.dataname:
|
||||
return (f"<Data for {self.func.__name__}{is_complex}: "
|
||||
f"{os.path.basename(self.dataname)}>")
|
||||
else:
|
||||
return f"<Data for {self.func.__name__}{is_complex}>"
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,522 @@
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
__all__ = [
|
||||
'geterr',
|
||||
'seterr',
|
||||
'errstate',
|
||||
'agm',
|
||||
'airy',
|
||||
'airye',
|
||||
'bdtr',
|
||||
'bdtrc',
|
||||
'bdtri',
|
||||
'bdtrik',
|
||||
'bdtrin',
|
||||
'bei',
|
||||
'beip',
|
||||
'ber',
|
||||
'berp',
|
||||
'besselpoly',
|
||||
'beta',
|
||||
'betainc',
|
||||
'betaincc',
|
||||
'betainccinv',
|
||||
'betaincinv',
|
||||
'betaln',
|
||||
'binom',
|
||||
'boxcox',
|
||||
'boxcox1p',
|
||||
'btdtria',
|
||||
'btdtrib',
|
||||
'cbrt',
|
||||
'chdtr',
|
||||
'chdtrc',
|
||||
'chdtri',
|
||||
'chdtriv',
|
||||
'chndtr',
|
||||
'chndtridf',
|
||||
'chndtrinc',
|
||||
'chndtrix',
|
||||
'cosdg',
|
||||
'cosm1',
|
||||
'cotdg',
|
||||
'dawsn',
|
||||
'ellipe',
|
||||
'ellipeinc',
|
||||
'ellipj',
|
||||
'ellipk',
|
||||
'ellipkinc',
|
||||
'ellipkm1',
|
||||
'elliprc',
|
||||
'elliprd',
|
||||
'elliprf',
|
||||
'elliprg',
|
||||
'elliprj',
|
||||
'entr',
|
||||
'erf',
|
||||
'erfc',
|
||||
'erfcinv',
|
||||
'erfcx',
|
||||
'erfi',
|
||||
'erfinv',
|
||||
'eval_chebyc',
|
||||
'eval_chebys',
|
||||
'eval_chebyt',
|
||||
'eval_chebyu',
|
||||
'eval_gegenbauer',
|
||||
'eval_genlaguerre',
|
||||
'eval_hermite',
|
||||
'eval_hermitenorm',
|
||||
'eval_jacobi',
|
||||
'eval_laguerre',
|
||||
'eval_legendre',
|
||||
'eval_sh_chebyt',
|
||||
'eval_sh_chebyu',
|
||||
'eval_sh_jacobi',
|
||||
'eval_sh_legendre',
|
||||
'exp1',
|
||||
'exp10',
|
||||
'exp2',
|
||||
'expi',
|
||||
'expit',
|
||||
'expm1',
|
||||
'expn',
|
||||
'exprel',
|
||||
'fdtr',
|
||||
'fdtrc',
|
||||
'fdtri',
|
||||
'fdtridfd',
|
||||
'fresnel',
|
||||
'gamma',
|
||||
'gammainc',
|
||||
'gammaincc',
|
||||
'gammainccinv',
|
||||
'gammaincinv',
|
||||
'gammaln',
|
||||
'gammasgn',
|
||||
'gdtr',
|
||||
'gdtrc',
|
||||
'gdtria',
|
||||
'gdtrib',
|
||||
'gdtrix',
|
||||
'hankel1',
|
||||
'hankel1e',
|
||||
'hankel2',
|
||||
'hankel2e',
|
||||
'huber',
|
||||
'hyp0f1',
|
||||
'hyp1f1',
|
||||
'hyp2f1',
|
||||
'hyperu',
|
||||
'i0',
|
||||
'i0e',
|
||||
'i1',
|
||||
'i1e',
|
||||
'inv_boxcox',
|
||||
'inv_boxcox1p',
|
||||
'it2i0k0',
|
||||
'it2j0y0',
|
||||
'it2struve0',
|
||||
'itairy',
|
||||
'iti0k0',
|
||||
'itj0y0',
|
||||
'itmodstruve0',
|
||||
'itstruve0',
|
||||
'iv',
|
||||
'ive',
|
||||
'j0',
|
||||
'j1',
|
||||
'jn',
|
||||
'jv',
|
||||
'jve',
|
||||
'k0',
|
||||
'k0e',
|
||||
'k1',
|
||||
'k1e',
|
||||
'kei',
|
||||
'keip',
|
||||
'kelvin',
|
||||
'ker',
|
||||
'kerp',
|
||||
'kl_div',
|
||||
'kn',
|
||||
'kolmogi',
|
||||
'kolmogorov',
|
||||
'kv',
|
||||
'kve',
|
||||
'log1p',
|
||||
'log_expit',
|
||||
'log_ndtr',
|
||||
'log_wright_bessel',
|
||||
'loggamma',
|
||||
'logit',
|
||||
'lpmv',
|
||||
'mathieu_a',
|
||||
'mathieu_b',
|
||||
'mathieu_cem',
|
||||
'mathieu_modcem1',
|
||||
'mathieu_modcem2',
|
||||
'mathieu_modsem1',
|
||||
'mathieu_modsem2',
|
||||
'mathieu_sem',
|
||||
'modfresnelm',
|
||||
'modfresnelp',
|
||||
'modstruve',
|
||||
'nbdtr',
|
||||
'nbdtrc',
|
||||
'nbdtri',
|
||||
'nbdtrik',
|
||||
'nbdtrin',
|
||||
'ncfdtr',
|
||||
'ncfdtri',
|
||||
'ncfdtridfd',
|
||||
'ncfdtridfn',
|
||||
'ncfdtrinc',
|
||||
'nctdtr',
|
||||
'nctdtridf',
|
||||
'nctdtrinc',
|
||||
'nctdtrit',
|
||||
'ndtr',
|
||||
'ndtri',
|
||||
'ndtri_exp',
|
||||
'nrdtrimn',
|
||||
'nrdtrisd',
|
||||
'obl_ang1',
|
||||
'obl_ang1_cv',
|
||||
'obl_cv',
|
||||
'obl_rad1',
|
||||
'obl_rad1_cv',
|
||||
'obl_rad2',
|
||||
'obl_rad2_cv',
|
||||
'owens_t',
|
||||
'pbdv',
|
||||
'pbvv',
|
||||
'pbwa',
|
||||
'pdtr',
|
||||
'pdtrc',
|
||||
'pdtri',
|
||||
'pdtrik',
|
||||
'poch',
|
||||
'powm1',
|
||||
'pro_ang1',
|
||||
'pro_ang1_cv',
|
||||
'pro_cv',
|
||||
'pro_rad1',
|
||||
'pro_rad1_cv',
|
||||
'pro_rad2',
|
||||
'pro_rad2_cv',
|
||||
'pseudo_huber',
|
||||
'psi',
|
||||
'radian',
|
||||
'rel_entr',
|
||||
'rgamma',
|
||||
'round',
|
||||
'shichi',
|
||||
'sici',
|
||||
'sindg',
|
||||
'smirnov',
|
||||
'smirnovi',
|
||||
'spence',
|
||||
'sph_harm',
|
||||
'stdtr',
|
||||
'stdtridf',
|
||||
'stdtrit',
|
||||
'struve',
|
||||
'tandg',
|
||||
'tklmbda',
|
||||
'voigt_profile',
|
||||
'wofz',
|
||||
'wright_bessel',
|
||||
'wrightomega',
|
||||
'xlog1py',
|
||||
'xlogy',
|
||||
'y0',
|
||||
'y1',
|
||||
'yn',
|
||||
'yv',
|
||||
'yve',
|
||||
'zetac'
|
||||
]
|
||||
|
||||
def geterr() -> dict[str, str]: ...
|
||||
def seterr(**kwargs: str) -> dict[str, str]: ...
|
||||
|
||||
class errstate:
|
||||
def __init__(self, **kargs: str) -> None: ...
|
||||
def __enter__(self) -> None: ...
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Any, # Unused
|
||||
exc_value: Any, # Unused
|
||||
traceback: Any, # Unused
|
||||
) -> None: ...
|
||||
|
||||
_cosine_cdf: np.ufunc
|
||||
_cosine_invcdf: np.ufunc
|
||||
_cospi: np.ufunc
|
||||
_ellip_harm: np.ufunc
|
||||
_factorial: np.ufunc
|
||||
_igam_fac: np.ufunc
|
||||
_kolmogc: np.ufunc
|
||||
_kolmogci: np.ufunc
|
||||
_kolmogp: np.ufunc
|
||||
_lambertw: np.ufunc
|
||||
_lanczos_sum_expg_scaled: np.ufunc
|
||||
_lgam1p: np.ufunc
|
||||
_log1mexp: np.ufunc
|
||||
_log1pmx: np.ufunc
|
||||
_riemann_zeta: np.ufunc
|
||||
_scaled_exp1: np.ufunc
|
||||
_sf_error_test_function: np.ufunc
|
||||
_sinpi: np.ufunc
|
||||
_smirnovc: np.ufunc
|
||||
_smirnovci: np.ufunc
|
||||
_smirnovp: np.ufunc
|
||||
_spherical_in: np.ufunc
|
||||
_spherical_in_d: np.ufunc
|
||||
_spherical_jn: np.ufunc
|
||||
_spherical_jn_d: np.ufunc
|
||||
_spherical_kn: np.ufunc
|
||||
_spherical_kn_d: np.ufunc
|
||||
_spherical_yn: np.ufunc
|
||||
_spherical_yn_d: np.ufunc
|
||||
_stirling2_inexact: np.ufunc
|
||||
_struve_asymp_large_z: np.ufunc
|
||||
_struve_bessel_series: np.ufunc
|
||||
_struve_power_series: np.ufunc
|
||||
_zeta: np.ufunc
|
||||
agm: np.ufunc
|
||||
airy: np.ufunc
|
||||
airye: np.ufunc
|
||||
bdtr: np.ufunc
|
||||
bdtrc: np.ufunc
|
||||
bdtri: np.ufunc
|
||||
bdtrik: np.ufunc
|
||||
bdtrin: np.ufunc
|
||||
bei: np.ufunc
|
||||
beip: np.ufunc
|
||||
ber: np.ufunc
|
||||
berp: np.ufunc
|
||||
besselpoly: np.ufunc
|
||||
beta: np.ufunc
|
||||
betainc: np.ufunc
|
||||
betaincc: np.ufunc
|
||||
betainccinv: np.ufunc
|
||||
betaincinv: np.ufunc
|
||||
betaln: np.ufunc
|
||||
binom: np.ufunc
|
||||
boxcox1p: np.ufunc
|
||||
boxcox: np.ufunc
|
||||
btdtria: np.ufunc
|
||||
btdtrib: np.ufunc
|
||||
cbrt: np.ufunc
|
||||
chdtr: np.ufunc
|
||||
chdtrc: np.ufunc
|
||||
chdtri: np.ufunc
|
||||
chdtriv: np.ufunc
|
||||
chndtr: np.ufunc
|
||||
chndtridf: np.ufunc
|
||||
chndtrinc: np.ufunc
|
||||
chndtrix: np.ufunc
|
||||
cosdg: np.ufunc
|
||||
cosm1: np.ufunc
|
||||
cotdg: np.ufunc
|
||||
dawsn: np.ufunc
|
||||
ellipe: np.ufunc
|
||||
ellipeinc: np.ufunc
|
||||
ellipj: np.ufunc
|
||||
ellipk: np.ufunc
|
||||
ellipkinc: np.ufunc
|
||||
ellipkm1: np.ufunc
|
||||
elliprc: np.ufunc
|
||||
elliprd: np.ufunc
|
||||
elliprf: np.ufunc
|
||||
elliprg: np.ufunc
|
||||
elliprj: np.ufunc
|
||||
entr: np.ufunc
|
||||
erf: np.ufunc
|
||||
erfc: np.ufunc
|
||||
erfcinv: np.ufunc
|
||||
erfcx: np.ufunc
|
||||
erfi: np.ufunc
|
||||
erfinv: np.ufunc
|
||||
eval_chebyc: np.ufunc
|
||||
eval_chebys: np.ufunc
|
||||
eval_chebyt: np.ufunc
|
||||
eval_chebyu: np.ufunc
|
||||
eval_gegenbauer: np.ufunc
|
||||
eval_genlaguerre: np.ufunc
|
||||
eval_hermite: np.ufunc
|
||||
eval_hermitenorm: np.ufunc
|
||||
eval_jacobi: np.ufunc
|
||||
eval_laguerre: np.ufunc
|
||||
eval_legendre: np.ufunc
|
||||
eval_sh_chebyt: np.ufunc
|
||||
eval_sh_chebyu: np.ufunc
|
||||
eval_sh_jacobi: np.ufunc
|
||||
eval_sh_legendre: np.ufunc
|
||||
exp10: np.ufunc
|
||||
exp1: np.ufunc
|
||||
exp2: np.ufunc
|
||||
expi: np.ufunc
|
||||
expit: np.ufunc
|
||||
expm1: np.ufunc
|
||||
expn: np.ufunc
|
||||
exprel: np.ufunc
|
||||
fdtr: np.ufunc
|
||||
fdtrc: np.ufunc
|
||||
fdtri: np.ufunc
|
||||
fdtridfd: np.ufunc
|
||||
fresnel: np.ufunc
|
||||
gamma: np.ufunc
|
||||
gammainc: np.ufunc
|
||||
gammaincc: np.ufunc
|
||||
gammainccinv: np.ufunc
|
||||
gammaincinv: np.ufunc
|
||||
gammaln: np.ufunc
|
||||
gammasgn: np.ufunc
|
||||
gdtr: np.ufunc
|
||||
gdtrc: np.ufunc
|
||||
gdtria: np.ufunc
|
||||
gdtrib: np.ufunc
|
||||
gdtrix: np.ufunc
|
||||
hankel1: np.ufunc
|
||||
hankel1e: np.ufunc
|
||||
hankel2: np.ufunc
|
||||
hankel2e: np.ufunc
|
||||
huber: np.ufunc
|
||||
hyp0f1: np.ufunc
|
||||
hyp1f1: np.ufunc
|
||||
hyp2f1: np.ufunc
|
||||
hyperu: np.ufunc
|
||||
i0: np.ufunc
|
||||
i0e: np.ufunc
|
||||
i1: np.ufunc
|
||||
i1e: np.ufunc
|
||||
inv_boxcox1p: np.ufunc
|
||||
inv_boxcox: np.ufunc
|
||||
it2i0k0: np.ufunc
|
||||
it2j0y0: np.ufunc
|
||||
it2struve0: np.ufunc
|
||||
itairy: np.ufunc
|
||||
iti0k0: np.ufunc
|
||||
itj0y0: np.ufunc
|
||||
itmodstruve0: np.ufunc
|
||||
itstruve0: np.ufunc
|
||||
iv: np.ufunc
|
||||
ive: np.ufunc
|
||||
j0: np.ufunc
|
||||
j1: np.ufunc
|
||||
jn: np.ufunc
|
||||
jv: np.ufunc
|
||||
jve: np.ufunc
|
||||
k0: np.ufunc
|
||||
k0e: np.ufunc
|
||||
k1: np.ufunc
|
||||
k1e: np.ufunc
|
||||
kei: np.ufunc
|
||||
keip: np.ufunc
|
||||
kelvin: np.ufunc
|
||||
ker: np.ufunc
|
||||
kerp: np.ufunc
|
||||
kl_div: np.ufunc
|
||||
kn: np.ufunc
|
||||
kolmogi: np.ufunc
|
||||
kolmogorov: np.ufunc
|
||||
kv: np.ufunc
|
||||
kve: np.ufunc
|
||||
log1p: np.ufunc
|
||||
log_expit: np.ufunc
|
||||
log_ndtr: np.ufunc
|
||||
log_wright_bessel: np.ufunc
|
||||
loggamma: np.ufunc
|
||||
logit: np.ufunc
|
||||
lpmv: np.ufunc
|
||||
mathieu_a: np.ufunc
|
||||
mathieu_b: np.ufunc
|
||||
mathieu_cem: np.ufunc
|
||||
mathieu_modcem1: np.ufunc
|
||||
mathieu_modcem2: np.ufunc
|
||||
mathieu_modsem1: np.ufunc
|
||||
mathieu_modsem2: np.ufunc
|
||||
mathieu_sem: np.ufunc
|
||||
modfresnelm: np.ufunc
|
||||
modfresnelp: np.ufunc
|
||||
modstruve: np.ufunc
|
||||
nbdtr: np.ufunc
|
||||
nbdtrc: np.ufunc
|
||||
nbdtri: np.ufunc
|
||||
nbdtrik: np.ufunc
|
||||
nbdtrin: np.ufunc
|
||||
ncfdtr: np.ufunc
|
||||
ncfdtri: np.ufunc
|
||||
ncfdtridfd: np.ufunc
|
||||
ncfdtridfn: np.ufunc
|
||||
ncfdtrinc: np.ufunc
|
||||
nctdtr: np.ufunc
|
||||
nctdtridf: np.ufunc
|
||||
nctdtrinc: np.ufunc
|
||||
nctdtrit: np.ufunc
|
||||
ndtr: np.ufunc
|
||||
ndtri: np.ufunc
|
||||
ndtri_exp: np.ufunc
|
||||
nrdtrimn: np.ufunc
|
||||
nrdtrisd: np.ufunc
|
||||
obl_ang1: np.ufunc
|
||||
obl_ang1_cv: np.ufunc
|
||||
obl_cv: np.ufunc
|
||||
obl_rad1: np.ufunc
|
||||
obl_rad1_cv: np.ufunc
|
||||
obl_rad2: np.ufunc
|
||||
obl_rad2_cv: np.ufunc
|
||||
owens_t: np.ufunc
|
||||
pbdv: np.ufunc
|
||||
pbvv: np.ufunc
|
||||
pbwa: np.ufunc
|
||||
pdtr: np.ufunc
|
||||
pdtrc: np.ufunc
|
||||
pdtri: np.ufunc
|
||||
pdtrik: np.ufunc
|
||||
poch: np.ufunc
|
||||
powm1: np.ufunc
|
||||
pro_ang1: np.ufunc
|
||||
pro_ang1_cv: np.ufunc
|
||||
pro_cv: np.ufunc
|
||||
pro_rad1: np.ufunc
|
||||
pro_rad1_cv: np.ufunc
|
||||
pro_rad2: np.ufunc
|
||||
pro_rad2_cv: np.ufunc
|
||||
pseudo_huber: np.ufunc
|
||||
psi: np.ufunc
|
||||
radian: np.ufunc
|
||||
rel_entr: np.ufunc
|
||||
rgamma: np.ufunc
|
||||
round: np.ufunc
|
||||
shichi: np.ufunc
|
||||
sici: np.ufunc
|
||||
sindg: np.ufunc
|
||||
smirnov: np.ufunc
|
||||
smirnovi: np.ufunc
|
||||
spence: np.ufunc
|
||||
sph_harm: np.ufunc
|
||||
stdtr: np.ufunc
|
||||
stdtridf: np.ufunc
|
||||
stdtrit: np.ufunc
|
||||
struve: np.ufunc
|
||||
tandg: np.ufunc
|
||||
tklmbda: np.ufunc
|
||||
voigt_profile: np.ufunc
|
||||
wofz: np.ufunc
|
||||
wright_bessel: np.ufunc
|
||||
wrightomega: np.ufunc
|
||||
xlog1py: np.ufunc
|
||||
xlogy: np.ufunc
|
||||
y0: np.ufunc
|
||||
y1: np.ufunc
|
||||
yn: np.ufunc
|
||||
yv: np.ufunc
|
||||
yve: np.ufunc
|
||||
zetac: np.ufunc
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -0,0 +1,142 @@
|
||||
from . cimport sf_error
|
||||
cdef void _set_action(sf_error.sf_error_t, sf_error.sf_action_t) noexcept nogil
|
||||
cdef void *_export_beta_pdf_float
|
||||
cdef void *_export_beta_pdf_double
|
||||
cdef void *_export_beta_ppf_float
|
||||
cdef void *_export_beta_ppf_double
|
||||
cdef void *_export_binom_cdf_float
|
||||
cdef void *_export_binom_cdf_double
|
||||
cdef void *_export_binom_isf_float
|
||||
cdef void *_export_binom_isf_double
|
||||
cdef void *_export_binom_pmf_float
|
||||
cdef void *_export_binom_pmf_double
|
||||
cdef void *_export_binom_ppf_float
|
||||
cdef void *_export_binom_ppf_double
|
||||
cdef void *_export_binom_sf_float
|
||||
cdef void *_export_binom_sf_double
|
||||
cdef void *_export_cauchy_isf_float
|
||||
cdef void *_export_cauchy_isf_double
|
||||
cdef void *_export_cauchy_ppf_float
|
||||
cdef void *_export_cauchy_ppf_double
|
||||
cdef void *_export_hypergeom_cdf_float
|
||||
cdef void *_export_hypergeom_cdf_double
|
||||
cdef void *_export_hypergeom_mean_float
|
||||
cdef void *_export_hypergeom_mean_double
|
||||
cdef void *_export_hypergeom_pmf_float
|
||||
cdef void *_export_hypergeom_pmf_double
|
||||
cdef void *_export_hypergeom_sf_float
|
||||
cdef void *_export_hypergeom_sf_double
|
||||
cdef void *_export_hypergeom_skewness_float
|
||||
cdef void *_export_hypergeom_skewness_double
|
||||
cdef void *_export_hypergeom_variance_float
|
||||
cdef void *_export_hypergeom_variance_double
|
||||
cdef void *_export_invgauss_isf_float
|
||||
cdef void *_export_invgauss_isf_double
|
||||
cdef void *_export_invgauss_ppf_float
|
||||
cdef void *_export_invgauss_ppf_double
|
||||
cdef void *_export_landau_cdf_float
|
||||
cdef void *_export_landau_cdf_double
|
||||
cdef void *_export_landau_isf_float
|
||||
cdef void *_export_landau_isf_double
|
||||
cdef void *_export_landau_pdf_float
|
||||
cdef void *_export_landau_pdf_double
|
||||
cdef void *_export_landau_ppf_float
|
||||
cdef void *_export_landau_ppf_double
|
||||
cdef void *_export_landau_sf_float
|
||||
cdef void *_export_landau_sf_double
|
||||
cdef void *_export_nbinom_cdf_float
|
||||
cdef void *_export_nbinom_cdf_double
|
||||
cdef void *_export_nbinom_isf_float
|
||||
cdef void *_export_nbinom_isf_double
|
||||
cdef void *_export_nbinom_kurtosis_excess_float
|
||||
cdef void *_export_nbinom_kurtosis_excess_double
|
||||
cdef void *_export_nbinom_mean_float
|
||||
cdef void *_export_nbinom_mean_double
|
||||
cdef void *_export_nbinom_pmf_float
|
||||
cdef void *_export_nbinom_pmf_double
|
||||
cdef void *_export_nbinom_ppf_float
|
||||
cdef void *_export_nbinom_ppf_double
|
||||
cdef void *_export_nbinom_sf_float
|
||||
cdef void *_export_nbinom_sf_double
|
||||
cdef void *_export_nbinom_skewness_float
|
||||
cdef void *_export_nbinom_skewness_double
|
||||
cdef void *_export_nbinom_variance_float
|
||||
cdef void *_export_nbinom_variance_double
|
||||
cdef void *_export_ncf_isf_float
|
||||
cdef void *_export_ncf_isf_double
|
||||
cdef void *_export_ncf_kurtosis_excess_float
|
||||
cdef void *_export_ncf_kurtosis_excess_double
|
||||
cdef void *_export_ncf_mean_float
|
||||
cdef void *_export_ncf_mean_double
|
||||
cdef void *_export_ncf_pdf_float
|
||||
cdef void *_export_ncf_pdf_double
|
||||
cdef void *_export_ncf_sf_float
|
||||
cdef void *_export_ncf_sf_double
|
||||
cdef void *_export_ncf_skewness_float
|
||||
cdef void *_export_ncf_skewness_double
|
||||
cdef void *_export_ncf_variance_float
|
||||
cdef void *_export_ncf_variance_double
|
||||
cdef void *_export_nct_isf_float
|
||||
cdef void *_export_nct_isf_double
|
||||
cdef void *_export_nct_kurtosis_excess_float
|
||||
cdef void *_export_nct_kurtosis_excess_double
|
||||
cdef void *_export_nct_mean_float
|
||||
cdef void *_export_nct_mean_double
|
||||
cdef void *_export_nct_pdf_float
|
||||
cdef void *_export_nct_pdf_double
|
||||
cdef void *_export_nct_sf_float
|
||||
cdef void *_export_nct_sf_double
|
||||
cdef void *_export_nct_skewness_float
|
||||
cdef void *_export_nct_skewness_double
|
||||
cdef void *_export_nct_variance_float
|
||||
cdef void *_export_nct_variance_double
|
||||
cdef void *_export_ncx2_cdf_float
|
||||
cdef void *_export_ncx2_cdf_double
|
||||
cdef void *_export_ncx2_isf_float
|
||||
cdef void *_export_ncx2_isf_double
|
||||
cdef void *_export_ncx2_pdf_float
|
||||
cdef void *_export_ncx2_pdf_double
|
||||
cdef void *_export_ncx2_ppf_float
|
||||
cdef void *_export_ncx2_ppf_double
|
||||
cdef void *_export_ncx2_sf_float
|
||||
cdef void *_export_ncx2_sf_double
|
||||
cdef void *_export_skewnorm_cdf_float
|
||||
cdef void *_export_skewnorm_cdf_double
|
||||
cdef void *_export_skewnorm_isf_float
|
||||
cdef void *_export_skewnorm_isf_double
|
||||
cdef void *_export_skewnorm_ppf_float
|
||||
cdef void *_export_skewnorm_ppf_double
|
||||
cdef void *_export__stirling2_inexact
|
||||
cdef void *_export_ibeta_float
|
||||
cdef void *_export_ibeta_double
|
||||
cdef void *_export_ibetac_float
|
||||
cdef void *_export_ibetac_double
|
||||
cdef void *_export_ibetac_inv_float
|
||||
cdef void *_export_ibetac_inv_double
|
||||
cdef void *_export_ibeta_inv_float
|
||||
cdef void *_export_ibeta_inv_double
|
||||
cdef void *_export_fellint_RC
|
||||
cdef void *_export_cellint_RC
|
||||
cdef void *_export_fellint_RD
|
||||
cdef void *_export_cellint_RD
|
||||
cdef void *_export_fellint_RF
|
||||
cdef void *_export_cellint_RF
|
||||
cdef void *_export_fellint_RG
|
||||
cdef void *_export_cellint_RG
|
||||
cdef void *_export_fellint_RJ
|
||||
cdef void *_export_cellint_RJ
|
||||
cdef void *_export_erfinv_float
|
||||
cdef void *_export_erfinv_double
|
||||
cdef void *_export_hyp1f1_double
|
||||
cdef void *_export_ncf_cdf_float
|
||||
cdef void *_export_ncf_cdf_double
|
||||
cdef void *_export_ncf_ppf_float
|
||||
cdef void *_export_ncf_ppf_double
|
||||
cdef void *_export_nct_cdf_float
|
||||
cdef void *_export_nct_cdf_double
|
||||
cdef void *_export_nct_ppf_float
|
||||
cdef void *_export_nct_ppf_double
|
||||
cdef void *_export_powm1_float
|
||||
cdef void *_export_powm1_double
|
||||
cdef void *_export_wrightomega
|
||||
cdef void *_export_wrightomega_real
|
||||
@@ -0,0 +1,427 @@
|
||||
# This file is automatically generated by _generate_pyx.py.
|
||||
# Do not edit manually!
|
||||
|
||||
from libc.math cimport NAN
|
||||
|
||||
include "_ufuncs_extra_code_common.pxi"
|
||||
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_beta_pdf_float "beta_pdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_beta_pdf_float = <void*>_func_beta_pdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_beta_pdf_double "beta_pdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_beta_pdf_double = <void*>_func_beta_pdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_beta_ppf_float "beta_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_beta_ppf_float = <void*>_func_beta_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_beta_ppf_double "beta_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_beta_ppf_double = <void*>_func_beta_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_binom_cdf_float "binom_cdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_binom_cdf_float = <void*>_func_binom_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_binom_cdf_double "binom_cdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_binom_cdf_double = <void*>_func_binom_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_binom_isf_float "binom_isf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_binom_isf_float = <void*>_func_binom_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_binom_isf_double "binom_isf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_binom_isf_double = <void*>_func_binom_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_binom_pmf_float "binom_pmf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_binom_pmf_float = <void*>_func_binom_pmf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_binom_pmf_double "binom_pmf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_binom_pmf_double = <void*>_func_binom_pmf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_binom_ppf_float "binom_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_binom_ppf_float = <void*>_func_binom_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_binom_ppf_double "binom_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_binom_ppf_double = <void*>_func_binom_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_binom_sf_float "binom_sf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_binom_sf_float = <void*>_func_binom_sf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_binom_sf_double "binom_sf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_binom_sf_double = <void*>_func_binom_sf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_cauchy_isf_float "cauchy_isf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_cauchy_isf_float = <void*>_func_cauchy_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_cauchy_isf_double "cauchy_isf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_cauchy_isf_double = <void*>_func_cauchy_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_cauchy_ppf_float "cauchy_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_cauchy_ppf_float = <void*>_func_cauchy_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_cauchy_ppf_double "cauchy_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_cauchy_ppf_double = <void*>_func_cauchy_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_hypergeom_cdf_float "hypergeom_cdf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_hypergeom_cdf_float = <void*>_func_hypergeom_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_hypergeom_cdf_double "hypergeom_cdf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_hypergeom_cdf_double = <void*>_func_hypergeom_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_hypergeom_mean_float "hypergeom_mean_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_hypergeom_mean_float = <void*>_func_hypergeom_mean_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_hypergeom_mean_double "hypergeom_mean_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_hypergeom_mean_double = <void*>_func_hypergeom_mean_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_hypergeom_pmf_float "hypergeom_pmf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_hypergeom_pmf_float = <void*>_func_hypergeom_pmf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_hypergeom_pmf_double "hypergeom_pmf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_hypergeom_pmf_double = <void*>_func_hypergeom_pmf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_hypergeom_sf_float "hypergeom_sf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_hypergeom_sf_float = <void*>_func_hypergeom_sf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_hypergeom_sf_double "hypergeom_sf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_hypergeom_sf_double = <void*>_func_hypergeom_sf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_hypergeom_skewness_float "hypergeom_skewness_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_hypergeom_skewness_float = <void*>_func_hypergeom_skewness_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_hypergeom_skewness_double "hypergeom_skewness_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_hypergeom_skewness_double = <void*>_func_hypergeom_skewness_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_hypergeom_variance_float "hypergeom_variance_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_hypergeom_variance_float = <void*>_func_hypergeom_variance_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_hypergeom_variance_double "hypergeom_variance_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_hypergeom_variance_double = <void*>_func_hypergeom_variance_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_invgauss_isf_float "invgauss_isf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_invgauss_isf_float = <void*>_func_invgauss_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_invgauss_isf_double "invgauss_isf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_invgauss_isf_double = <void*>_func_invgauss_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_invgauss_ppf_float "invgauss_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_invgauss_ppf_float = <void*>_func_invgauss_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_invgauss_ppf_double "invgauss_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_invgauss_ppf_double = <void*>_func_invgauss_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_landau_cdf_float "landau_cdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_landau_cdf_float = <void*>_func_landau_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_landau_cdf_double "landau_cdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_landau_cdf_double = <void*>_func_landau_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_landau_isf_float "landau_isf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_landau_isf_float = <void*>_func_landau_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_landau_isf_double "landau_isf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_landau_isf_double = <void*>_func_landau_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_landau_pdf_float "landau_pdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_landau_pdf_float = <void*>_func_landau_pdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_landau_pdf_double "landau_pdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_landau_pdf_double = <void*>_func_landau_pdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_landau_ppf_float "landau_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_landau_ppf_float = <void*>_func_landau_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_landau_ppf_double "landau_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_landau_ppf_double = <void*>_func_landau_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_landau_sf_float "landau_sf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_landau_sf_float = <void*>_func_landau_sf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_landau_sf_double "landau_sf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_landau_sf_double = <void*>_func_landau_sf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_cdf_float "nbinom_cdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_cdf_float = <void*>_func_nbinom_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_cdf_double "nbinom_cdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_cdf_double = <void*>_func_nbinom_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_isf_float "nbinom_isf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_isf_float = <void*>_func_nbinom_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_isf_double "nbinom_isf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_isf_double = <void*>_func_nbinom_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_kurtosis_excess_float "nbinom_kurtosis_excess_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_kurtosis_excess_float = <void*>_func_nbinom_kurtosis_excess_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_kurtosis_excess_double "nbinom_kurtosis_excess_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_kurtosis_excess_double = <void*>_func_nbinom_kurtosis_excess_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_mean_float "nbinom_mean_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_mean_float = <void*>_func_nbinom_mean_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_mean_double "nbinom_mean_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_mean_double = <void*>_func_nbinom_mean_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_pmf_float "nbinom_pmf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_pmf_float = <void*>_func_nbinom_pmf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_pmf_double "nbinom_pmf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_pmf_double = <void*>_func_nbinom_pmf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_ppf_float "nbinom_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_ppf_float = <void*>_func_nbinom_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_ppf_double "nbinom_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_ppf_double = <void*>_func_nbinom_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_sf_float "nbinom_sf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_sf_float = <void*>_func_nbinom_sf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_sf_double "nbinom_sf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_sf_double = <void*>_func_nbinom_sf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_skewness_float "nbinom_skewness_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_skewness_float = <void*>_func_nbinom_skewness_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_skewness_double "nbinom_skewness_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_skewness_double = <void*>_func_nbinom_skewness_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nbinom_variance_float "nbinom_variance_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nbinom_variance_float = <void*>_func_nbinom_variance_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nbinom_variance_double "nbinom_variance_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nbinom_variance_double = <void*>_func_nbinom_variance_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_isf_float "ncf_isf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_isf_float = <void*>_func_ncf_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_isf_double "ncf_isf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_isf_double = <void*>_func_ncf_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_kurtosis_excess_float "ncf_kurtosis_excess_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_kurtosis_excess_float = <void*>_func_ncf_kurtosis_excess_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_kurtosis_excess_double "ncf_kurtosis_excess_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_kurtosis_excess_double = <void*>_func_ncf_kurtosis_excess_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_mean_float "ncf_mean_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_mean_float = <void*>_func_ncf_mean_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_mean_double "ncf_mean_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_mean_double = <void*>_func_ncf_mean_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_pdf_float "ncf_pdf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_pdf_float = <void*>_func_ncf_pdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_pdf_double "ncf_pdf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_pdf_double = <void*>_func_ncf_pdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_sf_float "ncf_sf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_sf_float = <void*>_func_ncf_sf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_sf_double "ncf_sf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_sf_double = <void*>_func_ncf_sf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_skewness_float "ncf_skewness_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_skewness_float = <void*>_func_ncf_skewness_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_skewness_double "ncf_skewness_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_skewness_double = <void*>_func_ncf_skewness_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_variance_float "ncf_variance_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_variance_float = <void*>_func_ncf_variance_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_variance_double "ncf_variance_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_variance_double = <void*>_func_ncf_variance_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_isf_float "nct_isf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nct_isf_float = <void*>_func_nct_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_isf_double "nct_isf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nct_isf_double = <void*>_func_nct_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_kurtosis_excess_float "nct_kurtosis_excess_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nct_kurtosis_excess_float = <void*>_func_nct_kurtosis_excess_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_kurtosis_excess_double "nct_kurtosis_excess_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nct_kurtosis_excess_double = <void*>_func_nct_kurtosis_excess_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_mean_float "nct_mean_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nct_mean_float = <void*>_func_nct_mean_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_mean_double "nct_mean_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nct_mean_double = <void*>_func_nct_mean_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_pdf_float "nct_pdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nct_pdf_float = <void*>_func_nct_pdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_pdf_double "nct_pdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nct_pdf_double = <void*>_func_nct_pdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_sf_float "nct_sf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nct_sf_float = <void*>_func_nct_sf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_sf_double "nct_sf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nct_sf_double = <void*>_func_nct_sf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_skewness_float "nct_skewness_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nct_skewness_float = <void*>_func_nct_skewness_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_skewness_double "nct_skewness_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nct_skewness_double = <void*>_func_nct_skewness_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_variance_float "nct_variance_float"(float, float) noexcept nogil
|
||||
cdef void *_export_nct_variance_float = <void*>_func_nct_variance_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_variance_double "nct_variance_double"(double, double) noexcept nogil
|
||||
cdef void *_export_nct_variance_double = <void*>_func_nct_variance_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncx2_cdf_float "ncx2_cdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncx2_cdf_float = <void*>_func_ncx2_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncx2_cdf_double "ncx2_cdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncx2_cdf_double = <void*>_func_ncx2_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncx2_isf_float "ncx2_isf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncx2_isf_float = <void*>_func_ncx2_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncx2_isf_double "ncx2_isf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncx2_isf_double = <void*>_func_ncx2_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncx2_pdf_float "ncx2_pdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncx2_pdf_float = <void*>_func_ncx2_pdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncx2_pdf_double "ncx2_pdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncx2_pdf_double = <void*>_func_ncx2_pdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncx2_ppf_float "ncx2_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncx2_ppf_float = <void*>_func_ncx2_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncx2_ppf_double "ncx2_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncx2_ppf_double = <void*>_func_ncx2_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncx2_sf_float "ncx2_sf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ncx2_sf_float = <void*>_func_ncx2_sf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncx2_sf_double "ncx2_sf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ncx2_sf_double = <void*>_func_ncx2_sf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_skewnorm_cdf_float "skewnorm_cdf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_skewnorm_cdf_float = <void*>_func_skewnorm_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_skewnorm_cdf_double "skewnorm_cdf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_skewnorm_cdf_double = <void*>_func_skewnorm_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_skewnorm_isf_float "skewnorm_isf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_skewnorm_isf_float = <void*>_func_skewnorm_isf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_skewnorm_isf_double "skewnorm_isf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_skewnorm_isf_double = <void*>_func_skewnorm_isf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_skewnorm_ppf_float "skewnorm_ppf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_skewnorm_ppf_float = <void*>_func_skewnorm_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_skewnorm_ppf_double "skewnorm_ppf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_skewnorm_ppf_double = <void*>_func_skewnorm_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func__stirling2_inexact "_stirling2_inexact"(double, double) noexcept nogil
|
||||
cdef void *_export__stirling2_inexact = <void*>_func__stirling2_inexact
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ibeta_float "ibeta_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ibeta_float = <void*>_func_ibeta_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ibeta_double "ibeta_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ibeta_double = <void*>_func_ibeta_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ibetac_float "ibetac_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ibetac_float = <void*>_func_ibetac_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ibetac_double "ibetac_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ibetac_double = <void*>_func_ibetac_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ibetac_inv_float "ibetac_inv_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ibetac_inv_float = <void*>_func_ibetac_inv_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ibetac_inv_double "ibetac_inv_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ibetac_inv_double = <void*>_func_ibetac_inv_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ibeta_inv_float "ibeta_inv_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_ibeta_inv_float = <void*>_func_ibeta_inv_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ibeta_inv_double "ibeta_inv_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_ibeta_inv_double = <void*>_func_ibeta_inv_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_fellint_RC "fellint_RC"(double, double) noexcept nogil
|
||||
cdef void *_export_fellint_RC = <void*>_func_fellint_RC
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_cellint_RC "cellint_RC"(double complex, double complex) noexcept nogil
|
||||
cdef void *_export_cellint_RC = <void*>_func_cellint_RC
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_fellint_RD "fellint_RD"(double, double, double) noexcept nogil
|
||||
cdef void *_export_fellint_RD = <void*>_func_fellint_RD
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_cellint_RD "cellint_RD"(double complex, double complex, double complex) noexcept nogil
|
||||
cdef void *_export_cellint_RD = <void*>_func_cellint_RD
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_fellint_RF "fellint_RF"(double, double, double) noexcept nogil
|
||||
cdef void *_export_fellint_RF = <void*>_func_fellint_RF
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_cellint_RF "cellint_RF"(double complex, double complex, double complex) noexcept nogil
|
||||
cdef void *_export_cellint_RF = <void*>_func_cellint_RF
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_fellint_RG "fellint_RG"(double, double, double) noexcept nogil
|
||||
cdef void *_export_fellint_RG = <void*>_func_fellint_RG
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_cellint_RG "cellint_RG"(double complex, double complex, double complex) noexcept nogil
|
||||
cdef void *_export_cellint_RG = <void*>_func_cellint_RG
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_fellint_RJ "fellint_RJ"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_fellint_RJ = <void*>_func_fellint_RJ
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_cellint_RJ "cellint_RJ"(double complex, double complex, double complex, double complex) noexcept nogil
|
||||
cdef void *_export_cellint_RJ = <void*>_func_cellint_RJ
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_erfinv_float "erfinv_float"(float) noexcept nogil
|
||||
cdef void *_export_erfinv_float = <void*>_func_erfinv_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_erfinv_double "erfinv_double"(double) noexcept nogil
|
||||
cdef void *_export_erfinv_double = <void*>_func_erfinv_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_hyp1f1_double "hyp1f1_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_hyp1f1_double = <void*>_func_hyp1f1_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_cdf_float "ncf_cdf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_cdf_float = <void*>_func_ncf_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_cdf_double "ncf_cdf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_cdf_double = <void*>_func_ncf_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_ncf_ppf_float "ncf_ppf_float"(float, float, float, float) noexcept nogil
|
||||
cdef void *_export_ncf_ppf_float = <void*>_func_ncf_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_ncf_ppf_double "ncf_ppf_double"(double, double, double, double) noexcept nogil
|
||||
cdef void *_export_ncf_ppf_double = <void*>_func_ncf_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_cdf_float "nct_cdf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nct_cdf_float = <void*>_func_nct_cdf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_cdf_double "nct_cdf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nct_cdf_double = <void*>_func_nct_cdf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_nct_ppf_float "nct_ppf_float"(float, float, float) noexcept nogil
|
||||
cdef void *_export_nct_ppf_float = <void*>_func_nct_ppf_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_nct_ppf_double "nct_ppf_double"(double, double, double) noexcept nogil
|
||||
cdef void *_export_nct_ppf_double = <void*>_func_nct_ppf_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef float _func_powm1_float "powm1_float"(float, float) noexcept nogil
|
||||
cdef void *_export_powm1_float = <void*>_func_powm1_float
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_powm1_double "powm1_double"(double, double) noexcept nogil
|
||||
cdef void *_export_powm1_double = <void*>_func_powm1_double
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_wrightomega "wrightomega"(double complex) noexcept nogil
|
||||
cdef void *_export_wrightomega = <void*>_func_wrightomega
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_wrightomega_real "wrightomega_real"(double) noexcept nogil
|
||||
cdef void *_export_wrightomega_real = <void*>_func_wrightomega_real
|
||||
@@ -0,0 +1,147 @@
|
||||
#ifndef UFUNCS_PROTO_H
|
||||
#define UFUNCS_PROTO_H 1
|
||||
#include "boost_special_functions.h"
|
||||
npy_float beta_pdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double beta_pdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float beta_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double beta_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float binom_cdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double binom_cdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float binom_isf_float(npy_float, npy_float, npy_float);
|
||||
npy_double binom_isf_double(npy_double, npy_double, npy_double);
|
||||
npy_float binom_pmf_float(npy_float, npy_float, npy_float);
|
||||
npy_double binom_pmf_double(npy_double, npy_double, npy_double);
|
||||
npy_float binom_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double binom_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float binom_sf_float(npy_float, npy_float, npy_float);
|
||||
npy_double binom_sf_double(npy_double, npy_double, npy_double);
|
||||
npy_float cauchy_isf_float(npy_float, npy_float, npy_float);
|
||||
npy_double cauchy_isf_double(npy_double, npy_double, npy_double);
|
||||
npy_float cauchy_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double cauchy_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float hypergeom_cdf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double hypergeom_cdf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float hypergeom_mean_float(npy_float, npy_float, npy_float);
|
||||
npy_double hypergeom_mean_double(npy_double, npy_double, npy_double);
|
||||
npy_float hypergeom_pmf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double hypergeom_pmf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float hypergeom_sf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double hypergeom_sf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float hypergeom_skewness_float(npy_float, npy_float, npy_float);
|
||||
npy_double hypergeom_skewness_double(npy_double, npy_double, npy_double);
|
||||
npy_float hypergeom_variance_float(npy_float, npy_float, npy_float);
|
||||
npy_double hypergeom_variance_double(npy_double, npy_double, npy_double);
|
||||
npy_float invgauss_isf_float(npy_float, npy_float, npy_float);
|
||||
npy_double invgauss_isf_double(npy_double, npy_double, npy_double);
|
||||
npy_float invgauss_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double invgauss_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float landau_cdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double landau_cdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float landau_isf_float(npy_float, npy_float, npy_float);
|
||||
npy_double landau_isf_double(npy_double, npy_double, npy_double);
|
||||
npy_float landau_pdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double landau_pdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float landau_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double landau_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float landau_sf_float(npy_float, npy_float, npy_float);
|
||||
npy_double landau_sf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nbinom_cdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nbinom_cdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nbinom_isf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nbinom_isf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nbinom_kurtosis_excess_float(npy_float, npy_float);
|
||||
npy_double nbinom_kurtosis_excess_double(npy_double, npy_double);
|
||||
npy_float nbinom_mean_float(npy_float, npy_float);
|
||||
npy_double nbinom_mean_double(npy_double, npy_double);
|
||||
npy_float nbinom_pmf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nbinom_pmf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nbinom_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nbinom_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nbinom_sf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nbinom_sf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nbinom_skewness_float(npy_float, npy_float);
|
||||
npy_double nbinom_skewness_double(npy_double, npy_double);
|
||||
npy_float nbinom_variance_float(npy_float, npy_float);
|
||||
npy_double nbinom_variance_double(npy_double, npy_double);
|
||||
npy_float ncf_isf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double ncf_isf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float ncf_kurtosis_excess_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncf_kurtosis_excess_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncf_mean_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncf_mean_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncf_pdf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double ncf_pdf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float ncf_sf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double ncf_sf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float ncf_skewness_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncf_skewness_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncf_variance_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncf_variance_double(npy_double, npy_double, npy_double);
|
||||
npy_float nct_isf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nct_isf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nct_kurtosis_excess_float(npy_float, npy_float);
|
||||
npy_double nct_kurtosis_excess_double(npy_double, npy_double);
|
||||
npy_float nct_mean_float(npy_float, npy_float);
|
||||
npy_double nct_mean_double(npy_double, npy_double);
|
||||
npy_float nct_pdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nct_pdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nct_sf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nct_sf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nct_skewness_float(npy_float, npy_float);
|
||||
npy_double nct_skewness_double(npy_double, npy_double);
|
||||
npy_float nct_variance_float(npy_float, npy_float);
|
||||
npy_double nct_variance_double(npy_double, npy_double);
|
||||
npy_float ncx2_cdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncx2_cdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncx2_isf_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncx2_isf_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncx2_pdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncx2_pdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncx2_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncx2_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncx2_sf_float(npy_float, npy_float, npy_float);
|
||||
npy_double ncx2_sf_double(npy_double, npy_double, npy_double);
|
||||
npy_float skewnorm_cdf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double skewnorm_cdf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float skewnorm_isf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double skewnorm_isf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float skewnorm_ppf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double skewnorm_ppf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
#include "stirling2.h"
|
||||
npy_double _stirling2_inexact(npy_double, npy_double);
|
||||
npy_float ibeta_float(npy_float, npy_float, npy_float);
|
||||
npy_double ibeta_double(npy_double, npy_double, npy_double);
|
||||
npy_float ibetac_float(npy_float, npy_float, npy_float);
|
||||
npy_double ibetac_double(npy_double, npy_double, npy_double);
|
||||
npy_float ibetac_inv_float(npy_float, npy_float, npy_float);
|
||||
npy_double ibetac_inv_double(npy_double, npy_double, npy_double);
|
||||
npy_float ibeta_inv_float(npy_float, npy_float, npy_float);
|
||||
npy_double ibeta_inv_double(npy_double, npy_double, npy_double);
|
||||
#include "ellint_carlson_wrap.hh"
|
||||
npy_double fellint_RC(npy_double, npy_double);
|
||||
npy_cdouble cellint_RC(npy_cdouble, npy_cdouble);
|
||||
npy_double fellint_RD(npy_double, npy_double, npy_double);
|
||||
npy_cdouble cellint_RD(npy_cdouble, npy_cdouble, npy_cdouble);
|
||||
npy_double fellint_RF(npy_double, npy_double, npy_double);
|
||||
npy_cdouble cellint_RF(npy_cdouble, npy_cdouble, npy_cdouble);
|
||||
npy_double fellint_RG(npy_double, npy_double, npy_double);
|
||||
npy_cdouble cellint_RG(npy_cdouble, npy_cdouble, npy_cdouble);
|
||||
npy_double fellint_RJ(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_cdouble cellint_RJ(npy_cdouble, npy_cdouble, npy_cdouble, npy_cdouble);
|
||||
npy_float erfinv_float(npy_float);
|
||||
npy_double erfinv_double(npy_double);
|
||||
npy_double hyp1f1_double(npy_double, npy_double, npy_double);
|
||||
npy_float ncf_cdf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double ncf_cdf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float ncf_ppf_float(npy_float, npy_float, npy_float, npy_float);
|
||||
npy_double ncf_ppf_double(npy_double, npy_double, npy_double, npy_double);
|
||||
npy_float nct_cdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nct_cdf_double(npy_double, npy_double, npy_double);
|
||||
npy_float nct_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nct_ppf_double(npy_double, npy_double, npy_double);
|
||||
npy_float powm1_float(npy_float, npy_float);
|
||||
npy_double powm1_double(npy_double, npy_double);
|
||||
#include "_wright.h"
|
||||
npy_cdouble wrightomega(npy_cdouble);
|
||||
npy_double wrightomega_real(npy_double);
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef UFUNCS_PROTO_H
|
||||
#define UFUNCS_PROTO_H 1
|
||||
#include "_cosine.h"
|
||||
npy_double cosine_cdf(npy_double);
|
||||
npy_double cosine_invcdf(npy_double);
|
||||
#include "xsf_wrappers.h"
|
||||
npy_double cephes_igam_fac(npy_double, npy_double);
|
||||
npy_double xsf_kolmogc(npy_double);
|
||||
npy_double xsf_kolmogci(npy_double);
|
||||
npy_double xsf_kolmogp(npy_double);
|
||||
npy_double cephes_lanczos_sum_expg_scaled(npy_double);
|
||||
npy_double cephes_lgam1p(npy_double);
|
||||
npy_double cephes_smirnovc_wrap(npy_intp, npy_double);
|
||||
npy_double cephes_smirnovci_wrap(npy_intp, npy_double);
|
||||
npy_double cephes_smirnovp_wrap(npy_intp, npy_double);
|
||||
npy_double cephes__struve_asymp_large_z(npy_double, npy_double, npy_intp, npy_double *);
|
||||
npy_double cephes__struve_bessel_series(npy_double, npy_double, npy_intp, npy_double *);
|
||||
npy_double cephes__struve_power_series(npy_double, npy_double, npy_intp, npy_double *);
|
||||
npy_double cephes_bdtr_wrap(npy_double, npy_intp, npy_double);
|
||||
npy_double cephes_bdtrc_wrap(npy_double, npy_intp, npy_double);
|
||||
npy_double cephes_bdtri_wrap(npy_double, npy_intp, npy_double);
|
||||
npy_double xsf_chdtr(npy_double, npy_double);
|
||||
npy_double xsf_chdtrc(npy_double, npy_double);
|
||||
npy_double xsf_chdtri(npy_double, npy_double);
|
||||
npy_double cephes_erfcinv(npy_double);
|
||||
npy_double cephes_expn_wrap(npy_intp, npy_double);
|
||||
npy_double xsf_fdtr(npy_double, npy_double, npy_double);
|
||||
npy_double xsf_fdtrc(npy_double, npy_double, npy_double);
|
||||
npy_double xsf_fdtri(npy_double, npy_double, npy_double);
|
||||
npy_double xsf_gdtr(npy_double, npy_double, npy_double);
|
||||
npy_double xsf_gdtrc(npy_double, npy_double, npy_double);
|
||||
npy_double xsf_gdtrib(npy_double, npy_double, npy_double);
|
||||
npy_cdouble chyp1f1_wrap(npy_double, npy_double, npy_cdouble);
|
||||
npy_double special_cyl_bessel_k_int(npy_intp, npy_double);
|
||||
npy_double xsf_kolmogi(npy_double);
|
||||
npy_double xsf_kolmogorov(npy_double);
|
||||
npy_double pmv_wrap(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_nbdtr_wrap(npy_intp, npy_intp, npy_double);
|
||||
npy_double cephes_nbdtrc_wrap(npy_intp, npy_intp, npy_double);
|
||||
npy_double cephes_nbdtri_wrap(npy_intp, npy_intp, npy_double);
|
||||
npy_double xsf_ndtri(npy_double);
|
||||
npy_double xsf_owens_t(npy_double, npy_double);
|
||||
npy_double xsf_pdtr(npy_double, npy_double);
|
||||
npy_double xsf_pdtrc(npy_double, npy_double);
|
||||
npy_double cephes_pdtri_wrap(npy_intp, npy_double);
|
||||
npy_double cephes_poch(npy_double, npy_double);
|
||||
npy_double cephes_round(npy_double);
|
||||
npy_int xsf_cshichi(npy_cdouble, npy_cdouble *, npy_cdouble *);
|
||||
npy_int xsf_shichi(npy_double, npy_double *, npy_double *);
|
||||
npy_int xsf_csici(npy_cdouble, npy_cdouble *, npy_cdouble *);
|
||||
npy_int xsf_sici(npy_double, npy_double *, npy_double *);
|
||||
npy_double cephes_smirnov_wrap(npy_intp, npy_double);
|
||||
npy_double cephes_smirnovi_wrap(npy_intp, npy_double);
|
||||
npy_double cephes_spence(npy_double);
|
||||
npy_double xsf_tukeylambdacdf(npy_double, npy_double);
|
||||
npy_double cephes_yn_wrap(npy_intp, npy_double);
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
# This file is not meant for public use and will be removed in SciPy v2.0.0.
|
||||
|
||||
from scipy._lib.deprecation import _sub_module_deprecation
|
||||
|
||||
__all__: list[str] = []
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
return _sub_module_deprecation(sub_package="special", module="add_newdocs",
|
||||
private_modules=["_add_newdocs"], all=__all__,
|
||||
attribute=name)
|
||||
@@ -0,0 +1,87 @@
|
||||
# This file is not meant for public use and will be removed in SciPy v2.0.0.
|
||||
# Use the `scipy.special` namespace for importing the functions
|
||||
# included below.
|
||||
|
||||
from scipy._lib.deprecation import _sub_module_deprecation
|
||||
|
||||
|
||||
__all__ = [ # noqa: F822
|
||||
'ai_zeros',
|
||||
'assoc_laguerre',
|
||||
'bei_zeros',
|
||||
'beip_zeros',
|
||||
'ber_zeros',
|
||||
'bernoulli',
|
||||
'berp_zeros',
|
||||
'bi_zeros',
|
||||
'clpmn',
|
||||
'comb',
|
||||
'digamma',
|
||||
'diric',
|
||||
'erf_zeros',
|
||||
'euler',
|
||||
'factorial',
|
||||
'factorial2',
|
||||
'factorialk',
|
||||
'fresnel_zeros',
|
||||
'fresnelc_zeros',
|
||||
'fresnels_zeros',
|
||||
'gamma',
|
||||
'h1vp',
|
||||
'h2vp',
|
||||
'hankel1',
|
||||
'hankel2',
|
||||
'iv',
|
||||
'ivp',
|
||||
'jn_zeros',
|
||||
'jnjnp_zeros',
|
||||
'jnp_zeros',
|
||||
'jnyn_zeros',
|
||||
'jv',
|
||||
'jvp',
|
||||
'kei_zeros',
|
||||
'keip_zeros',
|
||||
'kelvin_zeros',
|
||||
'ker_zeros',
|
||||
'kerp_zeros',
|
||||
'kv',
|
||||
'kvp',
|
||||
'lmbda',
|
||||
'lpmn',
|
||||
'lpn',
|
||||
'lqmn',
|
||||
'lqn',
|
||||
'mathieu_a',
|
||||
'mathieu_b',
|
||||
'mathieu_even_coef',
|
||||
'mathieu_odd_coef',
|
||||
'obl_cv_seq',
|
||||
'pbdn_seq',
|
||||
'pbdv_seq',
|
||||
'pbvv_seq',
|
||||
'perm',
|
||||
'polygamma',
|
||||
'pro_cv_seq',
|
||||
'psi',
|
||||
'riccati_jn',
|
||||
'riccati_yn',
|
||||
'sinc',
|
||||
'y0_zeros',
|
||||
'y1_zeros',
|
||||
'y1p_zeros',
|
||||
'yn_zeros',
|
||||
'ynp_zeros',
|
||||
'yv',
|
||||
'yvp',
|
||||
'zeta'
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
return _sub_module_deprecation(sub_package="special", module="basic",
|
||||
private_modules=["_basic", "_ufuncs"], all=__all__,
|
||||
attribute=name)
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,259 @@
|
||||
|
||||
ctypedef fused number_t:
|
||||
double complex
|
||||
double
|
||||
|
||||
cpdef number_t spherical_jn(Py_ssize_t n, number_t z, bint derivative=*) noexcept nogil
|
||||
cpdef number_t spherical_yn(Py_ssize_t n, number_t z, bint derivative=*) noexcept nogil
|
||||
cpdef number_t spherical_in(Py_ssize_t n, number_t z, bint derivative=*) noexcept nogil
|
||||
cpdef number_t spherical_kn(Py_ssize_t n, number_t z, bint derivative=*) noexcept nogil
|
||||
|
||||
ctypedef fused Dd_number_t:
|
||||
double complex
|
||||
double
|
||||
|
||||
ctypedef fused df_number_t:
|
||||
double
|
||||
float
|
||||
|
||||
ctypedef fused dfg_number_t:
|
||||
double
|
||||
float
|
||||
long double
|
||||
|
||||
ctypedef fused dlp_number_t:
|
||||
double
|
||||
long
|
||||
Py_ssize_t
|
||||
|
||||
cpdef double voigt_profile(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double agm(double x0, double x1) noexcept nogil
|
||||
cdef void airy(Dd_number_t x0, Dd_number_t *y0, Dd_number_t *y1, Dd_number_t *y2, Dd_number_t *y3) noexcept nogil
|
||||
cdef void airye(Dd_number_t x0, Dd_number_t *y0, Dd_number_t *y1, Dd_number_t *y2, Dd_number_t *y3) noexcept nogil
|
||||
cpdef double bdtr(double x0, dlp_number_t x1, double x2) noexcept nogil
|
||||
cpdef double bdtrc(double x0, dlp_number_t x1, double x2) noexcept nogil
|
||||
cpdef double bdtri(double x0, dlp_number_t x1, double x2) noexcept nogil
|
||||
cpdef double bdtrik(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double bdtrin(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double bei(double x0) noexcept nogil
|
||||
cpdef double beip(double x0) noexcept nogil
|
||||
cpdef double ber(double x0) noexcept nogil
|
||||
cpdef double berp(double x0) noexcept nogil
|
||||
cpdef double besselpoly(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double beta(double x0, double x1) noexcept nogil
|
||||
cpdef df_number_t betainc(df_number_t x0, df_number_t x1, df_number_t x2) noexcept nogil
|
||||
cpdef df_number_t betaincc(df_number_t x0, df_number_t x1, df_number_t x2) noexcept nogil
|
||||
cpdef df_number_t betaincinv(df_number_t x0, df_number_t x1, df_number_t x2) noexcept nogil
|
||||
cpdef df_number_t betainccinv(df_number_t x0, df_number_t x1, df_number_t x2) noexcept nogil
|
||||
cpdef double betaln(double x0, double x1) noexcept nogil
|
||||
cpdef double binom(double x0, double x1) noexcept nogil
|
||||
cpdef double boxcox(double x0, double x1) noexcept nogil
|
||||
cpdef double boxcox1p(double x0, double x1) noexcept nogil
|
||||
cpdef double btdtria(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double btdtrib(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double cbrt(double x0) noexcept nogil
|
||||
cpdef double chdtr(double x0, double x1) noexcept nogil
|
||||
cpdef double chdtrc(double x0, double x1) noexcept nogil
|
||||
cpdef double chdtri(double x0, double x1) noexcept nogil
|
||||
cpdef double chdtriv(double x0, double x1) noexcept nogil
|
||||
cpdef double chndtr(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double chndtridf(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double chndtrinc(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double chndtrix(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double cosdg(double x0) noexcept nogil
|
||||
cpdef double cosm1(double x0) noexcept nogil
|
||||
cpdef double cotdg(double x0) noexcept nogil
|
||||
cpdef Dd_number_t dawsn(Dd_number_t x0) noexcept nogil
|
||||
cpdef double ellipe(double x0) noexcept nogil
|
||||
cpdef double ellipeinc(double x0, double x1) noexcept nogil
|
||||
cdef void ellipj(double x0, double x1, double *y0, double *y1, double *y2, double *y3) noexcept nogil
|
||||
cpdef double ellipkinc(double x0, double x1) noexcept nogil
|
||||
cpdef double ellipkm1(double x0) noexcept nogil
|
||||
cpdef double ellipk(double x0) noexcept nogil
|
||||
cpdef Dd_number_t elliprc(Dd_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t elliprd(Dd_number_t x0, Dd_number_t x1, Dd_number_t x2) noexcept nogil
|
||||
cpdef Dd_number_t elliprf(Dd_number_t x0, Dd_number_t x1, Dd_number_t x2) noexcept nogil
|
||||
cpdef Dd_number_t elliprg(Dd_number_t x0, Dd_number_t x1, Dd_number_t x2) noexcept nogil
|
||||
cpdef Dd_number_t elliprj(Dd_number_t x0, Dd_number_t x1, Dd_number_t x2, Dd_number_t x3) noexcept nogil
|
||||
cpdef double entr(double x0) noexcept nogil
|
||||
cpdef Dd_number_t erf(Dd_number_t x0) noexcept nogil
|
||||
cpdef Dd_number_t erfc(Dd_number_t x0) noexcept nogil
|
||||
cpdef Dd_number_t erfcx(Dd_number_t x0) noexcept nogil
|
||||
cpdef Dd_number_t erfi(Dd_number_t x0) noexcept nogil
|
||||
cpdef df_number_t erfinv(df_number_t x0) noexcept nogil
|
||||
cpdef double erfcinv(double x0) noexcept nogil
|
||||
cpdef Dd_number_t eval_chebyc(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_chebys(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_chebyt(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_chebyu(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_gegenbauer(dlp_number_t x0, double x1, Dd_number_t x2) noexcept nogil
|
||||
cpdef Dd_number_t eval_genlaguerre(dlp_number_t x0, double x1, Dd_number_t x2) noexcept nogil
|
||||
cpdef double eval_hermite(Py_ssize_t x0, double x1) noexcept nogil
|
||||
cpdef double eval_hermitenorm(Py_ssize_t x0, double x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_jacobi(dlp_number_t x0, double x1, double x2, Dd_number_t x3) noexcept nogil
|
||||
cpdef Dd_number_t eval_laguerre(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_legendre(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_sh_chebyt(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_sh_chebyu(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t eval_sh_jacobi(dlp_number_t x0, double x1, double x2, Dd_number_t x3) noexcept nogil
|
||||
cpdef Dd_number_t eval_sh_legendre(dlp_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t exp1(Dd_number_t x0) noexcept nogil
|
||||
cpdef double exp10(double x0) noexcept nogil
|
||||
cpdef double exp2(double x0) noexcept nogil
|
||||
cpdef Dd_number_t expi(Dd_number_t x0) noexcept nogil
|
||||
cpdef dfg_number_t expit(dfg_number_t x0) noexcept nogil
|
||||
cpdef Dd_number_t expm1(Dd_number_t x0) noexcept nogil
|
||||
cpdef double expn(dlp_number_t x0, double x1) noexcept nogil
|
||||
cpdef double exprel(double x0) noexcept nogil
|
||||
cpdef double fdtr(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double fdtrc(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double fdtri(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double fdtridfd(double x0, double x1, double x2) noexcept nogil
|
||||
cdef void fresnel(Dd_number_t x0, Dd_number_t *y0, Dd_number_t *y1) noexcept nogil
|
||||
cpdef Dd_number_t gamma(Dd_number_t x0) noexcept nogil
|
||||
cpdef double gammainc(double x0, double x1) noexcept nogil
|
||||
cpdef double gammaincc(double x0, double x1) noexcept nogil
|
||||
cpdef double gammainccinv(double x0, double x1) noexcept nogil
|
||||
cpdef double gammaincinv(double x0, double x1) noexcept nogil
|
||||
cpdef double gammaln(double x0) noexcept nogil
|
||||
cpdef double gammasgn(double x0) noexcept nogil
|
||||
cpdef double gdtr(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double gdtrc(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double gdtria(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double gdtrib(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double gdtrix(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double complex hankel1(double x0, double complex x1) noexcept nogil
|
||||
cpdef double complex hankel1e(double x0, double complex x1) noexcept nogil
|
||||
cpdef double complex hankel2(double x0, double complex x1) noexcept nogil
|
||||
cpdef double complex hankel2e(double x0, double complex x1) noexcept nogil
|
||||
cpdef double huber(double x0, double x1) noexcept nogil
|
||||
cpdef Dd_number_t hyp0f1(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t hyp1f1(double x0, double x1, Dd_number_t x2) noexcept nogil
|
||||
cpdef Dd_number_t hyp2f1(double x0, double x1, double x2, Dd_number_t x3) noexcept nogil
|
||||
cpdef double hyperu(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double i0(double x0) noexcept nogil
|
||||
cpdef double i0e(double x0) noexcept nogil
|
||||
cpdef double i1(double x0) noexcept nogil
|
||||
cpdef double i1e(double x0) noexcept nogil
|
||||
cpdef double inv_boxcox(double x0, double x1) noexcept nogil
|
||||
cpdef double inv_boxcox1p(double x0, double x1) noexcept nogil
|
||||
cdef void it2i0k0(double x0, double *y0, double *y1) noexcept nogil
|
||||
cdef void it2j0y0(double x0, double *y0, double *y1) noexcept nogil
|
||||
cpdef double it2struve0(double x0) noexcept nogil
|
||||
cdef void itairy(double x0, double *y0, double *y1, double *y2, double *y3) noexcept nogil
|
||||
cdef void iti0k0(double x0, double *y0, double *y1) noexcept nogil
|
||||
cdef void itj0y0(double x0, double *y0, double *y1) noexcept nogil
|
||||
cpdef double itmodstruve0(double x0) noexcept nogil
|
||||
cpdef double itstruve0(double x0) noexcept nogil
|
||||
cpdef Dd_number_t iv(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t ive(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef double j0(double x0) noexcept nogil
|
||||
cpdef double j1(double x0) noexcept nogil
|
||||
cpdef Dd_number_t jv(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t jve(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef double k0(double x0) noexcept nogil
|
||||
cpdef double k0e(double x0) noexcept nogil
|
||||
cpdef double k1(double x0) noexcept nogil
|
||||
cpdef double k1e(double x0) noexcept nogil
|
||||
cpdef double kei(double x0) noexcept nogil
|
||||
cpdef double keip(double x0) noexcept nogil
|
||||
cdef void kelvin(double x0, double complex *y0, double complex *y1, double complex *y2, double complex *y3) noexcept nogil
|
||||
cpdef double ker(double x0) noexcept nogil
|
||||
cpdef double kerp(double x0) noexcept nogil
|
||||
cpdef double kl_div(double x0, double x1) noexcept nogil
|
||||
cpdef double kn(dlp_number_t x0, double x1) noexcept nogil
|
||||
cpdef double kolmogi(double x0) noexcept nogil
|
||||
cpdef double kolmogorov(double x0) noexcept nogil
|
||||
cpdef Dd_number_t kv(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t kve(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t log1p(Dd_number_t x0) noexcept nogil
|
||||
cpdef dfg_number_t log_expit(dfg_number_t x0) noexcept nogil
|
||||
cpdef Dd_number_t log_ndtr(Dd_number_t x0) noexcept nogil
|
||||
cpdef Dd_number_t loggamma(Dd_number_t x0) noexcept nogil
|
||||
cpdef dfg_number_t logit(dfg_number_t x0) noexcept nogil
|
||||
cpdef double lpmv(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double mathieu_a(double x0, double x1) noexcept nogil
|
||||
cpdef double mathieu_b(double x0, double x1) noexcept nogil
|
||||
cdef void mathieu_cem(double x0, double x1, double x2, double *y0, double *y1) noexcept nogil
|
||||
cdef void mathieu_modcem1(double x0, double x1, double x2, double *y0, double *y1) noexcept nogil
|
||||
cdef void mathieu_modcem2(double x0, double x1, double x2, double *y0, double *y1) noexcept nogil
|
||||
cdef void mathieu_modsem1(double x0, double x1, double x2, double *y0, double *y1) noexcept nogil
|
||||
cdef void mathieu_modsem2(double x0, double x1, double x2, double *y0, double *y1) noexcept nogil
|
||||
cdef void mathieu_sem(double x0, double x1, double x2, double *y0, double *y1) noexcept nogil
|
||||
cdef void modfresnelm(double x0, double complex *y0, double complex *y1) noexcept nogil
|
||||
cdef void modfresnelp(double x0, double complex *y0, double complex *y1) noexcept nogil
|
||||
cpdef double modstruve(double x0, double x1) noexcept nogil
|
||||
cpdef double nbdtr(dlp_number_t x0, dlp_number_t x1, double x2) noexcept nogil
|
||||
cpdef double nbdtrc(dlp_number_t x0, dlp_number_t x1, double x2) noexcept nogil
|
||||
cpdef double nbdtri(dlp_number_t x0, dlp_number_t x1, double x2) noexcept nogil
|
||||
cpdef double nbdtrik(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double nbdtrin(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef df_number_t ncfdtr(df_number_t x0, df_number_t x1, df_number_t x2, df_number_t x3) noexcept nogil
|
||||
cpdef df_number_t ncfdtri(df_number_t x0, df_number_t x1, df_number_t x2, df_number_t x3) noexcept nogil
|
||||
cpdef double ncfdtridfd(double x0, double x1, double x2, double x3) noexcept nogil
|
||||
cpdef double ncfdtridfn(double x0, double x1, double x2, double x3) noexcept nogil
|
||||
cpdef double ncfdtrinc(double x0, double x1, double x2, double x3) noexcept nogil
|
||||
cpdef df_number_t nctdtr(df_number_t x0, df_number_t x1, df_number_t x2) noexcept nogil
|
||||
cpdef double nctdtridf(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double nctdtrinc(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef df_number_t nctdtrit(df_number_t x0, df_number_t x1, df_number_t x2) noexcept nogil
|
||||
cpdef Dd_number_t ndtr(Dd_number_t x0) noexcept nogil
|
||||
cpdef double ndtri(double x0) noexcept nogil
|
||||
cpdef double nrdtrimn(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double nrdtrisd(double x0, double x1, double x2) noexcept nogil
|
||||
cdef void obl_ang1(double x0, double x1, double x2, double x3, double *y0, double *y1) noexcept nogil
|
||||
cdef void obl_ang1_cv(double x0, double x1, double x2, double x3, double x4, double *y0, double *y1) noexcept nogil
|
||||
cpdef double obl_cv(double x0, double x1, double x2) noexcept nogil
|
||||
cdef void obl_rad1(double x0, double x1, double x2, double x3, double *y0, double *y1) noexcept nogil
|
||||
cdef void obl_rad1_cv(double x0, double x1, double x2, double x3, double x4, double *y0, double *y1) noexcept nogil
|
||||
cdef void obl_rad2(double x0, double x1, double x2, double x3, double *y0, double *y1) noexcept nogil
|
||||
cdef void obl_rad2_cv(double x0, double x1, double x2, double x3, double x4, double *y0, double *y1) noexcept nogil
|
||||
cpdef double owens_t(double x0, double x1) noexcept nogil
|
||||
cdef void pbdv(double x0, double x1, double *y0, double *y1) noexcept nogil
|
||||
cdef void pbvv(double x0, double x1, double *y0, double *y1) noexcept nogil
|
||||
cdef void pbwa(double x0, double x1, double *y0, double *y1) noexcept nogil
|
||||
cpdef double pdtr(double x0, double x1) noexcept nogil
|
||||
cpdef double pdtrc(double x0, double x1) noexcept nogil
|
||||
cpdef double pdtri(dlp_number_t x0, double x1) noexcept nogil
|
||||
cpdef double pdtrik(double x0, double x1) noexcept nogil
|
||||
cpdef double poch(double x0, double x1) noexcept nogil
|
||||
cpdef df_number_t powm1(df_number_t x0, df_number_t x1) noexcept nogil
|
||||
cdef void pro_ang1(double x0, double x1, double x2, double x3, double *y0, double *y1) noexcept nogil
|
||||
cdef void pro_ang1_cv(double x0, double x1, double x2, double x3, double x4, double *y0, double *y1) noexcept nogil
|
||||
cpdef double pro_cv(double x0, double x1, double x2) noexcept nogil
|
||||
cdef void pro_rad1(double x0, double x1, double x2, double x3, double *y0, double *y1) noexcept nogil
|
||||
cdef void pro_rad1_cv(double x0, double x1, double x2, double x3, double x4, double *y0, double *y1) noexcept nogil
|
||||
cdef void pro_rad2(double x0, double x1, double x2, double x3, double *y0, double *y1) noexcept nogil
|
||||
cdef void pro_rad2_cv(double x0, double x1, double x2, double x3, double x4, double *y0, double *y1) noexcept nogil
|
||||
cpdef double pseudo_huber(double x0, double x1) noexcept nogil
|
||||
cpdef Dd_number_t psi(Dd_number_t x0) noexcept nogil
|
||||
cpdef double radian(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double rel_entr(double x0, double x1) noexcept nogil
|
||||
cpdef Dd_number_t rgamma(Dd_number_t x0) noexcept nogil
|
||||
cpdef double round(double x0) noexcept nogil
|
||||
cdef void shichi(Dd_number_t x0, Dd_number_t *y0, Dd_number_t *y1) noexcept nogil
|
||||
cdef void sici(Dd_number_t x0, Dd_number_t *y0, Dd_number_t *y1) noexcept nogil
|
||||
cpdef double sindg(double x0) noexcept nogil
|
||||
cpdef double smirnov(dlp_number_t x0, double x1) noexcept nogil
|
||||
cpdef double smirnovi(dlp_number_t x0, double x1) noexcept nogil
|
||||
cpdef Dd_number_t spence(Dd_number_t x0) noexcept nogil
|
||||
cpdef double complex sph_harm(dlp_number_t x0, dlp_number_t x1, double x2, double x3) noexcept nogil
|
||||
cpdef double stdtr(double x0, double x1) noexcept nogil
|
||||
cpdef double stdtridf(double x0, double x1) noexcept nogil
|
||||
cpdef double stdtrit(double x0, double x1) noexcept nogil
|
||||
cpdef double struve(double x0, double x1) noexcept nogil
|
||||
cpdef double tandg(double x0) noexcept nogil
|
||||
cpdef double tklmbda(double x0, double x1) noexcept nogil
|
||||
cpdef double complex wofz(double complex x0) noexcept nogil
|
||||
cpdef Dd_number_t wrightomega(Dd_number_t x0) noexcept nogil
|
||||
cpdef Dd_number_t xlog1py(Dd_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t xlogy(Dd_number_t x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef double y0(double x0) noexcept nogil
|
||||
cpdef double y1(double x0) noexcept nogil
|
||||
cpdef double yn(dlp_number_t x0, double x1) noexcept nogil
|
||||
cpdef Dd_number_t yv(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef Dd_number_t yve(double x0, Dd_number_t x1) noexcept nogil
|
||||
cpdef double zetac(double x0) noexcept nogil
|
||||
cpdef double wright_bessel(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double log_wright_bessel(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double ndtri_exp(double x0) noexcept nogil
|
||||
@@ -0,0 +1,3 @@
|
||||
from typing import Any
|
||||
|
||||
def __getattr__(name) -> Any: ...
|
||||
@@ -0,0 +1,45 @@
|
||||
# This file is not meant for public use and will be removed in SciPy v2.0.0.
|
||||
# Use the `scipy.special` namespace for importing the functions
|
||||
# included below.
|
||||
|
||||
from scipy._lib.deprecation import _sub_module_deprecation
|
||||
|
||||
|
||||
_polyfuns = ['legendre', 'chebyt', 'chebyu', 'chebyc', 'chebys',
|
||||
'jacobi', 'laguerre', 'genlaguerre', 'hermite',
|
||||
'hermitenorm', 'gegenbauer', 'sh_legendre', 'sh_chebyt',
|
||||
'sh_chebyu', 'sh_jacobi']
|
||||
|
||||
# Correspondence between new and old names of root functions
|
||||
_rootfuns_map = {'roots_legendre': 'p_roots',
|
||||
'roots_chebyt': 't_roots',
|
||||
'roots_chebyu': 'u_roots',
|
||||
'roots_chebyc': 'c_roots',
|
||||
'roots_chebys': 's_roots',
|
||||
'roots_jacobi': 'j_roots',
|
||||
'roots_laguerre': 'l_roots',
|
||||
'roots_genlaguerre': 'la_roots',
|
||||
'roots_hermite': 'h_roots',
|
||||
'roots_hermitenorm': 'he_roots',
|
||||
'roots_gegenbauer': 'cg_roots',
|
||||
'roots_sh_legendre': 'ps_roots',
|
||||
'roots_sh_chebyt': 'ts_roots',
|
||||
'roots_sh_chebyu': 'us_roots',
|
||||
'roots_sh_jacobi': 'js_roots'}
|
||||
|
||||
|
||||
__all__ = _polyfuns + list(_rootfuns_map.keys()) + [ # noqa: F822
|
||||
'airy', 'p_roots', 't_roots', 'u_roots', 'c_roots', 's_roots',
|
||||
'j_roots', 'l_roots', 'la_roots', 'h_roots', 'he_roots', 'cg_roots',
|
||||
'ps_roots', 'ts_roots', 'us_roots', 'js_roots'
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
return _sub_module_deprecation(sub_package="special", module="orthogonal",
|
||||
private_modules=["_orthogonal"], all=__all__,
|
||||
attribute=name)
|
||||
@@ -0,0 +1,20 @@
|
||||
# This file is not meant for public use and will be removed in SciPy v2.0.0.
|
||||
# Use the `scipy.special` namespace for importing the functions
|
||||
# included below.
|
||||
|
||||
from scipy._lib.deprecation import _sub_module_deprecation
|
||||
|
||||
__all__ = [ # noqa: F822
|
||||
'SpecialFunctionWarning',
|
||||
'SpecialFunctionError'
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
return _sub_module_deprecation(sub_package="special", module="sf_error",
|
||||
private_modules=["_sf_error"], all=__all__,
|
||||
attribute=name)
|
||||
@@ -0,0 +1,24 @@
|
||||
# This file is not meant for public use and will be removed in SciPy v2.0.0.
|
||||
# Use the `scipy.special` namespace for importing the functions
|
||||
# included below.
|
||||
|
||||
from scipy._lib.deprecation import _sub_module_deprecation
|
||||
|
||||
# ruff: noqa: F822
|
||||
__all__ = [
|
||||
'clpmn',
|
||||
'lpmn',
|
||||
'lpn',
|
||||
'lqmn',
|
||||
'pbdv'
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
return _sub_module_deprecation(sub_package="special", module="specfun",
|
||||
private_modules=["_basic", "_specfun"], all=__all__,
|
||||
attribute=name)
|
||||
@@ -0,0 +1,17 @@
|
||||
# This file is not meant for public use and will be removed in SciPy v2.0.0.
|
||||
# Use the `scipy.special` namespace for importing the functions
|
||||
# included below.
|
||||
|
||||
from scipy._lib.deprecation import _sub_module_deprecation
|
||||
|
||||
__all__ = ['multigammaln'] # noqa: F822
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
return _sub_module_deprecation(sub_package="special", module="spfun_stats",
|
||||
private_modules=["_spfun_stats"], all=__all__,
|
||||
attribute=name)
|
||||
@@ -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.
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,123 @@
|
||||
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
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize('x, expected', [(0, -np.inf), (np.inf, np.inf)])
|
||||
def test_limits(self, x, expected):
|
||||
y = sc.expi(x)
|
||||
assert y == expected
|
||||
|
||||
|
||||
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)
|
||||
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)
|
||||
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
|
||||
@@ -0,0 +1,53 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_allclose
|
||||
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
def test_symmetries():
|
||||
np.random.seed(1234)
|
||||
a, h = np.random.rand(100), np.random.rand(100)
|
||||
assert_equal(sc.owens_t(h, a), sc.owens_t(-h, a))
|
||||
assert_equal(sc.owens_t(h, a), -sc.owens_t(h, -a))
|
||||
|
||||
|
||||
def test_special_cases():
|
||||
assert_equal(sc.owens_t(5, 0), 0)
|
||||
assert_allclose(sc.owens_t(0, 5), 0.5*np.arctan(5)/np.pi,
|
||||
rtol=5e-14)
|
||||
# Target value is 0.5*Phi(5)*(1 - Phi(5)) for Phi the CDF of the
|
||||
# standard normal distribution
|
||||
assert_allclose(sc.owens_t(5, 1), 1.4332574485503512543e-07,
|
||||
rtol=5e-14)
|
||||
|
||||
|
||||
def test_nans():
|
||||
assert_equal(sc.owens_t(20, np.nan), np.nan)
|
||||
assert_equal(sc.owens_t(np.nan, 20), np.nan)
|
||||
assert_equal(sc.owens_t(np.nan, np.nan), np.nan)
|
||||
|
||||
|
||||
def test_infs():
|
||||
h, a = 0, np.inf
|
||||
# T(0, a) = 1/2π * arctan(a)
|
||||
res = 1/(2*np.pi) * np.arctan(a)
|
||||
assert_allclose(sc.owens_t(h, a), res, rtol=5e-14)
|
||||
assert_allclose(sc.owens_t(h, -a), -res, rtol=5e-14)
|
||||
|
||||
h = 1
|
||||
# Refer Owens T function definition in Wikipedia
|
||||
# https://en.wikipedia.org/wiki/Owen%27s_T_function
|
||||
# Value approximated through Numerical Integration
|
||||
# using scipy.integrate.quad
|
||||
# quad(lambda x: 1/(2*pi)*(exp(-0.5*(1*1)*(1+x*x))/(1+x*x)), 0, inf)
|
||||
res = 0.07932762696572854
|
||||
assert_allclose(sc.owens_t(h, np.inf), res, rtol=5e-14)
|
||||
assert_allclose(sc.owens_t(h, -np.inf), -res, rtol=5e-14)
|
||||
|
||||
assert_equal(sc.owens_t(np.inf, 1), 0)
|
||||
assert_equal(sc.owens_t(-np.inf, 1), 0)
|
||||
|
||||
assert_equal(sc.owens_t(np.inf, np.inf), 0)
|
||||
assert_equal(sc.owens_t(-np.inf, np.inf), 0)
|
||||
assert_equal(sc.owens_t(np.inf, -np.inf), -0.0)
|
||||
assert_equal(sc.owens_t(-np.inf, -np.inf), -0.0)
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Tests for parabolic cylinder functions.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
import scipy.special as sc
|
||||
|
||||
|
||||
def test_pbwa_segfault():
|
||||
# Regression test for https://github.com/scipy/scipy/issues/6208.
|
||||
#
|
||||
# Data generated by mpmath.
|
||||
#
|
||||
w = 1.02276567211316867161
|
||||
wp = -0.48887053372346189882
|
||||
assert_allclose(sc.pbwa(0, 0), (w, wp), rtol=1e-13, atol=0)
|
||||
|
||||
|
||||
def test_pbwa_nan():
|
||||
# Check that NaN's are returned outside of the range in which the
|
||||
# implementation is accurate.
|
||||
pts = [(-6, -6), (-6, 6), (6, -6), (6, 6)]
|
||||
for p in pts:
|
||||
assert_equal(sc.pbwa(*p), (np.nan, np.nan))
|
||||
@@ -0,0 +1,48 @@
|
||||
import numpy as np
|
||||
import scipy.special as sc
|
||||
from numpy.testing import assert_almost_equal, assert_array_equal
|
||||
|
||||
|
||||
class TestPdtr:
|
||||
def test(self):
|
||||
val = sc.pdtr(0, 1)
|
||||
assert_almost_equal(val, np.exp(-1))
|
||||
|
||||
def test_m_zero(self):
|
||||
val = sc.pdtr([0, 1, 2], 0)
|
||||
assert_array_equal(val, [1, 1, 1])
|
||||
|
||||
def test_rounding(self):
|
||||
double_val = sc.pdtr([0.1, 1.1, 2.1], 1.0)
|
||||
int_val = sc.pdtr([0, 1, 2], 1.0)
|
||||
assert_array_equal(double_val, int_val)
|
||||
|
||||
def test_inf(self):
|
||||
val = sc.pdtr(np.inf, 1.0)
|
||||
assert_almost_equal(val, 1.0)
|
||||
|
||||
def test_domain(self):
|
||||
val = sc.pdtr(-1.1, 1.0)
|
||||
assert np.isnan(val)
|
||||
|
||||
class TestPdtrc:
|
||||
def test_value(self):
|
||||
val = sc.pdtrc(0, 1)
|
||||
assert_almost_equal(val, 1 - np.exp(-1))
|
||||
|
||||
def test_m_zero(self):
|
||||
val = sc.pdtrc([0, 1, 2], 0.0)
|
||||
assert_array_equal(val, [0, 0, 0])
|
||||
|
||||
def test_rounding(self):
|
||||
double_val = sc.pdtrc([0.1, 1.1, 2.1], 1.0)
|
||||
int_val = sc.pdtrc([0, 1, 2], 1.0)
|
||||
assert_array_equal(double_val, int_val)
|
||||
|
||||
def test_inf(self):
|
||||
val = sc.pdtrc(np.inf, 1.0)
|
||||
assert_almost_equal(val, 0.0)
|
||||
|
||||
def test_domain(self):
|
||||
val = sc.pdtrc(-1.1, 1.0)
|
||||
assert np.isnan(val)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user