asd
This commit is contained in:
894
venv/lib/python3.12/site-packages/scipy/special/__init__.py
Normal file
894
venv/lib/python3.12/site-packages/scipy/special/__init__.py
Normal file
@ -0,0 +1,894 @@
|
||||
"""
|
||||
========================================
|
||||
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; 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/
|
||||
|
||||
btdtr -- Cumulative distribution function of the beta distribution.
|
||||
btdtri -- The `p`-th quantile of the beta distribution.
|
||||
btdtria -- Inverse of `btdtr` with respect to `a`.
|
||||
btdtrib -- btdtria(a, p, x).
|
||||
|
||||
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/
|
||||
|
||||
lpmv -- Associated Legendre function of integer order and real degree.
|
||||
sph_harm -- Compute spherical harmonics.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
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 (physicst'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.
|
||||
|
||||
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
|
||||
|
||||
import os
|
||||
import warnings
|
||||
|
||||
|
||||
def _load_libsf_error_state():
|
||||
"""Load libsf_error_state.dll shared library on Windows
|
||||
|
||||
libsf_error_state manages shared state used by
|
||||
``scipy.special.seterr`` and ``scipy.special.geterr`` so that these
|
||||
can work consistently between special functions provided by different
|
||||
extension modules. This shared library is installed in scipy/special
|
||||
alongside this __init__.py file. Due to lack of rpath support, Windows
|
||||
cannot find shared libraries installed within wheels. To circumvent this,
|
||||
we pre-load ``lib_sf_error_state.dll`` when on Windows.
|
||||
|
||||
The logic for this function was borrowed from the function ``make_init``
|
||||
in `scipy/tools/openblas_support.py`:
|
||||
https://github.com/scipy/scipy/blob/bb92c8014e21052e7dde67a76b28214dd1dcb94a/tools/openblas_support.py#L239-L274
|
||||
""" # noqa: E501
|
||||
if os.name == "nt":
|
||||
try:
|
||||
from ctypes import WinDLL
|
||||
basedir = os.path.dirname(__file__)
|
||||
except: # noqa: E722
|
||||
pass
|
||||
else:
|
||||
dll_path = os.path.join(basedir, "libsf_error_state.dll")
|
||||
if os.path.exists(dll_path):
|
||||
WinDLL(dll_path)
|
||||
|
||||
|
||||
_load_libsf_error_state()
|
||||
|
||||
|
||||
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 (
|
||||
log_ndtr, ndtr, ndtri, erf, erfc, i0, i0e, i1, i1e, gammaln,
|
||||
gammainc, gammaincc, logit, expit, entr, rel_entr, xlogy, chdtrc)
|
||||
|
||||
from . import _basic
|
||||
from ._basic import *
|
||||
|
||||
from ._logsumexp import logsumexp, softmax, log_softmax
|
||||
|
||||
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__
|
||||
__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
|
||||
|
||||
_depr_msg = ('\nThis function was deprecated in SciPy 1.12.0, and will be '
|
||||
'removed in SciPy 1.14.0. Use scipy.special.{} instead.')
|
||||
|
||||
|
||||
def btdtr(*args, **kwargs): # type: ignore [no-redef]
|
||||
warnings.warn(_depr_msg.format('betainc'), category=DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return _ufuncs.btdtr(*args, **kwargs)
|
||||
|
||||
|
||||
btdtr.__doc__ = _ufuncs.btdtr.__doc__ # type: ignore [misc]
|
||||
|
||||
|
||||
def btdtri(*args, **kwargs): # type: ignore [no-redef]
|
||||
warnings.warn(_depr_msg.format('betaincinv'), category=DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return _ufuncs.btdtri(*args, **kwargs)
|
||||
|
||||
|
||||
btdtri.__doc__ = _ufuncs.btdtri.__doc__ # type: ignore [misc]
|
||||
|
||||
|
||||
def _get_include():
|
||||
"""This function is for development purposes only.
|
||||
|
||||
This function could disappear or its behavior could change at any time.
|
||||
"""
|
||||
import os
|
||||
return os.path.dirname(__file__)
|
||||
|
||||
12847
venv/lib/python3.12/site-packages/scipy/special/_add_newdocs.py
Normal file
12847
venv/lib/python3.12/site-packages/scipy/special/_add_newdocs.py
Normal file
File diff suppressed because it is too large
Load Diff
3451
venv/lib/python3.12/site-packages/scipy/special/_basic.py
Normal file
3451
venv/lib/python3.12/site-packages/scipy/special/_basic.py
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
214
venv/lib/python3.12/site-packages/scipy/special/_ellip_harm.py
Normal file
214
venv/lib/python3.12/site-packages/scipy/special/_ellip_harm.py
Normal file
@ -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)
|
||||
Binary file not shown.
Binary file not shown.
149
venv/lib/python3.12/site-packages/scipy/special/_lambertw.py
Normal file
149
venv/lib/python3.12/site-packages/scipy/special/_lambertw.py
Normal file
@ -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)
|
||||
308
venv/lib/python3.12/site-packages/scipy/special/_logsumexp.py
Normal file
308
venv/lib/python3.12/site-packages/scipy/special/_logsumexp.py
Normal file
@ -0,0 +1,308 @@
|
||||
import numpy as np
|
||||
from scipy._lib._util import _asarray_validated
|
||||
|
||||
__all__ = ["logsumexp", "softmax", "log_softmax"]
|
||||
|
||||
|
||||
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
|
||||
--------
|
||||
numpy.logaddexp, 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.
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
a = _asarray_validated(a, check_finite=False)
|
||||
if b is not None:
|
||||
a, b = np.broadcast_arrays(a, b)
|
||||
if np.any(b == 0):
|
||||
a = a + 0. # promote to at least float
|
||||
a[b == 0] = -np.inf
|
||||
|
||||
# Scale by real part for complex inputs, because this affects
|
||||
# the magnitude of the exponential.
|
||||
initial_value = -np.inf if np.size(a) == 0 else None
|
||||
a_max = np.amax(a.real, axis=axis, keepdims=True, initial=initial_value)
|
||||
|
||||
if a_max.ndim > 0:
|
||||
a_max[~np.isfinite(a_max)] = 0
|
||||
elif not np.isfinite(a_max):
|
||||
a_max = 0
|
||||
|
||||
if b is not None:
|
||||
b = np.asarray(b)
|
||||
tmp = b * np.exp(a - a_max)
|
||||
else:
|
||||
tmp = np.exp(a - a_max)
|
||||
|
||||
# suppress warnings about log of zero
|
||||
with np.errstate(divide='ignore'):
|
||||
s = np.sum(tmp, axis=axis, keepdims=keepdims)
|
||||
if return_sign:
|
||||
# For complex, use the numpy>=2.0 convention for sign.
|
||||
if np.issubdtype(s.dtype, np.complexfloating):
|
||||
sgn = s / np.where(s == 0, 1, abs(s))
|
||||
else:
|
||||
sgn = np.sign(s)
|
||||
s = abs(s)
|
||||
out = np.log(s)
|
||||
|
||||
if not keepdims:
|
||||
a_max = np.squeeze(a_max, axis=axis)
|
||||
out += a_max
|
||||
|
||||
if return_sign:
|
||||
return out, sgn
|
||||
else:
|
||||
return out
|
||||
|
||||
|
||||
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.])
|
||||
|
||||
"""
|
||||
x = _asarray_validated(x, check_finite=False)
|
||||
x_max = np.amax(x, axis=axis, keepdims=True)
|
||||
exp_x_shifted = np.exp(x - x_max)
|
||||
return exp_x_shifted / np.sum(exp_x_shifted, axis=axis, keepdims=True)
|
||||
|
||||
|
||||
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])
|
||||
|
||||
"""
|
||||
|
||||
x = _asarray_validated(x, check_finite=False)
|
||||
|
||||
x_max = np.amax(x, axis=axis, keepdims=True)
|
||||
|
||||
if x_max.ndim > 0:
|
||||
x_max[~np.isfinite(x_max)] = 0
|
||||
elif not np.isfinite(x_max):
|
||||
x_max = 0
|
||||
|
||||
tmp = x - x_max
|
||||
exp_tmp = np.exp(tmp)
|
||||
|
||||
# suppress warnings about log of zero
|
||||
with np.errstate(divide='ignore'):
|
||||
s = np.sum(exp_tmp, axis=axis, keepdims=True)
|
||||
out = np.log(s)
|
||||
|
||||
out = tmp - out
|
||||
return out
|
||||
453
venv/lib/python3.12/site-packages/scipy/special/_mptestutils.py
Normal file
453
venv/lib/python3.12/site-packages/scipy/special/_mptestutils.py
Normal file
@ -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("-> %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))
|
||||
2605
venv/lib/python3.12/site-packages/scipy/special/_orthogonal.py
Normal file
2605
venv/lib/python3.12/site-packages/scipy/special/_orthogonal.py
Normal file
File diff suppressed because it is too large
Load Diff
331
venv/lib/python3.12/site-packages/scipy/special/_orthogonal.pyi
Normal file
331
venv/lib/python3.12/site-packages/scipy/special/_orthogonal.pyi
Normal file
@ -0,0 +1,331 @@
|
||||
from __future__ import annotations
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Literal,
|
||||
Optional,
|
||||
overload,
|
||||
)
|
||||
|
||||
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()
|
||||
15
venv/lib/python3.12/site-packages/scipy/special/_sf_error.py
Normal file
15
venv/lib/python3.12/site-packages/scipy/special/_sf_error.py
Normal file
@ -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
|
||||
Binary file not shown.
Binary file not shown.
106
venv/lib/python3.12/site-packages/scipy/special/_spfun_stats.py
Normal file
106
venv/lib/python3.12/site-packages/scipy/special/_spfun_stats.py
Normal file
@ -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,354 @@
|
||||
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 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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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(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,148 @@
|
||||
import os
|
||||
import sys
|
||||
import functools
|
||||
|
||||
import numpy as np
|
||||
import scipy
|
||||
from scipy._lib._array_api import (
|
||||
array_namespace, scipy_namespace_for, is_numpy, is_torch
|
||||
)
|
||||
from . import _ufuncs
|
||||
# These don't really need to be imported, but otherwise IDEs might not realize
|
||||
# that these are defined in this file / report an error in __init__.py
|
||||
from ._ufuncs import (
|
||||
log_ndtr, ndtr, ndtri, erf, erfc, i0, i0e, i1, i1e, gammaln, # noqa: F401
|
||||
gammainc, gammaincc, logit, expit, entr, rel_entr, xlogy, # noqa: F401
|
||||
chdtrc # noqa: F401
|
||||
)
|
||||
|
||||
_SCIPY_ARRAY_API = os.environ.get("SCIPY_ARRAY_API", False)
|
||||
array_api_compat_prefix = "scipy._lib.array_api_compat"
|
||||
|
||||
|
||||
def get_array_special_func(f_name, xp, n_array_args):
|
||||
spx = scipy_namespace_for(xp)
|
||||
f = None
|
||||
if is_numpy(xp):
|
||||
f = getattr(_ufuncs, f_name, None)
|
||||
elif is_torch(xp):
|
||||
f = getattr(xp.special, f_name, None)
|
||||
elif spx is not scipy:
|
||||
f = getattr(spx.special, f_name, None)
|
||||
|
||||
if f is not None:
|
||||
return f
|
||||
|
||||
# if generic array-API implementation is available, use that;
|
||||
# otherwise, fall back to NumPy/SciPy
|
||||
if f_name in _generic_implementations:
|
||||
_f = _generic_implementations[f_name](xp=xp, spx=spx)
|
||||
if _f is not None:
|
||||
return _f
|
||||
|
||||
_f = getattr(_ufuncs, f_name, None)
|
||||
def f(*args, _f=_f, _xp=xp, **kwargs):
|
||||
array_args = args[:n_array_args]
|
||||
other_args = args[n_array_args:]
|
||||
array_args = [np.asarray(arg) for arg in array_args]
|
||||
out = _f(*array_args, *other_args, **kwargs)
|
||||
return _xp.asarray(out)
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def _get_shape_dtype(*args, xp):
|
||||
args = xp.broadcast_arrays(*args)
|
||||
shape = args[0].shape
|
||||
dtype = xp.result_type(*args)
|
||||
if xp.isdtype(dtype, 'integral'):
|
||||
dtype = xp.float64
|
||||
args = [xp.asarray(arg, dtype=dtype) for arg in args]
|
||||
return args, shape, dtype
|
||||
|
||||
|
||||
def _rel_entr(xp, spx):
|
||||
def __rel_entr(x, y, *, xp=xp):
|
||||
args, shape, dtype = _get_shape_dtype(x, y, xp=xp)
|
||||
x, y = args
|
||||
res = xp.full(x.shape, xp.inf, dtype=dtype)
|
||||
res[(x == 0) & (y >= 0)] = xp.asarray(0, dtype=dtype)
|
||||
i = (x > 0) & (y > 0)
|
||||
res[i] = x[i] * (xp.log(x[i]) - xp.log(y[i]))
|
||||
return res
|
||||
return __rel_entr
|
||||
|
||||
|
||||
def _xlogy(xp, spx):
|
||||
def __xlogy(x, y, *, xp=xp):
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
temp = x * xp.log(y)
|
||||
return xp.where(x == 0., xp.asarray(0., dtype=temp.dtype), temp)
|
||||
return __xlogy
|
||||
|
||||
|
||||
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 = getattr(spx, 'gammaincc', None) # noqa: F811
|
||||
if gammaincc is None and hasattr(xp, 'special'):
|
||||
gammaincc = getattr(xp.special, 'gammaincc', None)
|
||||
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)
|
||||
res = xp.where(i_nan, xp.nan, res)
|
||||
return res
|
||||
return __chdtrc
|
||||
|
||||
|
||||
_generic_implementations = {'rel_entr': _rel_entr,
|
||||
'xlogy': _xlogy,
|
||||
'chdtrc': _chdtrc}
|
||||
|
||||
|
||||
# functools.wraps doesn't work because:
|
||||
# 'numpy.ufunc' object has no attribute '__module__'
|
||||
def support_alternative_backends(f_name, n_array_args):
|
||||
func = getattr(_ufuncs, f_name)
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapped(*args, **kwargs):
|
||||
xp = array_namespace(*args[:n_array_args])
|
||||
f = get_array_special_func(f_name, xp, n_array_args)
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
array_special_func_map = {
|
||||
'log_ndtr': 1,
|
||||
'ndtr': 1,
|
||||
'ndtri': 1,
|
||||
'erf': 1,
|
||||
'erfc': 1,
|
||||
'i0': 1,
|
||||
'i0e': 1,
|
||||
'i1': 1,
|
||||
'i1e': 1,
|
||||
'gammaln': 1,
|
||||
'gammainc': 2,
|
||||
'gammaincc': 2,
|
||||
'logit': 1,
|
||||
'expit': 1,
|
||||
'entr': 1,
|
||||
'rel_entr': 2,
|
||||
'xlogy': 2,
|
||||
'chdtrc': 2,
|
||||
}
|
||||
|
||||
for f_name, n_array_args in array_special_func_map.items():
|
||||
f = (support_alternative_backends(f_name, n_array_args) if _SCIPY_ARRAY_API
|
||||
else getattr(_ufuncs, f_name))
|
||||
sys.modules[__name__].__dict__[f_name] = f
|
||||
|
||||
__all__ = list(array_special_func_map)
|
||||
Binary file not shown.
@ -0,0 +1,9 @@
|
||||
import numpy as np
|
||||
|
||||
def have_fenv() -> bool: ...
|
||||
def random_double(size: int) -> np.float64: ...
|
||||
def test_add_round(size: int, mode: str): ...
|
||||
|
||||
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]: ...
|
||||
321
venv/lib/python3.12/site-packages/scipy/special/_testutils.py
Normal file
321
venv/lib/python3.12/site-packages/scipy/special/_testutils.py
Normal file
@ -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) == 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("Max |adiff|: %g" % diff[bad_j].max())
|
||||
msg.append("Max |rdiff|: %g" % rdiff[bad_j].max())
|
||||
msg.append("Bad results (%d out of %d) for the following points "
|
||||
"(in output %d):"
|
||||
% (np.sum(bad_j), point_count, output_num,))
|
||||
for j in np.nonzero(bad_j)[0]:
|
||||
j = int(j)
|
||||
def fmt(x):
|
||||
return '%30s' % np.array2string(x[j], precision=18)
|
||||
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, esp. for Nose output"""
|
||||
if np.any(list(map(np.iscomplexobj, self.param_columns))):
|
||||
is_complex = " (complex)"
|
||||
else:
|
||||
is_complex = ""
|
||||
if self.dataname:
|
||||
return "<Data for {}{}: {}>".format(self.func.__name__, is_complex,
|
||||
os.path.basename(self.dataname))
|
||||
else:
|
||||
return f"<Data for {self.func.__name__}{is_complex}>"
|
||||
Binary file not shown.
525
venv/lib/python3.12/site-packages/scipy/special/_ufuncs.pyi
Normal file
525
venv/lib/python3.12/site-packages/scipy/special/_ufuncs.pyi
Normal file
@ -0,0 +1,525 @@
|
||||
from typing import Any, Dict
|
||||
|
||||
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',
|
||||
'btdtr',
|
||||
'btdtri',
|
||||
'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
|
||||
_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
|
||||
btdtr: np.ufunc
|
||||
btdtri: 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
|
||||
|
||||
17266
venv/lib/python3.12/site-packages/scipy/special/_ufuncs.pyx
Normal file
17266
venv/lib/python3.12/site-packages/scipy/special/_ufuncs.pyx
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
139
venv/lib/python3.12/site-packages/scipy/special/_ufuncs_cxx.pxd
Normal file
139
venv/lib/python3.12/site-packages/scipy/special/_ufuncs_cxx.pxd
Normal file
@ -0,0 +1,139 @@
|
||||
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_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_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_cdf_float
|
||||
cdef void *_export_ncf_cdf_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_ppf_float
|
||||
cdef void *_export_ncf_ppf_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_cdf_float
|
||||
cdef void *_export_nct_cdf_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_ppf_float
|
||||
cdef void *_export_nct_ppf_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_faddeeva_dawsn
|
||||
cdef void *_export_faddeeva_dawsn_complex
|
||||
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_faddeeva_erf
|
||||
cdef void *_export_faddeeva_erfc_complex
|
||||
cdef void *_export_faddeeva_erfcx
|
||||
cdef void *_export_faddeeva_erfcx_complex
|
||||
cdef void *_export_faddeeva_erfi
|
||||
cdef void *_export_faddeeva_erfi_complex
|
||||
cdef void *_export_erfinv_float
|
||||
cdef void *_export_erfinv_double
|
||||
cdef void *_export_hyp1f1_double
|
||||
cdef void *_export_faddeeva_log_ndtr
|
||||
cdef void *_export_faddeeva_log_ndtr_complex
|
||||
cdef void *_export_faddeeva_ndtr
|
||||
cdef void *_export_powm1_float
|
||||
cdef void *_export_powm1_double
|
||||
cdef void *_export_faddeeva_voigt_profile
|
||||
cdef void *_export_faddeeva_w
|
||||
cdef void *_export_wrightomega
|
||||
cdef void *_export_wrightomega_real
|
||||
418
venv/lib/python3.12/site-packages/scipy/special/_ufuncs_cxx.pyx
Normal file
418
venv/lib/python3.12/site-packages/scipy/special/_ufuncs_cxx.pyx
Normal file
@ -0,0 +1,418 @@
|
||||
# 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_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_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_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_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_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_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_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_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_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_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_faddeeva_dawsn "faddeeva_dawsn"(double) noexcept nogil
|
||||
cdef void *_export_faddeeva_dawsn = <void*>_func_faddeeva_dawsn
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_faddeeva_dawsn_complex "faddeeva_dawsn_complex"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_dawsn_complex = <void*>_func_faddeeva_dawsn_complex
|
||||
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 double complex _func_faddeeva_erf "faddeeva_erf"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_erf = <void*>_func_faddeeva_erf
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_faddeeva_erfc_complex "faddeeva_erfc_complex"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_erfc_complex = <void*>_func_faddeeva_erfc_complex
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_faddeeva_erfcx "faddeeva_erfcx"(double) noexcept nogil
|
||||
cdef void *_export_faddeeva_erfcx = <void*>_func_faddeeva_erfcx
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_faddeeva_erfcx_complex "faddeeva_erfcx_complex"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_erfcx_complex = <void*>_func_faddeeva_erfcx_complex
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double _func_faddeeva_erfi "faddeeva_erfi"(double) noexcept nogil
|
||||
cdef void *_export_faddeeva_erfi = <void*>_func_faddeeva_erfi
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_faddeeva_erfi_complex "faddeeva_erfi_complex"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_erfi_complex = <void*>_func_faddeeva_erfi_complex
|
||||
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 double _func_faddeeva_log_ndtr "faddeeva_log_ndtr"(double) noexcept nogil
|
||||
cdef void *_export_faddeeva_log_ndtr = <void*>_func_faddeeva_log_ndtr
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_faddeeva_log_ndtr_complex "faddeeva_log_ndtr_complex"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_log_ndtr_complex = <void*>_func_faddeeva_log_ndtr_complex
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_faddeeva_ndtr "faddeeva_ndtr"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_ndtr = <void*>_func_faddeeva_ndtr
|
||||
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 _func_faddeeva_voigt_profile "faddeeva_voigt_profile"(double, double, double) noexcept nogil
|
||||
cdef void *_export_faddeeva_voigt_profile = <void*>_func_faddeeva_voigt_profile
|
||||
cdef extern from r"_ufuncs_cxx_defs.h":
|
||||
cdef double complex _func_faddeeva_w "faddeeva_w"(double complex) noexcept nogil
|
||||
cdef void *_export_faddeeva_w = <void*>_func_faddeeva_w
|
||||
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,145 @@
|
||||
#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 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 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_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_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_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 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_cdf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nct_cdf_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_ppf_float(npy_float, npy_float, npy_float);
|
||||
npy_double nct_ppf_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 "_faddeeva.h"
|
||||
npy_double faddeeva_dawsn(npy_double);
|
||||
npy_cdouble faddeeva_dawsn_complex(npy_cdouble);
|
||||
#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_cdouble faddeeva_erf(npy_cdouble);
|
||||
npy_cdouble faddeeva_erfc_complex(npy_cdouble);
|
||||
npy_double faddeeva_erfcx(npy_double);
|
||||
npy_cdouble faddeeva_erfcx_complex(npy_cdouble);
|
||||
npy_double faddeeva_erfi(npy_double);
|
||||
npy_cdouble faddeeva_erfi_complex(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_double faddeeva_log_ndtr(npy_double);
|
||||
npy_cdouble faddeeva_log_ndtr_complex(npy_cdouble);
|
||||
npy_cdouble faddeeva_ndtr(npy_cdouble);
|
||||
npy_float powm1_float(npy_float, npy_float);
|
||||
npy_double powm1_double(npy_double, npy_double);
|
||||
npy_double faddeeva_voigt_profile(npy_double, npy_double, npy_double);
|
||||
npy_cdouble faddeeva_w(npy_cdouble);
|
||||
#include "_wright.h"
|
||||
npy_cdouble wrightomega(npy_cdouble);
|
||||
npy_double wrightomega_real(npy_double);
|
||||
#endif
|
||||
103
venv/lib/python3.12/site-packages/scipy/special/_ufuncs_defs.h
Normal file
103
venv/lib/python3.12/site-packages/scipy/special/_ufuncs_defs.h
Normal file
@ -0,0 +1,103 @@
|
||||
#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 "special_wrappers.h"
|
||||
npy_double cephes_igam_fac(npy_double, npy_double);
|
||||
npy_double cephes_kolmogc(npy_double);
|
||||
npy_double cephes_kolmogci(npy_double);
|
||||
npy_double cephes_kolmogp(npy_double);
|
||||
npy_double cephes_lanczos_sum_expg_scaled(npy_double);
|
||||
npy_double cephes_lgam1p(npy_double);
|
||||
npy_double cephes_log1pmx(npy_double);
|
||||
npy_double cephes_riemann_zeta(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 cephes_besselpoly(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_beta(npy_double, npy_double);
|
||||
npy_double cephes_lbeta(npy_double, npy_double);
|
||||
npy_double cephes_btdtr(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_btdtri(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_cbrt(npy_double);
|
||||
npy_double cephes_chdtr(npy_double, npy_double);
|
||||
npy_double cephes_chdtrc(npy_double, npy_double);
|
||||
npy_double cephes_chdtri(npy_double, npy_double);
|
||||
npy_double cephes_cosdg(npy_double);
|
||||
npy_double cephes_cosm1(npy_double);
|
||||
npy_double cephes_cotdg(npy_double);
|
||||
npy_double cephes_ellpe(npy_double);
|
||||
npy_double cephes_ellie(npy_double, npy_double);
|
||||
npy_int cephes_ellpj_wrap(npy_double, npy_double, npy_double *, npy_double *, npy_double *, npy_double *);
|
||||
npy_double special_ellipk(npy_double);
|
||||
npy_double cephes_ellik(npy_double, npy_double);
|
||||
npy_double cephes_ellpk(npy_double);
|
||||
npy_double cephes_erf(npy_double);
|
||||
npy_double cephes_erfc(npy_double);
|
||||
npy_double cephes_erfcinv(npy_double);
|
||||
npy_double cephes_exp10(npy_double);
|
||||
npy_double cephes_exp2(npy_double);
|
||||
npy_double cephes_expm1(npy_double);
|
||||
npy_double cephes_expn_wrap(npy_intp, npy_double);
|
||||
npy_double cephes_fdtr(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_fdtrc(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_fdtri(npy_double, npy_double, npy_double);
|
||||
npy_int cephes_fresnl_wrap(npy_double, npy_double *, npy_double *);
|
||||
npy_int cfresnl_wrap(npy_cdouble, npy_cdouble *, npy_cdouble *);
|
||||
npy_double cephes_igam(npy_double, npy_double);
|
||||
npy_double cephes_igamc(npy_double, npy_double);
|
||||
npy_double cephes_igamci(npy_double, npy_double);
|
||||
npy_double cephes_igami(npy_double, npy_double);
|
||||
npy_double cephes_gammasgn(npy_double);
|
||||
npy_double cephes_gdtr(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_gdtrc(npy_double, npy_double, npy_double);
|
||||
npy_cdouble chyp1f1_wrap(npy_double, npy_double, npy_cdouble);
|
||||
npy_double cephes_i0(npy_double);
|
||||
npy_double cephes_i0e(npy_double);
|
||||
npy_double cephes_i1(npy_double);
|
||||
npy_double cephes_i1e(npy_double);
|
||||
npy_double cephes_j0(npy_double);
|
||||
npy_double cephes_j1(npy_double);
|
||||
npy_double cephes_k0(npy_double);
|
||||
npy_double cephes_k0e(npy_double);
|
||||
npy_double cephes_k1(npy_double);
|
||||
npy_double cephes_k1e(npy_double);
|
||||
npy_double special_cyl_bessel_k_int(npy_intp, npy_double);
|
||||
npy_double cephes_kolmogi(npy_double);
|
||||
npy_double cephes_kolmogorov(npy_double);
|
||||
npy_double cephes_log1p(npy_double);
|
||||
npy_double pmv_wrap(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_struve_l(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 cephes_ndtr(npy_double);
|
||||
npy_double cephes_ndtri(npy_double);
|
||||
npy_double cephes_owens_t(npy_double, npy_double);
|
||||
npy_double cephes_pdtr(npy_double, npy_double);
|
||||
npy_double cephes_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_radian(npy_double, npy_double, npy_double);
|
||||
npy_double cephes_round(npy_double);
|
||||
npy_int cephes_shichi_wrap(npy_double, npy_double *, npy_double *);
|
||||
npy_int cephes_sici_wrap(npy_double, npy_double *, npy_double *);
|
||||
npy_double cephes_sindg(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 cephes_struve_h(npy_double, npy_double);
|
||||
npy_double cephes_tandg(npy_double);
|
||||
npy_double cephes_tukeylambdacdf(npy_double, npy_double);
|
||||
npy_double cephes_y0(npy_double);
|
||||
npy_double cephes_y1(npy_double);
|
||||
npy_double cephes_yn_wrap(npy_intp, npy_double);
|
||||
npy_double cephes_zetac(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)
|
||||
87
venv/lib/python3.12/site-packages/scipy/special/basic.py
Normal file
87
venv/lib/python3.12/site-packages/scipy/special/basic.py
Normal file
@ -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)
|
||||
Binary file not shown.
@ -0,0 +1,261 @@
|
||||
|
||||
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 btdtr(double x0, double x1, double x2) noexcept nogil
|
||||
cpdef double btdtri(double x0, double x1, double x2) 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 double ncfdtr(double x0, double x1, double x2, double x3) noexcept nogil
|
||||
cpdef double ncfdtri(double x0, double x1, double x2, double 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 double nctdtr(double x0, double x1, double 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 double nctdtrit(double x0, double x1, double 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: ...
|
||||
BIN
venv/lib/python3.12/site-packages/scipy/special/libsf_error_state.so
Executable file
BIN
venv/lib/python3.12/site-packages/scipy/special/libsf_error_state.so
Executable file
Binary file not shown.
@ -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)
|
||||
20
venv/lib/python3.12/site-packages/scipy/special/sf_error.py
Normal file
20
venv/lib/python3.12/site-packages/scipy/special/sf_error.py
Normal file
@ -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)
|
||||
24
venv/lib/python3.12/site-packages/scipy/special/specfun.py
Normal file
24
venv/lib/python3.12/site-packages/scipy/special/specfun.py
Normal file
@ -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,89 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2024.
|
||||
*
|
||||
* Original authors: Pauli Virtanen, Eric Moore
|
||||
*/
|
||||
|
||||
// Binomial coefficient
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "cephes/beta.h"
|
||||
#include "cephes/gamma.h"
|
||||
|
||||
namespace special {
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double binom(double n, double k) {
|
||||
double kx, nx, num, den, dk, sgn;
|
||||
|
||||
if (n < 0) {
|
||||
nx = std::floor(n);
|
||||
if (n == nx) {
|
||||
// Undefined
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
kx = std::floor(k);
|
||||
if (k == kx && (std::abs(n) > 1E-8 || n == 0)) {
|
||||
/* Integer case: use multiplication formula for less rounding
|
||||
* error for cases where the result is an integer.
|
||||
*
|
||||
* This cannot be used for small nonzero n due to loss of
|
||||
* precision. */
|
||||
nx = std::floor(n);
|
||||
if (nx == n && kx > nx / 2 && nx > 0) {
|
||||
// Reduce kx by symmetry
|
||||
kx = nx - kx;
|
||||
}
|
||||
|
||||
if (kx >= 0 && kx < 20) {
|
||||
num = 1.0;
|
||||
den = 1.0;
|
||||
for (int i = 1; i < 1 + static_cast<int>(kx); i++) {
|
||||
num *= i + n - kx;
|
||||
den *= i;
|
||||
if (std::abs(num) > 1E50) {
|
||||
num /= den;
|
||||
den = 1.0;
|
||||
}
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
}
|
||||
|
||||
// general case
|
||||
if (n >= 1E10 * k and k > 0) {
|
||||
// avoid under/overflows intermediate results
|
||||
return std::exp(-cephes::lbeta(1 + n - k, 1 + k) - std::log(n + 1));
|
||||
}
|
||||
if (k > 1E8 * std::abs(n)) {
|
||||
// avoid loss of precision
|
||||
num = cephes::Gamma(1 + n) / std::abs(k) + cephes::Gamma(1 + n) * n / (2 * k * k); // + ...
|
||||
num /= M_PI * std::pow(std::abs(k), n);
|
||||
if (k > 0) {
|
||||
kx = std::floor(k);
|
||||
if (static_cast<int>(kx) == kx) {
|
||||
dk = k - kx;
|
||||
sgn = (static_cast<int>(kx) % 2 == 0) ? 1 : -1;
|
||||
} else {
|
||||
dk = k;
|
||||
sgn = 1;
|
||||
}
|
||||
return num * std::sin((dk - n) * M_PI) * sgn;
|
||||
}
|
||||
kx = std::floor(k);
|
||||
if (static_cast<int>(kx) == kx) {
|
||||
return 0;
|
||||
}
|
||||
return num * std::sin(k * M_PI);
|
||||
}
|
||||
return 1 / (n + 1) / cephes::beta(1 + n - k, 1 + k);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline float binom(float n, float k) {
|
||||
return binom(static_cast<double>(n), static_cast<double>(k));
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
@ -0,0 +1,307 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* airy.c
|
||||
*
|
||||
* Airy function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, ai, aip, bi, bip;
|
||||
* int airy();
|
||||
*
|
||||
* airy( x, _&ai, _&aip, _&bi, _&bip );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Solution of the differential equation
|
||||
*
|
||||
* y"(x) = xy.
|
||||
*
|
||||
* The function returns the two independent solutions Ai, Bi
|
||||
* and their first derivatives Ai'(x), Bi'(x).
|
||||
*
|
||||
* Evaluation is by power series summation for small x,
|
||||
* by rational minimax approximations for large x.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
* Error criterion is absolute when function <= 1, relative
|
||||
* when function > 1, except * denotes relative error criterion.
|
||||
* For large negative x, the absolute error increases as x^1.5.
|
||||
* For large positive x, the relative error increases as x^1.5.
|
||||
*
|
||||
* Arithmetic domain function # trials peak rms
|
||||
* IEEE -10, 0 Ai 10000 1.6e-15 2.7e-16
|
||||
* IEEE 0, 10 Ai 10000 2.3e-14* 1.8e-15*
|
||||
* IEEE -10, 0 Ai' 10000 4.6e-15 7.6e-16
|
||||
* IEEE 0, 10 Ai' 10000 1.8e-14* 1.5e-15*
|
||||
* IEEE -10, 10 Bi 30000 4.2e-15 5.3e-16
|
||||
* IEEE -10, 10 Bi' 30000 4.9e-15 7.3e-16
|
||||
*
|
||||
*/
|
||||
/* airy.c */
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1989, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "const.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double airy_c1 = 0.35502805388781723926;
|
||||
constexpr double airy_c2 = 0.258819403792806798405;
|
||||
constexpr double MAXAIRY = 103.892;
|
||||
|
||||
constexpr double airy_AN[8] = {
|
||||
3.46538101525629032477E-1, 1.20075952739645805542E1, 7.62796053615234516538E1, 1.68089224934630576269E2,
|
||||
1.59756391350164413639E2, 7.05360906840444183113E1, 1.40264691163389668864E1, 9.99999999999999995305E-1,
|
||||
};
|
||||
|
||||
constexpr double airy_AD[8] = {
|
||||
5.67594532638770212846E-1, 1.47562562584847203173E1, 8.45138970141474626562E1, 1.77318088145400459522E2,
|
||||
1.64234692871529701831E2, 7.14778400825575695274E1, 1.40959135607834029598E1, 1.00000000000000000470E0,
|
||||
};
|
||||
|
||||
constexpr double airy_APN[8] = {
|
||||
6.13759184814035759225E-1, 1.47454670787755323881E1, 8.20584123476060982430E1, 1.71184781360976385540E2,
|
||||
1.59317847137141783523E2, 6.99778599330103016170E1, 1.39470856980481566958E1, 1.00000000000000000550E0,
|
||||
};
|
||||
|
||||
constexpr double airy_APD[8] = {
|
||||
3.34203677749736953049E-1, 1.11810297306158156705E1, 7.11727352147859965283E1, 1.58778084372838313640E2,
|
||||
1.53206427475809220834E2, 6.86752304592780337944E1, 1.38498634758259442477E1, 9.99999999999999994502E-1,
|
||||
};
|
||||
|
||||
constexpr double airy_BN16[5] = {
|
||||
-2.53240795869364152689E-1, 5.75285167332467384228E-1, -3.29907036873225371650E-1,
|
||||
6.44404068948199951727E-2, -3.82519546641336734394E-3,
|
||||
};
|
||||
|
||||
constexpr double airy_BD16[5] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
-7.15685095054035237902E0, 1.06039580715664694291E1, -5.23246636471251500874E0,
|
||||
9.57395864378383833152E-1, -5.50828147163549611107E-2,
|
||||
};
|
||||
|
||||
constexpr double airy_BPPN[5] = {
|
||||
4.65461162774651610328E-1, -1.08992173800493920734E0, 6.38800117371827987759E-1,
|
||||
-1.26844349553102907034E-1, 7.62487844342109852105E-3,
|
||||
};
|
||||
|
||||
constexpr double airy_BPPD[5] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
-8.70622787633159124240E0, 1.38993162704553213172E1, -7.14116144616431159572E0,
|
||||
1.34008595960680518666E0, -7.84273211323341930448E-2,
|
||||
};
|
||||
|
||||
constexpr double airy_AFN[9] = {
|
||||
-1.31696323418331795333E-1, -6.26456544431912369773E-1, -6.93158036036933542233E-1,
|
||||
-2.79779981545119124951E-1, -4.91900132609500318020E-2, -4.06265923594885404393E-3,
|
||||
-1.59276496239262096340E-4, -2.77649108155232920844E-6, -1.67787698489114633780E-8,
|
||||
};
|
||||
|
||||
constexpr double airy_AFD[9] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
1.33560420706553243746E1, 3.26825032795224613948E1, 2.67367040941499554804E1,
|
||||
9.18707402907259625840E0, 1.47529146771666414581E0, 1.15687173795188044134E-1,
|
||||
4.40291641615211203805E-3, 7.54720348287414296618E-5, 4.51850092970580378464E-7,
|
||||
};
|
||||
|
||||
constexpr double airy_AGN[11] = {
|
||||
1.97339932091685679179E-2, 3.91103029615688277255E-1, 1.06579897599595591108E0, 9.39169229816650230044E-1,
|
||||
3.51465656105547619242E-1, 6.33888919628925490927E-2, 5.85804113048388458567E-3, 2.82851600836737019778E-4,
|
||||
6.98793669997260967291E-6, 8.11789239554389293311E-8, 3.41551784765923618484E-10,
|
||||
};
|
||||
|
||||
constexpr double airy_AGD[10] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
9.30892908077441974853E0, 1.98352928718312140417E1, 1.55646628932864612953E1, 5.47686069422975497931E0,
|
||||
9.54293611618961883998E-1, 8.64580826352392193095E-2, 4.12656523824222607191E-3, 1.01259085116509135510E-4,
|
||||
1.17166733214413521882E-6, 4.91834570062930015649E-9,
|
||||
};
|
||||
|
||||
constexpr double airy_APFN[9] = {
|
||||
1.85365624022535566142E-1, 8.86712188052584095637E-1, 9.87391981747398547272E-1,
|
||||
4.01241082318003734092E-1, 7.10304926289631174579E-2, 5.90618657995661810071E-3,
|
||||
2.33051409401776799569E-4, 4.08718778289035454598E-6, 2.48379932900442457853E-8,
|
||||
};
|
||||
|
||||
constexpr double airy_APFD[9] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
1.47345854687502542552E1, 3.75423933435489594466E1, 3.14657751203046424330E1,
|
||||
1.09969125207298778536E1, 1.78885054766999417817E0, 1.41733275753662636873E-1,
|
||||
5.44066067017226003627E-3, 9.39421290654511171663E-5, 5.65978713036027009243E-7,
|
||||
};
|
||||
|
||||
constexpr double airy_APGN[11] = {
|
||||
-3.55615429033082288335E-2, -6.37311518129435504426E-1, -1.70856738884312371053E0,
|
||||
-1.50221872117316635393E0, -5.63606665822102676611E-1, -1.02101031120216891789E-1,
|
||||
-9.48396695961445269093E-3, -4.60325307486780994357E-4, -1.14300836484517375919E-5,
|
||||
-1.33415518685547420648E-7, -5.63803833958893494476E-10,
|
||||
};
|
||||
|
||||
constexpr double airy_APGD[11] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
9.85865801696130355144E0, 2.16401867356585941885E1, 1.73130776389749389525E1, 6.17872175280828766327E0,
|
||||
1.08848694396321495475E0, 9.95005543440888479402E-2, 4.78468199683886610842E-3, 1.18159633322838625562E-4,
|
||||
1.37480673554219441465E-6, 5.79912514929147598821E-9,
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline int airy(double x, double *ai, double *aip, double *bi, double *bip) {
|
||||
double z, zz, t, f, g, uf, ug, k, zeta, theta;
|
||||
int domflg;
|
||||
|
||||
domflg = 0;
|
||||
if (x > detail::MAXAIRY) {
|
||||
*ai = 0;
|
||||
*aip = 0;
|
||||
*bi = std::numeric_limits<double>::infinity();
|
||||
*bip = std::numeric_limits<double>::infinity();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (x < -2.09) {
|
||||
domflg = 15;
|
||||
t = std::sqrt(-x);
|
||||
zeta = -2.0 * x * t / 3.0;
|
||||
t = std::sqrt(t);
|
||||
k = detail::SQRT1OPI / t;
|
||||
z = 1.0 / zeta;
|
||||
zz = z * z;
|
||||
uf = 1.0 + zz * polevl(zz, detail::airy_AFN, 8) / p1evl(zz, detail::airy_AFD, 9);
|
||||
ug = z * polevl(zz, detail::airy_AGN, 10) / p1evl(zz, detail::airy_AGD, 10);
|
||||
theta = zeta + 0.25 * M_PI;
|
||||
f = std::sin(theta);
|
||||
g = std::cos(theta);
|
||||
*ai = k * (f * uf - g * ug);
|
||||
*bi = k * (g * uf + f * ug);
|
||||
uf = 1.0 + zz * polevl(zz, detail::airy_APFN, 8) / p1evl(zz, detail::airy_APFD, 9);
|
||||
ug = z * polevl(zz, detail::airy_APGN, 10) / p1evl(zz, detail::airy_APGD, 10);
|
||||
k = detail::SQRT1OPI * t;
|
||||
*aip = -k * (g * uf + f * ug);
|
||||
*bip = k * (f * uf - g * ug);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (x >= 2.09) { /* cbrt(9) */
|
||||
domflg = 5;
|
||||
t = std::sqrt(x);
|
||||
zeta = 2.0 * x * t / 3.0;
|
||||
g = std::exp(zeta);
|
||||
t = std::sqrt(t);
|
||||
k = 2.0 * t * g;
|
||||
z = 1.0 / zeta;
|
||||
f = polevl(z, detail::airy_AN, 7) / polevl(z, detail::airy_AD, 7);
|
||||
*ai = detail::SQRT1OPI * f / k;
|
||||
k = -0.5 * detail::SQRT1OPI * t / g;
|
||||
f = polevl(z, detail::airy_APN, 7) / polevl(z, detail::airy_APD, 7);
|
||||
*aip = f * k;
|
||||
|
||||
if (x > 8.3203353) { /* zeta > 16 */
|
||||
f = z * polevl(z, detail::airy_BN16, 4) / p1evl(z, detail::airy_BD16, 5);
|
||||
k = detail::SQRT1OPI * g;
|
||||
*bi = k * (1.0 + f) / t;
|
||||
f = z * polevl(z, detail::airy_BPPN, 4) / p1evl(z, detail::airy_BPPD, 5);
|
||||
*bip = k * t * (1.0 + f);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
f = 1.0;
|
||||
g = x;
|
||||
t = 1.0;
|
||||
uf = 1.0;
|
||||
ug = x;
|
||||
k = 1.0;
|
||||
z = x * x * x;
|
||||
while (t > detail::MACHEP) {
|
||||
uf *= z;
|
||||
k += 1.0;
|
||||
uf /= k;
|
||||
ug *= z;
|
||||
k += 1.0;
|
||||
ug /= k;
|
||||
uf /= k;
|
||||
f += uf;
|
||||
k += 1.0;
|
||||
ug /= k;
|
||||
g += ug;
|
||||
t = std::abs(uf / f);
|
||||
}
|
||||
uf = detail::airy_c1 * f;
|
||||
ug = detail::airy_c2 * g;
|
||||
if ((domflg & 1) == 0) {
|
||||
*ai = uf - ug;
|
||||
}
|
||||
if ((domflg & 2) == 0) {
|
||||
*bi = detail::SQRT3 * (uf + ug);
|
||||
}
|
||||
|
||||
/* the deriviative of ai */
|
||||
k = 4.0;
|
||||
uf = x * x / 2.0;
|
||||
ug = z / 3.0;
|
||||
f = uf;
|
||||
g = 1.0 + ug;
|
||||
uf /= 3.0;
|
||||
t = 1.0;
|
||||
|
||||
while (t > detail::MACHEP) {
|
||||
uf *= z;
|
||||
ug /= k;
|
||||
k += 1.0;
|
||||
ug *= z;
|
||||
uf /= k;
|
||||
f += uf;
|
||||
k += 1.0;
|
||||
ug /= k;
|
||||
uf /= k;
|
||||
g += ug;
|
||||
k += 1.0;
|
||||
t = std::abs(ug / g);
|
||||
}
|
||||
|
||||
uf = detail::airy_c1 * f;
|
||||
ug = detail::airy_c2 * g;
|
||||
if ((domflg & 4) == 0) {
|
||||
*aip = uf - ug;
|
||||
}
|
||||
if ((domflg & 8) == 0) {
|
||||
*bip = detail::SQRT3 * (uf + ug);
|
||||
};
|
||||
return (0);
|
||||
}
|
||||
|
||||
inline int airy(float xf, float *aif, float *aipf, float *bif, float *bipf) {
|
||||
double ai;
|
||||
double aip;
|
||||
double bi;
|
||||
double bip;
|
||||
int res = cephes::airy(xf, &ai, &aip, &bi, &bip);
|
||||
|
||||
*aif = ai;
|
||||
*aipf = aip;
|
||||
*bif = bi;
|
||||
*bipf = bip;
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,51 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
*
|
||||
* This was not part of the original cephes library.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "gamma.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
namespace detail {
|
||||
|
||||
constexpr double besselpoly_EPS = 1.0e-17;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double besselpoly(double a, double lambda, double nu) {
|
||||
|
||||
int m, factor = 0;
|
||||
double Sm, relerr, Sol;
|
||||
double sum = 0.0;
|
||||
|
||||
/* Special handling for a = 0.0 */
|
||||
if (a == 0.0) {
|
||||
if (nu == 0.0) {
|
||||
return 1.0 / (lambda + 1);
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
/* Special handling for negative and integer nu */
|
||||
if ((nu < 0) && (std::floor(nu) == nu)) {
|
||||
nu = -nu;
|
||||
factor = static_cast<int>(nu) % 2;
|
||||
}
|
||||
Sm = std::exp(nu * std::log(a)) / (Gamma(nu + 1) * (lambda + nu + 1));
|
||||
m = 0;
|
||||
do {
|
||||
sum += Sm;
|
||||
Sol = Sm;
|
||||
Sm *= -a * a * (lambda + nu + 1 + 2 * m) / ((nu + m + 1) * (m + 1) * (lambda + nu + 1 + 2 * m + 2));
|
||||
m++;
|
||||
relerr = std::abs((Sm - Sol) / Sm);
|
||||
} while (relerr > detail::besselpoly_EPS && m < 1000);
|
||||
if (!factor)
|
||||
return sum;
|
||||
else
|
||||
return -sum;
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,255 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* beta.c
|
||||
*
|
||||
* Beta function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double a, b, y, beta();
|
||||
*
|
||||
* y = beta( a, b );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* - -
|
||||
* | (a) | (b)
|
||||
* beta( a, b ) = -----------.
|
||||
* -
|
||||
* | (a+b)
|
||||
*
|
||||
* For large arguments the logarithm of the function is
|
||||
* evaluated using lgam(), then exponentiated.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,30 30000 8.1e-14 1.1e-14
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* message condition value returned
|
||||
* beta overflow log(beta) > MAXLOG 0.0
|
||||
* a or b <0 integer 0.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.0: April, 1987
|
||||
* Copyright 1984, 1987 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
SPECFUN_HOST_DEVICE double beta(double, double);
|
||||
SPECFUN_HOST_DEVICE double lbeta(double, double);
|
||||
|
||||
namespace detail {
|
||||
constexpr double beta_ASYMP_FACTOR = 1e6;
|
||||
|
||||
/*
|
||||
* Asymptotic expansion for ln(|B(a, b)|) for a > ASYMP_FACTOR*max(|b|, 1).
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline double lbeta_asymp(double a, double b, int *sgn) {
|
||||
double r = lgam_sgn(b, sgn);
|
||||
r -= b * std::log(a);
|
||||
|
||||
r += b * (1 - b) / (2 * a);
|
||||
r += b * (1 - b) * (1 - 2 * b) / (12 * a * a);
|
||||
r += -b * b * (1 - b) * (1 - b) / (12 * a * a * a);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special case for a negative integer argument
|
||||
*/
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double beta_negint(int a, double b) {
|
||||
int sgn;
|
||||
if (b == static_cast<int>(b) && 1 - a - b > 0) {
|
||||
sgn = (static_cast<int>(b) % 2 == 0) ? 1 : -1;
|
||||
return sgn * special::cephes::beta(1 - a - b, b);
|
||||
} else {
|
||||
set_error("lbeta", SF_ERROR_OVERFLOW, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double lbeta_negint(int a, double b) {
|
||||
double r;
|
||||
if (b == static_cast<int>(b) && 1 - a - b > 0) {
|
||||
r = special::cephes::lbeta(1 - a - b, b);
|
||||
return r;
|
||||
} else {
|
||||
set_error("lbeta", SF_ERROR_OVERFLOW, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double beta(double a, double b) {
|
||||
double y;
|
||||
int sign = 1;
|
||||
|
||||
if (a <= 0.0) {
|
||||
if (a == std::floor(a)) {
|
||||
if (a == static_cast<int>(a)) {
|
||||
return detail::beta_negint(static_cast<int>(a), b);
|
||||
} else {
|
||||
goto overflow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (b <= 0.0) {
|
||||
if (b == std::floor(b)) {
|
||||
if (b == static_cast<int>(b)) {
|
||||
return detail::beta_negint(static_cast<int>(b), a);
|
||||
} else {
|
||||
goto overflow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (std::abs(a) < std::abs(b)) {
|
||||
y = a;
|
||||
a = b;
|
||||
b = y;
|
||||
}
|
||||
|
||||
if (std::abs(a) > detail::beta_ASYMP_FACTOR * std::abs(b) && a > detail::beta_ASYMP_FACTOR) {
|
||||
/* Avoid loss of precision in lgam(a + b) - lgam(a) */
|
||||
y = detail::lbeta_asymp(a, b, &sign);
|
||||
return sign * std::exp(y);
|
||||
}
|
||||
|
||||
y = a + b;
|
||||
if (std::abs(y) > detail::MAXGAM || std::abs(a) > detail::MAXGAM || std::abs(b) > detail::MAXGAM) {
|
||||
int sgngam;
|
||||
y = detail::lgam_sgn(y, &sgngam);
|
||||
sign *= sgngam; /* keep track of the sign */
|
||||
y = detail::lgam_sgn(b, &sgngam) - y;
|
||||
sign *= sgngam;
|
||||
y = detail::lgam_sgn(a, &sgngam) + y;
|
||||
sign *= sgngam;
|
||||
if (y > detail::MAXLOG) {
|
||||
goto overflow;
|
||||
}
|
||||
return (sign * std::exp(y));
|
||||
}
|
||||
|
||||
y = Gamma(y);
|
||||
a = Gamma(a);
|
||||
b = Gamma(b);
|
||||
if (y == 0.0)
|
||||
goto overflow;
|
||||
|
||||
if (std::abs(std::abs(a) - std::abs(y)) > std::abs(std::abs(b) - std::abs(y))) {
|
||||
y = b / y;
|
||||
y *= a;
|
||||
} else {
|
||||
y = a / y;
|
||||
y *= b;
|
||||
}
|
||||
|
||||
return (y);
|
||||
|
||||
overflow:
|
||||
set_error("beta", SF_ERROR_OVERFLOW, NULL);
|
||||
return (sign * std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
/* Natural log of |beta|. */
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double lbeta(double a, double b) {
|
||||
double y;
|
||||
int sign;
|
||||
|
||||
sign = 1;
|
||||
|
||||
if (a <= 0.0) {
|
||||
if (a == std::floor(a)) {
|
||||
if (a == static_cast<int>(a)) {
|
||||
return detail::lbeta_negint(static_cast<int>(a), b);
|
||||
} else {
|
||||
goto over;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (b <= 0.0) {
|
||||
if (b == std::floor(b)) {
|
||||
if (b == static_cast<int>(b)) {
|
||||
return detail::lbeta_negint(static_cast<int>(b), a);
|
||||
} else {
|
||||
goto over;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (std::abs(a) < std::abs(b)) {
|
||||
y = a;
|
||||
a = b;
|
||||
b = y;
|
||||
}
|
||||
|
||||
if (std::abs(a) > detail::beta_ASYMP_FACTOR * std::abs(b) && a > detail::beta_ASYMP_FACTOR) {
|
||||
/* Avoid loss of precision in lgam(a + b) - lgam(a) */
|
||||
y = detail::lbeta_asymp(a, b, &sign);
|
||||
return y;
|
||||
}
|
||||
|
||||
y = a + b;
|
||||
if (std::abs(y) > detail::MAXGAM || std::abs(a) > detail::MAXGAM || std::abs(b) > detail::MAXGAM) {
|
||||
int sgngam;
|
||||
y = detail::lgam_sgn(y, &sgngam);
|
||||
sign *= sgngam; /* keep track of the sign */
|
||||
y = detail::lgam_sgn(b, &sgngam) - y;
|
||||
sign *= sgngam;
|
||||
y = detail::lgam_sgn(a, &sgngam) + y;
|
||||
sign *= sgngam;
|
||||
return (y);
|
||||
}
|
||||
|
||||
y = Gamma(y);
|
||||
a = Gamma(a);
|
||||
b = Gamma(b);
|
||||
if (y == 0.0) {
|
||||
over:
|
||||
set_error("lbeta", SF_ERROR_OVERFLOW, NULL);
|
||||
return (sign * std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
if (std::abs(std::abs(a) - std::abs(y)) > std::abs(std::abs(b) - std::abs(y))) {
|
||||
y = b / y;
|
||||
y *= a;
|
||||
} else {
|
||||
y = a / y;
|
||||
y *= b;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
y = -y;
|
||||
}
|
||||
|
||||
return (std::log(y));
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,131 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* cbrt.c
|
||||
*
|
||||
* Cube root
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, cbrt();
|
||||
*
|
||||
* y = cbrt( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns the cube root of the argument, which may be negative.
|
||||
*
|
||||
* Range reduction involves determining the power of 2 of
|
||||
* the argument. A polynomial of degree 2 applied to the
|
||||
* mantissa, and multiplication by the cube root of 1, 2, or 4
|
||||
* approximates the root to within about 0.1%. Then Newton's
|
||||
* iteration is used three times to converge to an accurate
|
||||
* result.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,1e308 30000 1.5e-16 5.0e-17
|
||||
*
|
||||
*/
|
||||
/* cbrt.c */
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.2: January, 1991
|
||||
* Copyright 1984, 1991 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double CBRT2 = 1.2599210498948731647672;
|
||||
constexpr double CBRT4 = 1.5874010519681994747517;
|
||||
constexpr double CBRT2I = 0.79370052598409973737585;
|
||||
constexpr double CBRT4I = 0.62996052494743658238361;
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double cbrt(double x) {
|
||||
int e, rem, sign;
|
||||
double z;
|
||||
|
||||
if (!std::isfinite(x)) {
|
||||
return x;
|
||||
}
|
||||
if (x == 0) {
|
||||
return (x);
|
||||
}
|
||||
if (x > 0) {
|
||||
sign = 1;
|
||||
} else {
|
||||
sign = -1;
|
||||
x = -x;
|
||||
}
|
||||
|
||||
z = x;
|
||||
/* extract power of 2, leaving
|
||||
* mantissa between 0.5 and 1
|
||||
*/
|
||||
x = std::frexp(x, &e);
|
||||
|
||||
/* Approximate cube root of number between .5 and 1,
|
||||
* peak relative error = 9.2e-6
|
||||
*/
|
||||
x = (((-1.3466110473359520655053e-1 * x + 5.4664601366395524503440e-1) * x - 9.5438224771509446525043e-1) *
|
||||
x +
|
||||
1.1399983354717293273738e0) *
|
||||
x +
|
||||
4.0238979564544752126924e-1;
|
||||
|
||||
/* exponent divided by 3 */
|
||||
if (e >= 0) {
|
||||
rem = e;
|
||||
e /= 3;
|
||||
rem -= 3 * e;
|
||||
if (rem == 1) {
|
||||
x *= CBRT2;
|
||||
} else if (rem == 2) {
|
||||
x *= CBRT4;
|
||||
}
|
||||
}
|
||||
/* argument less than 1 */
|
||||
else {
|
||||
e = -e;
|
||||
rem = e;
|
||||
e /= 3;
|
||||
rem -= 3 * e;
|
||||
if (rem == 1) {
|
||||
x *= CBRT2I;
|
||||
} else if (rem == 2) {
|
||||
x *= CBRT4I;
|
||||
}
|
||||
e = -e;
|
||||
}
|
||||
|
||||
/* multiply by power of 2 */
|
||||
x = std::ldexp(x, e);
|
||||
|
||||
/* Newton iteration */
|
||||
x -= (x - (z / (x * x))) * 0.33333333333333333333;
|
||||
x -= (x - (z / (x * x))) * 0.33333333333333333333;
|
||||
|
||||
if (sign < 0)
|
||||
x = -x;
|
||||
return (x);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,85 @@
|
||||
/* chbevl.c
|
||||
*
|
||||
* Evaluate Chebyshev series
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* int N;
|
||||
* double x, y, coef[N], chebevl();
|
||||
*
|
||||
* y = chbevl( x, coef, N );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Evaluates the series
|
||||
*
|
||||
* N-1
|
||||
* - '
|
||||
* y = > coef[i] T (x/2)
|
||||
* - i
|
||||
* i=0
|
||||
*
|
||||
* of Chebyshev polynomials Ti at argument x/2.
|
||||
*
|
||||
* Coefficients are stored in reverse order, i.e. the zero
|
||||
* order term is last in the array. Note N is the number of
|
||||
* coefficients, not the order.
|
||||
*
|
||||
* If coefficients are for the interval a to b, x must
|
||||
* have been transformed to x -> 2(2x - b - a)/(b-a) before
|
||||
* entering the routine. This maps x from (a, b) to (-1, 1),
|
||||
* over which the Chebyshev polynomials are defined.
|
||||
*
|
||||
* If the coefficients are for the inverted interval, in
|
||||
* which (a, b) is mapped to (1/b, 1/a), the transformation
|
||||
* required is x -> 2(2ab/x - b - a)/(b-a). If b is infinity,
|
||||
* this becomes x -> 4a/x - 1.
|
||||
*
|
||||
*
|
||||
*
|
||||
* SPEED:
|
||||
*
|
||||
* Taking advantage of the recurrence properties of the
|
||||
* Chebyshev polynomials, the routine requires one more
|
||||
* addition per loop than evaluating a nested polynomial of
|
||||
* the same degree.
|
||||
*
|
||||
*/
|
||||
/* chbevl.c */
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.0: April, 1987
|
||||
* Copyright 1985, 1987 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
SPECFUN_HOST_DEVICE double chbevl(double x, const double array[], int n) {
|
||||
double b0, b1, b2;
|
||||
const double *p;
|
||||
int i;
|
||||
|
||||
p = array;
|
||||
b0 = *p++;
|
||||
b1 = 0.0;
|
||||
i = n - 1;
|
||||
|
||||
do {
|
||||
b2 = b1;
|
||||
b1 = b0;
|
||||
b0 = x * b1 - b2 + *p++;
|
||||
} while (--i);
|
||||
|
||||
return (0.5 * (b0 - b2));
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,193 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* chdtr.c
|
||||
*
|
||||
* Chi-square distribution
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double df, x, y, chdtr();
|
||||
*
|
||||
* y = chdtr( df, x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns the area under the left hand tail (from 0 to x)
|
||||
* of the Chi square probability density function with
|
||||
* v degrees of freedom.
|
||||
*
|
||||
*
|
||||
* inf.
|
||||
* -
|
||||
* 1 | | v/2-1 -t/2
|
||||
* P( x | v ) = ----------- | t e dt
|
||||
* v/2 - | |
|
||||
* 2 | (v/2) -
|
||||
* x
|
||||
*
|
||||
* where x is the Chi-square variable.
|
||||
*
|
||||
* The incomplete Gamma integral is used, according to the
|
||||
* formula
|
||||
*
|
||||
* y = chdtr( v, x ) = igam( v/2.0, x/2.0 ).
|
||||
*
|
||||
*
|
||||
* The arguments must both be positive.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* See igam().
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* message condition value returned
|
||||
* chdtr domain x < 0 or v < 1 0.0
|
||||
*/
|
||||
/* chdtrc()
|
||||
*
|
||||
* Complemented Chi-square distribution
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double v, x, y, chdtrc();
|
||||
*
|
||||
* y = chdtrc( v, x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns the area under the right hand tail (from x to
|
||||
* infinity) of the Chi square probability density function
|
||||
* with v degrees of freedom:
|
||||
*
|
||||
*
|
||||
* inf.
|
||||
* -
|
||||
* 1 | | v/2-1 -t/2
|
||||
* P( x | v ) = ----------- | t e dt
|
||||
* v/2 - | |
|
||||
* 2 | (v/2) -
|
||||
* x
|
||||
*
|
||||
* where x is the Chi-square variable.
|
||||
*
|
||||
* The incomplete Gamma integral is used, according to the
|
||||
* formula
|
||||
*
|
||||
* y = chdtr( v, x ) = igamc( v/2.0, x/2.0 ).
|
||||
*
|
||||
*
|
||||
* The arguments must both be positive.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* See igamc().
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* message condition value returned
|
||||
* chdtrc domain x < 0 or v < 1 0.0
|
||||
*/
|
||||
/* chdtri()
|
||||
*
|
||||
* Inverse of complemented Chi-square distribution
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double df, x, y, chdtri();
|
||||
*
|
||||
* x = chdtri( df, y );
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Finds the Chi-square argument x such that the integral
|
||||
* from x to infinity of the Chi-square density is equal
|
||||
* to the given cumulative probability y.
|
||||
*
|
||||
* This is accomplished using the inverse Gamma integral
|
||||
* function and the relation
|
||||
*
|
||||
* x/2 = igamci( df/2, y );
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* See igami.c.
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* message condition value returned
|
||||
* chdtri domain y < 0 or y > 1 0.0
|
||||
* v < 1
|
||||
*
|
||||
*/
|
||||
|
||||
/* chdtr() */
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.0: April, 1987
|
||||
* Copyright 1984, 1987 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "igam.h"
|
||||
#include "igami.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double chdtrc(double df, double x) {
|
||||
|
||||
if (x < 0.0)
|
||||
return 1.0; /* modified by T. Oliphant */
|
||||
return (igamc(df / 2.0, x / 2.0));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double chdtr(double df, double x) {
|
||||
|
||||
if ((x < 0.0)) { /* || (df < 1.0) ) */
|
||||
set_error("chdtr", SF_ERROR_DOMAIN, NULL);
|
||||
return (std::numeric_limits<double>::quiet_NaN());
|
||||
}
|
||||
return (igam(df / 2.0, x / 2.0));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE double chdtri(double df, double y) {
|
||||
double x;
|
||||
|
||||
if ((y < 0.0) || (y > 1.0)) { /* || (df < 1.0) ) */
|
||||
set_error("chdtri", SF_ERROR_DOMAIN, NULL);
|
||||
return (std::numeric_limits<double>::quiet_NaN());
|
||||
}
|
||||
|
||||
x = igamci(0.5 * df, y);
|
||||
return (2.0 * x);
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,87 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*
|
||||
* Since we support only IEEE-754 floating point numbers, conditional logic
|
||||
* supporting other arithmetic types has been removed.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* const.c
|
||||
*
|
||||
* Globally declared constants
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* extern double nameofconstant;
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* This file contains a number of mathematical constants and
|
||||
* also some needed size parameters of the computer arithmetic.
|
||||
* The values are supplied as arrays of hexadecimal integers
|
||||
* for IEEE arithmetic, and in a normal decimal scientific notation for
|
||||
* other machines. The particular notation used is determined
|
||||
* by a symbol (IBMPC, or UNK) defined in the include file
|
||||
* mconf.h.
|
||||
*
|
||||
* The default size parameters are as follows.
|
||||
*
|
||||
* For UNK mode:
|
||||
* MACHEP = 1.38777878078144567553E-17 2**-56
|
||||
* MAXLOG = 8.8029691931113054295988E1 log(2**127)
|
||||
* MINLOG = -8.872283911167299960540E1 log(2**-128)
|
||||
*
|
||||
* For IEEE arithmetic (IBMPC):
|
||||
* MACHEP = 1.11022302462515654042E-16 2**-53
|
||||
* MAXLOG = 7.09782712893383996843E2 log(2**1024)
|
||||
* MINLOG = -7.08396418532264106224E2 log(2**-1022)
|
||||
*
|
||||
* The global symbols for mathematical constants are
|
||||
* SQ2OPI = 7.9788456080286535587989E-1 sqrt( 2/pi )
|
||||
* LOGSQ2 = 3.46573590279972654709E-1 log(2)/2
|
||||
* THPIO4 = 2.35619449019234492885 3*pi/4
|
||||
*
|
||||
* These lists are subject to change.
|
||||
*/
|
||||
/* const.c */
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.3: March, 1995
|
||||
* Copyright 1984, 1995 by Stephen L. Moshier
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
namespace detail {
|
||||
constexpr std::uint64_t MAXITER = 500;
|
||||
constexpr double MACHEP = 1.11022302462515654042E-16; // 2**-53
|
||||
constexpr double MAXLOG = 7.09782712893383996732E2; // log(DBL_MAX)
|
||||
constexpr double MINLOG = -7.451332191019412076235E2; // log 2**-1022
|
||||
constexpr double SQRT1OPI = 5.64189583547756286948E-1; // sqrt( 1/pi)
|
||||
constexpr double SQRT2OPI = 7.9788456080286535587989E-1; // sqrt( 2/pi )
|
||||
constexpr double SQRT2PI = 0.79788456080286535587989; // sqrt(2pi)
|
||||
constexpr double LOGSQ2 = 3.46573590279972654709E-1; // log(2)/2
|
||||
constexpr double THPIO4 = 2.35619449019234492885; // 3*pi/4
|
||||
constexpr double SQRT3 = 1.732050807568877293527; // sqrt(3)
|
||||
constexpr double PI180 = 1.74532925199432957692E-2; // pi/180
|
||||
constexpr double SQRTPI = 2.50662827463100050242E0; // sqrt(pi)
|
||||
constexpr double LOGPI = 1.14472988584940017414; // log(pi)
|
||||
constexpr double MAXGAM = 171.624376956302725;
|
||||
constexpr double LOGSQRT2PI = 0.9189385332046727; // log(sqrt(pi))
|
||||
|
||||
// Following two added by SciPy developers.
|
||||
// Euler's constant
|
||||
constexpr double SCIPY_EULER = 0.577215664901532860606512090082402431;
|
||||
// e as long double
|
||||
constexpr long double SCIPY_El = 2.718281828459045235360287471352662498L;
|
||||
} // namespace detail
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,260 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* expn.c
|
||||
*
|
||||
* Exponential integral En
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* int n;
|
||||
* double x, y, expn();
|
||||
*
|
||||
* y = expn( n, x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Evaluates the exponential integral
|
||||
*
|
||||
* inf.
|
||||
* -
|
||||
* | | -xt
|
||||
* | e
|
||||
* E (x) = | ---- dt.
|
||||
* n | n
|
||||
* | | t
|
||||
* -
|
||||
* 1
|
||||
*
|
||||
*
|
||||
* Both n and x must be nonnegative.
|
||||
*
|
||||
* The routine employs either a power series, a continued
|
||||
* fraction, or an asymptotic formula depending on the
|
||||
* relative values of n and x.
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 10000 1.7e-15 3.6e-16
|
||||
*
|
||||
*/
|
||||
|
||||
/* expn.c */
|
||||
|
||||
/* Cephes Math Library Release 1.1: March, 1985
|
||||
* Copyright 1985 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140 */
|
||||
|
||||
/* Sources
|
||||
* [1] NIST, "The Digital Library of Mathematical Functions", dlmf.nist.gov
|
||||
*/
|
||||
|
||||
/* Scipy changes:
|
||||
* - 09-10-2016: improved asymptotic expansion for large n
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr int expn_nA = 13;
|
||||
constexpr double expn_A0[] = {1.00000000000000000};
|
||||
constexpr double expn_A1[] = {1.00000000000000000};
|
||||
constexpr double expn_A2[] = {-2.00000000000000000, 1.00000000000000000};
|
||||
constexpr double expn_A3[] = {6.00000000000000000, -8.00000000000000000, 1.00000000000000000};
|
||||
constexpr double expn_A4[] = {-24.0000000000000000, 58.0000000000000000, -22.0000000000000000,
|
||||
1.00000000000000000};
|
||||
constexpr double expn_A5[] = {120.000000000000000, -444.000000000000000, 328.000000000000000,
|
||||
-52.0000000000000000, 1.00000000000000000};
|
||||
constexpr double expn_A6[] = {-720.000000000000000, 3708.00000000000000, -4400.00000000000000,
|
||||
1452.00000000000000, -114.000000000000000, 1.00000000000000000};
|
||||
constexpr double expn_A7[] = {5040.00000000000000, -33984.0000000000000, 58140.0000000000000,
|
||||
-32120.0000000000000, 5610.00000000000000, -240.000000000000000,
|
||||
1.00000000000000000};
|
||||
constexpr double expn_A8[] = {-40320.0000000000000, 341136.000000000000, -785304.000000000000,
|
||||
644020.000000000000, -195800.000000000000, 19950.0000000000000,
|
||||
-494.000000000000000, 1.00000000000000000};
|
||||
constexpr double expn_A9[] = {362880.000000000000, -3733920.00000000000, 11026296.0000000000,
|
||||
-12440064.0000000000, 5765500.00000000000, -1062500.00000000000,
|
||||
67260.0000000000000, -1004.00000000000000, 1.00000000000000000};
|
||||
constexpr double expn_A10[] = {-3628800.00000000000, 44339040.0000000000, -162186912.000000000,
|
||||
238904904.000000000, -155357384.000000000, 44765000.0000000000,
|
||||
-5326160.00000000000, 218848.000000000000, -2026.00000000000000,
|
||||
1.00000000000000000};
|
||||
constexpr double expn_A11[] = {39916800.0000000000, -568356480.000000000, 2507481216.00000000,
|
||||
-4642163952.00000000, 4002695088.00000000, -1648384304.00000000,
|
||||
314369720.000000000, -25243904.0000000000, 695038.000000000000,
|
||||
-4072.00000000000000, 1.00000000000000000};
|
||||
constexpr double expn_A12[] = {-479001600.000000000, 7827719040.00000000, -40788301824.0000000,
|
||||
92199790224.0000000, -101180433024.000000, 56041398784.0000000,
|
||||
-15548960784.0000000, 2051482776.00000000, -114876376.000000000,
|
||||
2170626.00000000000, -8166.00000000000000, 1.00000000000000000};
|
||||
constexpr const double *expn_A[] = {expn_A0, expn_A1, expn_A2, expn_A3, expn_A4, expn_A5, expn_A6,
|
||||
expn_A7, expn_A8, expn_A9, expn_A10, expn_A11, expn_A12};
|
||||
constexpr int expn_Adegs[] = {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
|
||||
/* Asymptotic expansion for large n, DLMF 8.20(ii) */
|
||||
SPECFUN_HOST_DEVICE double expn_large_n(int n, double x) {
|
||||
int k;
|
||||
double p = n;
|
||||
double lambda = x / p;
|
||||
double multiplier = 1 / p / (lambda + 1) / (lambda + 1);
|
||||
double fac = 1;
|
||||
double res = 1; /* A[0] = 1 */
|
||||
double expfac, term;
|
||||
|
||||
expfac = std::exp(-lambda * p) / (lambda + 1) / p;
|
||||
if (expfac == 0) {
|
||||
set_error("expn", SF_ERROR_UNDERFLOW, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do the k = 1 term outside the loop since A[1] = 1 */
|
||||
fac *= multiplier;
|
||||
res += fac;
|
||||
|
||||
for (k = 2; k < expn_nA; k++) {
|
||||
fac *= multiplier;
|
||||
term = fac * polevl(lambda, expn_A[k], expn_Adegs[k]);
|
||||
res += term;
|
||||
if (std::abs(term) < MACHEP * std::abs(res)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return expfac * res;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE double expn(int n, double x) {
|
||||
double ans, r, t, yk, xk;
|
||||
double pk, pkm1, pkm2, qk, qkm1, qkm2;
|
||||
double psi, z;
|
||||
int i, k;
|
||||
constexpr double big = 1.44115188075855872E+17;
|
||||
|
||||
if (std::isnan(x)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
} else if (n < 0 || x < 0) {
|
||||
set_error("expn", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (x > detail::MAXLOG) {
|
||||
return (0.0);
|
||||
}
|
||||
|
||||
if (x == 0.0) {
|
||||
if (n < 2) {
|
||||
set_error("expn", SF_ERROR_SINGULAR, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else {
|
||||
return (1.0 / (n - 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
return (std::exp(-x) / x);
|
||||
}
|
||||
|
||||
/* Asymptotic expansion for large n, DLMF 8.20(ii) */
|
||||
if (n > 50) {
|
||||
ans = detail::expn_large_n(n, x);
|
||||
return ans;
|
||||
}
|
||||
|
||||
/* Continued fraction, DLMF 8.19.17 */
|
||||
if (x > 1.0) {
|
||||
k = 1;
|
||||
pkm2 = 1.0;
|
||||
qkm2 = x;
|
||||
pkm1 = 1.0;
|
||||
qkm1 = x + n;
|
||||
ans = pkm1 / qkm1;
|
||||
|
||||
do {
|
||||
k += 1;
|
||||
if (k & 1) {
|
||||
yk = 1.0;
|
||||
xk = n + (k - 1) / 2;
|
||||
} else {
|
||||
yk = x;
|
||||
xk = k / 2;
|
||||
}
|
||||
pk = pkm1 * yk + pkm2 * xk;
|
||||
qk = qkm1 * yk + qkm2 * xk;
|
||||
if (qk != 0) {
|
||||
r = pk / qk;
|
||||
t = std::abs((ans - r) / r);
|
||||
ans = r;
|
||||
} else {
|
||||
t = 1.0;
|
||||
}
|
||||
pkm2 = pkm1;
|
||||
pkm1 = pk;
|
||||
qkm2 = qkm1;
|
||||
qkm1 = qk;
|
||||
if (std::abs(pk) > big) {
|
||||
pkm2 /= big;
|
||||
pkm1 /= big;
|
||||
qkm2 /= big;
|
||||
qkm1 /= big;
|
||||
}
|
||||
} while (t > detail::MACHEP);
|
||||
|
||||
ans *= std::exp(-x);
|
||||
return ans;
|
||||
}
|
||||
|
||||
/* Power series expansion, DLMF 8.19.8 */
|
||||
psi = -detail::SCIPY_EULER - std::log(x);
|
||||
for (i = 1; i < n; i++) {
|
||||
psi = psi + 1.0 / i;
|
||||
}
|
||||
|
||||
z = -x;
|
||||
xk = 0.0;
|
||||
yk = 1.0;
|
||||
pk = 1.0 - n;
|
||||
if (n == 1) {
|
||||
ans = 0.0;
|
||||
} else {
|
||||
ans = 1.0 / pk;
|
||||
}
|
||||
do {
|
||||
xk += 1.0;
|
||||
yk *= z / xk;
|
||||
pk += 1.0;
|
||||
if (pk != 0.0) {
|
||||
ans += yk / pk;
|
||||
}
|
||||
if (ans != 0.0)
|
||||
t = std::abs(yk / ans);
|
||||
else
|
||||
t = 1.0;
|
||||
} while (t > detail::MACHEP);
|
||||
k = xk;
|
||||
t = n;
|
||||
r = n - 1;
|
||||
ans = (std::pow(z, r) * psi / Gamma(t)) - ans;
|
||||
return ans;
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,376 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Gamma function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, Gamma();
|
||||
*
|
||||
* y = Gamma( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns Gamma function of the argument. The result is
|
||||
* correctly signed.
|
||||
*
|
||||
* Arguments |x| <= 34 are reduced by recurrence and the function
|
||||
* approximated by a rational function of degree 6/7 in the
|
||||
* interval (2,3). Large arguments are handled by Stirling's
|
||||
* formula. Large negative arguments are made positive using
|
||||
* a reflection formula.
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE -170,-33 20000 2.3e-15 3.3e-16
|
||||
* IEEE -33, 33 20000 9.4e-16 2.2e-16
|
||||
* IEEE 33, 171.6 20000 2.3e-15 3.2e-16
|
||||
*
|
||||
* Error for arguments outside the test range will be larger
|
||||
* owing to error amplification by the exponential function.
|
||||
*
|
||||
*/
|
||||
|
||||
/* lgam()
|
||||
*
|
||||
* Natural logarithm of Gamma function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, lgam();
|
||||
*
|
||||
* y = lgam( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns the base e (2.718...) logarithm of the absolute
|
||||
* value of the Gamma function of the argument.
|
||||
*
|
||||
* For arguments greater than 13, the logarithm of the Gamma
|
||||
* function is approximated by the logarithmic version of
|
||||
* Stirling's formula using a polynomial approximation of
|
||||
* degree 4. Arguments between -33 and +33 are reduced by
|
||||
* recurrence to the interval [2,3] of a rational approximation.
|
||||
* The cosecant reflection formula is employed for arguments
|
||||
* less than -33.
|
||||
*
|
||||
* Arguments greater than MAXLGM return INFINITY and an error
|
||||
* message. MAXLGM = 2.556348e305 for IEEE arithmetic.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
*
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 3 28000 5.4e-16 1.1e-16
|
||||
* IEEE 2.718, 2.556e305 40000 3.5e-16 8.3e-17
|
||||
* The error criterion was relative when the function magnitude
|
||||
* was greater than one but absolute when it was less than one.
|
||||
*
|
||||
* The following test used the relative error criterion, though
|
||||
* at certain points the relative error could be much higher than
|
||||
* indicated.
|
||||
* IEEE -200, -4 10000 4.8e-16 1.3e-16
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.2: July, 1992
|
||||
* Copyright 1984, 1987, 1989, 1992 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
#include "const.h"
|
||||
#include "polevl.h"
|
||||
#include "trig.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
namespace detail {
|
||||
constexpr double gamma_P[] = {1.60119522476751861407E-4, 1.19135147006586384913E-3, 1.04213797561761569935E-2,
|
||||
4.76367800457137231464E-2, 2.07448227648435975150E-1, 4.94214826801497100753E-1,
|
||||
9.99999999999999996796E-1};
|
||||
|
||||
constexpr double gamma_Q[] = {-2.31581873324120129819E-5, 5.39605580493303397842E-4, -4.45641913851797240494E-3,
|
||||
1.18139785222060435552E-2, 3.58236398605498653373E-2, -2.34591795718243348568E-1,
|
||||
7.14304917030273074085E-2, 1.00000000000000000320E0};
|
||||
|
||||
/* Stirling's formula for the Gamma function */
|
||||
constexpr double gamma_STIR[5] = {
|
||||
7.87311395793093628397E-4, -2.29549961613378126380E-4, -2.68132617805781232825E-3,
|
||||
3.47222221605458667310E-3, 8.33333333333482257126E-2,
|
||||
};
|
||||
|
||||
constexpr double MAXSTIR = 143.01608;
|
||||
|
||||
/* Gamma function computed by Stirling's formula.
|
||||
* The polynomial STIR is valid for 33 <= x <= 172.
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline double stirf(double x) {
|
||||
double y, w, v;
|
||||
|
||||
if (x >= MAXGAM) {
|
||||
return (std::numeric_limits<double>::infinity());
|
||||
}
|
||||
w = 1.0 / x;
|
||||
w = 1.0 + w * special::cephes::polevl(w, gamma_STIR, 4);
|
||||
y = std::exp(x);
|
||||
if (x > MAXSTIR) { /* Avoid overflow in pow() */
|
||||
v = std::pow(x, 0.5 * x - 0.25);
|
||||
y = v * (v / y);
|
||||
} else {
|
||||
y = std::pow(x, x - 0.5) / y;
|
||||
}
|
||||
y = SQRTPI * y * w;
|
||||
return (y);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double Gamma(double x) {
|
||||
double p, q, z;
|
||||
int i;
|
||||
int sgngam = 1;
|
||||
|
||||
if (!std::isfinite(x)) {
|
||||
return x;
|
||||
}
|
||||
q = std::abs(x);
|
||||
|
||||
if (q > 33.0) {
|
||||
if (x < 0.0) {
|
||||
p = std::floor(q);
|
||||
if (p == q) {
|
||||
gamnan:
|
||||
set_error("Gamma", SF_ERROR_OVERFLOW, NULL);
|
||||
return (std::numeric_limits<double>::infinity());
|
||||
}
|
||||
i = p;
|
||||
if ((i & 1) == 0) {
|
||||
sgngam = -1;
|
||||
}
|
||||
z = q - p;
|
||||
if (z > 0.5) {
|
||||
p += 1.0;
|
||||
z = q - p;
|
||||
}
|
||||
z = q * sinpi(z);
|
||||
if (z == 0.0) {
|
||||
return (sgngam * std::numeric_limits<double>::infinity());
|
||||
}
|
||||
z = std::abs(z);
|
||||
z = M_PI / (z * detail::stirf(q));
|
||||
} else {
|
||||
z = detail::stirf(x);
|
||||
}
|
||||
return (sgngam * z);
|
||||
}
|
||||
|
||||
z = 1.0;
|
||||
while (x >= 3.0) {
|
||||
x -= 1.0;
|
||||
z *= x;
|
||||
}
|
||||
|
||||
while (x < 0.0) {
|
||||
if (x > -1.E-9) {
|
||||
goto small;
|
||||
}
|
||||
z /= x;
|
||||
x += 1.0;
|
||||
}
|
||||
|
||||
while (x < 2.0) {
|
||||
if (x < 1.e-9) {
|
||||
goto small;
|
||||
}
|
||||
z /= x;
|
||||
x += 1.0;
|
||||
}
|
||||
|
||||
if (x == 2.0) {
|
||||
return (z);
|
||||
}
|
||||
|
||||
x -= 2.0;
|
||||
p = polevl(x, detail::gamma_P, 6);
|
||||
q = polevl(x, detail::gamma_Q, 7);
|
||||
return (z * p / q);
|
||||
|
||||
small:
|
||||
if (x == 0.0) {
|
||||
goto gamnan;
|
||||
} else
|
||||
return (z / ((1.0 + 0.5772156649015329 * x) * x));
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
/* A[]: Stirling's formula expansion of log Gamma
|
||||
* B[], C[]: log Gamma function between 2 and 3
|
||||
*/
|
||||
constexpr double gamma_A[] = {8.11614167470508450300E-4, -5.95061904284301438324E-4, 7.93650340457716943945E-4,
|
||||
-2.77777777730099687205E-3, 8.33333333333331927722E-2};
|
||||
|
||||
constexpr double gamma_B[] = {-1.37825152569120859100E3, -3.88016315134637840924E4, -3.31612992738871184744E5,
|
||||
-1.16237097492762307383E6, -1.72173700820839662146E6, -8.53555664245765465627E5};
|
||||
|
||||
constexpr double gamma_C[] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
-3.51815701436523470549E2, -1.70642106651881159223E4, -2.20528590553854454839E5,
|
||||
-1.13933444367982507207E6, -2.53252307177582951285E6, -2.01889141433532773231E6};
|
||||
|
||||
/* log( sqrt( 2*pi ) ) */
|
||||
constexpr double LS2PI = 0.91893853320467274178;
|
||||
|
||||
constexpr double MAXLGM = 2.556348e305;
|
||||
|
||||
/* Disable optimizations for this function on 32 bit systems when compiling with GCC.
|
||||
* We've found that enabling optimizations can result in degraded precision
|
||||
* for this asymptotic approximation in that case. */
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("00")
|
||||
#endif
|
||||
SPECFUN_HOST_DEVICE inline double lgam_large_x(double x) {
|
||||
double q = (x - 0.5) * std::log(x) - x + LS2PI;
|
||||
if (x > 1.0e8) {
|
||||
return (q);
|
||||
}
|
||||
double p = 1.0 / (x * x);
|
||||
p = ((7.9365079365079365079365e-4 * p - 2.7777777777777777777778e-3) * p + 0.0833333333333333333333) / x;
|
||||
return q + p;
|
||||
}
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double lgam_sgn(double x, int *sign) {
|
||||
double p, q, u, w, z;
|
||||
int i;
|
||||
|
||||
*sign = 1;
|
||||
|
||||
if (!std::isfinite(x)) {
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < -34.0) {
|
||||
q = -x;
|
||||
w = lgam_sgn(q, sign);
|
||||
p = std::floor(q);
|
||||
if (p == q) {
|
||||
lgsing:
|
||||
set_error("lgam", SF_ERROR_SINGULAR, NULL);
|
||||
return (std::numeric_limits<double>::infinity());
|
||||
}
|
||||
i = p;
|
||||
if ((i & 1) == 0) {
|
||||
*sign = -1;
|
||||
} else {
|
||||
*sign = 1;
|
||||
}
|
||||
z = q - p;
|
||||
if (z > 0.5) {
|
||||
p += 1.0;
|
||||
z = p - q;
|
||||
}
|
||||
z = q * sinpi(z);
|
||||
if (z == 0.0) {
|
||||
goto lgsing;
|
||||
}
|
||||
/* z = log(M_PI) - log( z ) - w; */
|
||||
z = LOGPI - std::log(z) - w;
|
||||
return (z);
|
||||
}
|
||||
|
||||
if (x < 13.0) {
|
||||
z = 1.0;
|
||||
p = 0.0;
|
||||
u = x;
|
||||
while (u >= 3.0) {
|
||||
p -= 1.0;
|
||||
u = x + p;
|
||||
z *= u;
|
||||
}
|
||||
while (u < 2.0) {
|
||||
if (u == 0.0) {
|
||||
goto lgsing;
|
||||
}
|
||||
z /= u;
|
||||
p += 1.0;
|
||||
u = x + p;
|
||||
}
|
||||
if (z < 0.0) {
|
||||
*sign = -1;
|
||||
z = -z;
|
||||
} else {
|
||||
*sign = 1;
|
||||
}
|
||||
if (u == 2.0) {
|
||||
return (std::log(z));
|
||||
}
|
||||
p -= 2.0;
|
||||
x = x + p;
|
||||
p = x * polevl(x, gamma_B, 5) / p1evl(x, gamma_C, 6);
|
||||
return (std::log(z) + p);
|
||||
}
|
||||
|
||||
if (x > MAXLGM) {
|
||||
return (*sign * std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
if (x >= 1000.0) {
|
||||
return lgam_large_x(x);
|
||||
}
|
||||
|
||||
q = (x - 0.5) * std::log(x) - x + LS2PI;
|
||||
p = 1.0 / (x * x);
|
||||
return q + polevl(p, gamma_A, 4) / x;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/* Logarithm of Gamma function */
|
||||
SPECFUN_HOST_DEVICE inline double lgam(double x) {
|
||||
int sign;
|
||||
return detail::lgam_sgn(x, &sign);
|
||||
}
|
||||
|
||||
/* Sign of the Gamma function */
|
||||
SPECFUN_HOST_DEVICE inline double gammasgn(double x) {
|
||||
double fx;
|
||||
|
||||
if (std::isnan(x)) {
|
||||
return x;
|
||||
}
|
||||
if (x > 0) {
|
||||
return 1.0;
|
||||
} else {
|
||||
fx = std::floor(x);
|
||||
if (x - fx == 0.0) {
|
||||
return 0.0;
|
||||
} else if (static_cast<int>(fx) % 2) {
|
||||
return -1.0;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,595 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* hyp2f1.c
|
||||
*
|
||||
* Gauss hypergeometric function F
|
||||
* 2 1
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double a, b, c, x, y, hyp2f1();
|
||||
*
|
||||
* y = hyp2f1( a, b, c, x );
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
*
|
||||
* hyp2f1( a, b, c, x ) = F ( a, b; c; x )
|
||||
* 2 1
|
||||
*
|
||||
* inf.
|
||||
* - a(a+1)...(a+k) b(b+1)...(b+k) k+1
|
||||
* = 1 + > ----------------------------- x .
|
||||
* - c(c+1)...(c+k) (k+1)!
|
||||
* k = 0
|
||||
*
|
||||
* Cases addressed are
|
||||
* Tests and escapes for negative integer a, b, or c
|
||||
* Linear transformation if c - a or c - b negative integer
|
||||
* Special case c = a or c = b
|
||||
* Linear transformation for x near +1
|
||||
* Transformation for x < -0.5
|
||||
* Psi function expansion if x > 0.5 and c - a - b integer
|
||||
* Conditionally, a recurrence on c to make c-a-b > 0
|
||||
*
|
||||
* x < -1 AMS 15.3.7 transformation applied (Travis Oliphant)
|
||||
* valid for b,a,c,(b-a) != integer and (c-a),(c-b) != negative integer
|
||||
*
|
||||
* x >= 1 is rejected (unless special cases are present)
|
||||
*
|
||||
* The parameters a, b, c are considered to be integer
|
||||
* valued if they are within 1.0e-14 of the nearest integer
|
||||
* (1.0e-13 for IEEE arithmetic).
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
*
|
||||
* Relative error (-1 < x < 1):
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE -1,7 230000 1.2e-11 5.2e-14
|
||||
*
|
||||
* Several special cases also tested with a, b, c in
|
||||
* the range -7 to 7.
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* A "partial loss of precision" message is printed if
|
||||
* the internally estimated relative error exceeds 1^-12.
|
||||
* A "singularity" message is printed on overflow or
|
||||
* in cases not addressed (such as x < -1).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1992, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
#include "psi.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
constexpr double hyp2f1_EPS = 1.0e-13;
|
||||
|
||||
constexpr double hyp2f1_ETHRESH = 1.0e-12;
|
||||
constexpr std::uint64_t hyp2f1_MAXITER = 10000;
|
||||
|
||||
/* hys2f1 and hyp2f1ra depend on each other, so we need this prototype */
|
||||
SPECFUN_HOST_DEVICE double hyp2f1ra(double a, double b, double c, double x, double *loss);
|
||||
|
||||
/* Defining power series expansion of Gauss hypergeometric function */
|
||||
/* The `loss` parameter estimates loss of significance */
|
||||
SPECFUN_HOST_DEVICE double hys2f1(double a, double b, double c, double x, double *loss) {
|
||||
double f, g, h, k, m, s, u, umax;
|
||||
std::uint64_t i;
|
||||
int ib, intflag = 0;
|
||||
|
||||
if (std::abs(b) > std::abs(a)) {
|
||||
/* Ensure that |a| > |b| ... */
|
||||
f = b;
|
||||
b = a;
|
||||
a = f;
|
||||
}
|
||||
|
||||
ib = std::round(b);
|
||||
|
||||
if (std::abs(b - ib) < hyp2f1_EPS && ib <= 0 && std::abs(b) < std::abs(a)) {
|
||||
/* .. except when `b` is a smaller negative integer */
|
||||
f = b;
|
||||
b = a;
|
||||
a = f;
|
||||
intflag = 1;
|
||||
}
|
||||
|
||||
if ((std::abs(a) > std::abs(c) + 1 || intflag) && std::abs(c - a) > 2 && std::abs(a) > 2) {
|
||||
/* |a| >> |c| implies that large cancellation error is to be expected.
|
||||
*
|
||||
* We try to reduce it with the recurrence relations
|
||||
*/
|
||||
return hyp2f1ra(a, b, c, x, loss);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
umax = 0.0;
|
||||
f = a;
|
||||
g = b;
|
||||
h = c;
|
||||
s = 1.0;
|
||||
u = 1.0;
|
||||
k = 0.0;
|
||||
do {
|
||||
if (std::abs(h) < hyp2f1_EPS) {
|
||||
*loss = 1.0;
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
m = k + 1.0;
|
||||
u = u * ((f + k) * (g + k) * x / ((h + k) * m));
|
||||
s += u;
|
||||
k = std::abs(u); /* remember largest term summed */
|
||||
if (k > umax)
|
||||
umax = k;
|
||||
k = m;
|
||||
if (++i > hyp2f1_MAXITER) { /* should never happen */
|
||||
*loss = 1.0;
|
||||
return (s);
|
||||
}
|
||||
} while (s == 0 || std::abs(u / s) > MACHEP);
|
||||
|
||||
/* return estimated relative error */
|
||||
*loss = (MACHEP * umax) / fabs(s) + (MACHEP * i);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* Apply transformations for |x| near 1 then call the power series */
|
||||
SPECFUN_HOST_DEVICE double hyt2f1(double a, double b, double c, double x, double *loss) {
|
||||
double p, q, r, s, t, y, w, d, err, err1;
|
||||
double ax, id, d1, d2, e, y1;
|
||||
int i, aid, sign;
|
||||
|
||||
int ia, ib, neg_int_a = 0, neg_int_b = 0;
|
||||
|
||||
ia = std::round(a);
|
||||
ib = std::round(b);
|
||||
|
||||
if (a <= 0 && std::abs(a - ia) < hyp2f1_EPS) { /* a is a negative integer */
|
||||
neg_int_a = 1;
|
||||
}
|
||||
|
||||
if (b <= 0 && std::abs(b - ib) < hyp2f1_EPS) { /* b is a negative integer */
|
||||
neg_int_b = 1;
|
||||
}
|
||||
|
||||
err = 0.0;
|
||||
s = 1.0 - x;
|
||||
if (x < -0.5 && !(neg_int_a || neg_int_b)) {
|
||||
if (b > a)
|
||||
y = std::pow(s, -a) * hys2f1(a, c - b, c, -x / s, &err);
|
||||
|
||||
else
|
||||
y = std::pow(s, -b) * hys2f1(c - a, b, c, -x / s, &err);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
d = c - a - b;
|
||||
id = std::round(d); /* nearest integer to d */
|
||||
|
||||
if (x > 0.9 && !(neg_int_a || neg_int_b)) {
|
||||
if (std::abs(d - id) > MACHEP) {
|
||||
int sgngam;
|
||||
|
||||
/* test for integer c-a-b */
|
||||
/* Try the power series first */
|
||||
y = hys2f1(a, b, c, x, &err);
|
||||
if (err < hyp2f1_ETHRESH) {
|
||||
goto done;
|
||||
}
|
||||
/* If power series fails, then apply AMS55 #15.3.6 */
|
||||
q = hys2f1(a, b, 1.0 - d, s, &err);
|
||||
sign = 1;
|
||||
w = lgam_sgn(d, &sgngam);
|
||||
sign *= sgngam;
|
||||
w -= lgam_sgn(c - a, &sgngam);
|
||||
sign *= sgngam;
|
||||
w -= lgam_sgn(c - b, &sgngam);
|
||||
sign *= sgngam;
|
||||
q *= sign * std::exp(w);
|
||||
r = std::pow(s, d) * hys2f1(c - a, c - b, d + 1.0, s, &err1);
|
||||
sign = 1;
|
||||
w = lgam_sgn(-d, &sgngam);
|
||||
sign *= sgngam;
|
||||
w -= lgam_sgn(a, &sgngam);
|
||||
sign *= sgngam;
|
||||
w -= lgam_sgn(b, &sgngam);
|
||||
sign *= sgngam;
|
||||
r *= sign * std::exp(w);
|
||||
y = q + r;
|
||||
|
||||
q = std::abs(q); /* estimate cancellation error */
|
||||
r = std::abs(r);
|
||||
if (q > r) {
|
||||
r = q;
|
||||
}
|
||||
err += err1 + (MACHEP * r) / y;
|
||||
|
||||
y *= special::cephes::Gamma(c);
|
||||
goto done;
|
||||
} else {
|
||||
/* Psi function expansion, AMS55 #15.3.10, #15.3.11, #15.3.12
|
||||
*
|
||||
* Although AMS55 does not explicitly state it, this expansion fails
|
||||
* for negative integer a or b, since the psi and Gamma functions
|
||||
* involved have poles.
|
||||
*/
|
||||
|
||||
if (id >= 0.0) {
|
||||
e = d;
|
||||
d1 = d;
|
||||
d2 = 0.0;
|
||||
aid = id;
|
||||
} else {
|
||||
e = -d;
|
||||
d1 = 0.0;
|
||||
d2 = d;
|
||||
aid = -id;
|
||||
}
|
||||
|
||||
ax = std::log(s);
|
||||
|
||||
/* sum for t = 0 */
|
||||
y = special::cephes::psi(1.0) + special::cephes::psi(1.0 + e) - special::cephes::psi(a + d1) -
|
||||
special::cephes::psi(b + d1) - ax;
|
||||
y /= special::cephes::Gamma(e + 1.0);
|
||||
|
||||
p = (a + d1) * (b + d1) * s / special::cephes::Gamma(e + 2.0); /* Poch for t=1 */
|
||||
t = 1.0;
|
||||
do {
|
||||
r = special::cephes::psi(1.0 + t) + special::cephes::psi(1.0 + t + e) -
|
||||
special::cephes::psi(a + t + d1) - special::cephes::psi(b + t + d1) - ax;
|
||||
q = p * r;
|
||||
y += q;
|
||||
p *= s * (a + t + d1) / (t + 1.0);
|
||||
p *= (b + t + d1) / (t + 1.0 + e);
|
||||
t += 1.0;
|
||||
if (t > hyp2f1_MAXITER) { /* should never happen */
|
||||
set_error("hyp2f1", SF_ERROR_SLOW, NULL);
|
||||
*loss = 1.0;
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
} while (y == 0 || std::abs(q / y) > hyp2f1_EPS);
|
||||
|
||||
if (id == 0.0) {
|
||||
y *= special::cephes::Gamma(c) / (special::cephes::Gamma(a) * special::cephes::Gamma(b));
|
||||
goto psidon;
|
||||
}
|
||||
|
||||
y1 = 1.0;
|
||||
|
||||
if (aid == 1)
|
||||
goto nosum;
|
||||
|
||||
t = 0.0;
|
||||
p = 1.0;
|
||||
for (i = 1; i < aid; i++) {
|
||||
r = 1.0 - e + t;
|
||||
p *= s * (a + t + d2) * (b + t + d2) / r;
|
||||
t += 1.0;
|
||||
p /= t;
|
||||
y1 += p;
|
||||
}
|
||||
nosum:
|
||||
p = special::cephes::Gamma(c);
|
||||
y1 *= special::cephes::Gamma(e) * p /
|
||||
(special::cephes::Gamma(a + d1) * special::cephes::Gamma(b + d1));
|
||||
|
||||
y *= p / (special::cephes::Gamma(a + d2) * special::cephes::Gamma(b + d2));
|
||||
if ((aid & 1) != 0)
|
||||
y = -y;
|
||||
|
||||
q = std::pow(s, id); /* s to the id power */
|
||||
if (id > 0.0)
|
||||
y *= q;
|
||||
else
|
||||
y1 *= q;
|
||||
|
||||
y += y1;
|
||||
psidon:
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use defining power series if no special cases */
|
||||
y = hys2f1(a, b, c, x, &err);
|
||||
|
||||
done:
|
||||
*loss = err;
|
||||
return (y);
|
||||
}
|
||||
|
||||
/*
|
||||
15.4.2 Abramowitz & Stegun.
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE double hyp2f1_neg_c_equal_bc(double a, double b, double x) {
|
||||
double k;
|
||||
double collector = 1;
|
||||
double sum = 1;
|
||||
double collector_max = 1;
|
||||
|
||||
if (!(std::abs(b) < 1e5)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
for (k = 1; k <= -b; k++) {
|
||||
collector *= (a + k - 1) * x / k;
|
||||
collector_max = std::fmax(std::abs(collector), collector_max);
|
||||
sum += collector;
|
||||
}
|
||||
|
||||
if (1e-16 * (1 + collector_max / std::abs(sum)) > 1e-7) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate hypergeometric function by two-term recurrence in `a`.
|
||||
*
|
||||
* This avoids some of the loss of precision in the strongly alternating
|
||||
* hypergeometric series, and can be used to reduce the `a` and `b` parameters
|
||||
* to smaller values.
|
||||
*
|
||||
* AMS55 #15.2.10
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE double hyp2f1ra(double a, double b, double c, double x, double *loss) {
|
||||
double f2, f1, f0;
|
||||
int n;
|
||||
double t, err, da;
|
||||
|
||||
/* Don't cross c or zero */
|
||||
if ((c < 0 && a <= c) || (c >= 0 && a >= c)) {
|
||||
da = std::round(a - c);
|
||||
} else {
|
||||
da = std::round(a);
|
||||
}
|
||||
t = a - da;
|
||||
|
||||
*loss = 0;
|
||||
|
||||
SPECFUN_ASSERT(da != 0);
|
||||
|
||||
if (std::abs(da) > hyp2f1_MAXITER) {
|
||||
/* Too expensive to compute this value, so give up */
|
||||
set_error("hyp2f1", SF_ERROR_NO_RESULT, NULL);
|
||||
*loss = 1.0;
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (da < 0) {
|
||||
/* Recurse down */
|
||||
f2 = 0;
|
||||
f1 = hys2f1(t, b, c, x, &err);
|
||||
*loss += err;
|
||||
f0 = hys2f1(t - 1, b, c, x, &err);
|
||||
*loss += err;
|
||||
t -= 1;
|
||||
for (n = 1; n < -da; ++n) {
|
||||
f2 = f1;
|
||||
f1 = f0;
|
||||
f0 = -(2 * t - c - t * x + b * x) / (c - t) * f1 - t * (x - 1) / (c - t) * f2;
|
||||
t -= 1;
|
||||
}
|
||||
} else {
|
||||
/* Recurse up */
|
||||
f2 = 0;
|
||||
f1 = hys2f1(t, b, c, x, &err);
|
||||
*loss += err;
|
||||
f0 = hys2f1(t + 1, b, c, x, &err);
|
||||
*loss += err;
|
||||
t += 1;
|
||||
for (n = 1; n < da; ++n) {
|
||||
f2 = f1;
|
||||
f1 = f0;
|
||||
f0 = -((2 * t - c - t * x + b * x) * f1 + (c - t) * f2) / (t * (x - 1));
|
||||
t += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return f0;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE double hyp2f1(double a, double b, double c, double x) {
|
||||
double d, d1, d2, e;
|
||||
double p, q, r, s, y, ax;
|
||||
double ia, ib, ic, id, err;
|
||||
double t1;
|
||||
int i, aid;
|
||||
int neg_int_a = 0, neg_int_b = 0;
|
||||
int neg_int_ca_or_cb = 0;
|
||||
|
||||
err = 0.0;
|
||||
ax = std::abs(x);
|
||||
s = 1.0 - x;
|
||||
ia = std::round(a); /* nearest integer to a */
|
||||
ib = std::round(b);
|
||||
|
||||
if (x == 0.0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
d = c - a - b;
|
||||
id = std::round(d);
|
||||
|
||||
if ((a == 0 || b == 0) && c != 0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if (a <= 0 && std::abs(a - ia) < detail::hyp2f1_EPS) { /* a is a negative integer */
|
||||
neg_int_a = 1;
|
||||
}
|
||||
|
||||
if (b <= 0 && std::abs(b - ib) < detail::hyp2f1_EPS) { /* b is a negative integer */
|
||||
neg_int_b = 1;
|
||||
}
|
||||
|
||||
if (d <= -1 && !(std::abs(d - id) > detail::hyp2f1_EPS && s < 0) && !(neg_int_a || neg_int_b)) {
|
||||
return std::pow(s, d) * hyp2f1(c - a, c - b, c, x);
|
||||
}
|
||||
if (d <= 0 && x == 1 && !(neg_int_a || neg_int_b))
|
||||
goto hypdiv;
|
||||
|
||||
if (ax < 1.0 || x == -1.0) {
|
||||
/* 2F1(a,b;b;x) = (1-x)**(-a) */
|
||||
if (std::abs(b - c) < detail::hyp2f1_EPS) { /* b = c */
|
||||
if (neg_int_b) {
|
||||
y = detail::hyp2f1_neg_c_equal_bc(a, b, x);
|
||||
} else {
|
||||
y = std::pow(s, -a); /* s to the -a power */
|
||||
}
|
||||
goto hypdon;
|
||||
}
|
||||
if (std::abs(a - c) < detail::hyp2f1_EPS) { /* a = c */
|
||||
y = std::pow(s, -b); /* s to the -b power */
|
||||
goto hypdon;
|
||||
}
|
||||
}
|
||||
|
||||
if (c <= 0.0) {
|
||||
ic = std::round(c); /* nearest integer to c */
|
||||
if (std::abs(c - ic) < detail::hyp2f1_EPS) { /* c is a negative integer */
|
||||
/* check if termination before explosion */
|
||||
if (neg_int_a && (ia > ic))
|
||||
goto hypok;
|
||||
if (neg_int_b && (ib > ic))
|
||||
goto hypok;
|
||||
goto hypdiv;
|
||||
}
|
||||
}
|
||||
|
||||
if (neg_int_a || neg_int_b) /* function is a polynomial */
|
||||
goto hypok;
|
||||
|
||||
t1 = std::abs(b - a);
|
||||
if (x < -2.0 && std::abs(t1 - round(t1)) > detail::hyp2f1_EPS) {
|
||||
/* This transform has a pole for b-a integer, and
|
||||
* may produce large cancellation errors for |1/x| close 1
|
||||
*/
|
||||
p = hyp2f1(a, 1 - c + a, 1 - b + a, 1.0 / x);
|
||||
q = hyp2f1(b, 1 - c + b, 1 - a + b, 1.0 / x);
|
||||
p *= std::pow(-x, -a);
|
||||
q *= std::pow(-x, -b);
|
||||
t1 = Gamma(c);
|
||||
s = t1 * Gamma(b - a) / (Gamma(b) * Gamma(c - a));
|
||||
y = t1 * Gamma(a - b) / (Gamma(a) * Gamma(c - b));
|
||||
return s * p + y * q;
|
||||
} else if (x < -1.0) {
|
||||
if (std::abs(a) < std::abs(b)) {
|
||||
return std::pow(s, -a) * hyp2f1(a, c - b, c, x / (x - 1));
|
||||
} else {
|
||||
return std::pow(s, -b) * hyp2f1(b, c - a, c, x / (x - 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (ax > 1.0) /* series diverges */
|
||||
goto hypdiv;
|
||||
|
||||
p = c - a;
|
||||
ia = std::round(p); /* nearest integer to c-a */
|
||||
if ((ia <= 0.0) && (std::abs(p - ia) < detail::hyp2f1_EPS)) /* negative int c - a */
|
||||
neg_int_ca_or_cb = 1;
|
||||
|
||||
r = c - b;
|
||||
ib = std::round(r); /* nearest integer to c-b */
|
||||
if ((ib <= 0.0) && (std::abs(r - ib) < detail::hyp2f1_EPS)) /* negative int c - b */
|
||||
neg_int_ca_or_cb = 1;
|
||||
|
||||
id = std::round(d); /* nearest integer to d */
|
||||
q = std::abs(d - id);
|
||||
|
||||
/* Thanks to Christian Burger <BURGER@DMRHRZ11.HRZ.Uni-Marburg.DE>
|
||||
* for reporting a bug here. */
|
||||
if (std::abs(ax - 1.0) < detail::hyp2f1_EPS) { /* |x| == 1.0 */
|
||||
if (x > 0.0) {
|
||||
if (neg_int_ca_or_cb) {
|
||||
if (d >= 0.0)
|
||||
goto hypf;
|
||||
else
|
||||
goto hypdiv;
|
||||
}
|
||||
if (d <= 0.0)
|
||||
goto hypdiv;
|
||||
y = Gamma(c) * Gamma(d) / (Gamma(p) * Gamma(r));
|
||||
goto hypdon;
|
||||
}
|
||||
if (d <= -1.0)
|
||||
goto hypdiv;
|
||||
}
|
||||
|
||||
/* Conditionally make d > 0 by recurrence on c
|
||||
* AMS55 #15.2.27
|
||||
*/
|
||||
if (d < 0.0) {
|
||||
/* Try the power series first */
|
||||
y = detail::hyt2f1(a, b, c, x, &err);
|
||||
if (err < detail::hyp2f1_ETHRESH)
|
||||
goto hypdon;
|
||||
/* Apply the recurrence if power series fails */
|
||||
err = 0.0;
|
||||
aid = 2 - id;
|
||||
e = c + aid;
|
||||
d2 = hyp2f1(a, b, e, x);
|
||||
d1 = hyp2f1(a, b, e + 1.0, x);
|
||||
q = a + b + 1.0;
|
||||
for (i = 0; i < aid; i++) {
|
||||
r = e - 1.0;
|
||||
y = (e * (r - (2.0 * e - q) * x) * d2 + (e - a) * (e - b) * x * d1) / (e * r * s);
|
||||
e = r;
|
||||
d1 = d2;
|
||||
d2 = y;
|
||||
}
|
||||
goto hypdon;
|
||||
}
|
||||
|
||||
if (neg_int_ca_or_cb) {
|
||||
goto hypf; /* negative integer c-a or c-b */
|
||||
}
|
||||
|
||||
hypok:
|
||||
y = detail::hyt2f1(a, b, c, x, &err);
|
||||
|
||||
hypdon:
|
||||
if (err > detail::hyp2f1_ETHRESH) {
|
||||
set_error("hyp2f1", SF_ERROR_LOSS, NULL);
|
||||
/* printf( "Estimated err = %.2e\n", err ); */
|
||||
}
|
||||
return (y);
|
||||
|
||||
/* The transformation for c-a or c-b negative integer
|
||||
* AMS55 #15.3.3
|
||||
*/
|
||||
hypf:
|
||||
y = std::pow(s, d) * detail::hys2f1(c - a, c - b, c, x, &err);
|
||||
goto hypdon;
|
||||
|
||||
/* The alarm exit */
|
||||
hypdiv:
|
||||
set_error("hyp2f1", SF_ERROR_OVERFLOW, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,360 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* hyperg.c
|
||||
*
|
||||
* Confluent hypergeometric function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double a, b, x, y, hyperg();
|
||||
*
|
||||
* y = hyperg( a, b, x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Computes the confluent hypergeometric function
|
||||
*
|
||||
* 1 2
|
||||
* a x a(a+1) x
|
||||
* F ( a,b;x ) = 1 + ---- + --------- + ...
|
||||
* 1 1 b 1! b(b+1) 2!
|
||||
*
|
||||
* Many higher transcendental functions are special cases of
|
||||
* this power series.
|
||||
*
|
||||
* As is evident from the formula, b must not be a negative
|
||||
* integer or zero unless a is an integer with 0 >= a > b.
|
||||
*
|
||||
* The routine attempts both a direct summation of the series
|
||||
* and an asymptotic expansion. In each case error due to
|
||||
* roundoff, cancellation, and nonconvergence is estimated.
|
||||
* The result with smaller estimated error is returned.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Tested at random points (a, b, x), all three variables
|
||||
* ranging from 0 to 30.
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,30 30000 1.8e-14 1.1e-15
|
||||
*
|
||||
* Larger errors can be observed when b is near a negative
|
||||
* integer or zero. Certain combinations of arguments yield
|
||||
* serious cancellation error in the power series summation
|
||||
* and also are not in the region of near convergence of the
|
||||
* asymptotic series. An error message is printed if the
|
||||
* self-estimated relative error is greater than 1.0e-12.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1988, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* the `type` parameter determines what converging factor to use */
|
||||
SPECFUN_HOST_DEVICE inline double hyp2f0(double a, double b, double x, int type, double *err) {
|
||||
double a0, alast, t, tlast, maxt;
|
||||
double n, an, bn, u, sum, temp;
|
||||
|
||||
an = a;
|
||||
bn = b;
|
||||
a0 = 1.0e0;
|
||||
alast = 1.0e0;
|
||||
sum = 0.0;
|
||||
n = 1.0e0;
|
||||
t = 1.0e0;
|
||||
tlast = 1.0e9;
|
||||
maxt = 0.0;
|
||||
|
||||
do {
|
||||
if (an == 0)
|
||||
goto pdone;
|
||||
if (bn == 0)
|
||||
goto pdone;
|
||||
|
||||
u = an * (bn * x / n);
|
||||
|
||||
/* check for blowup */
|
||||
temp = std::abs(u);
|
||||
if ((temp > 1.0) && (maxt > (std::numeric_limits<double>::max() / temp)))
|
||||
goto error;
|
||||
|
||||
a0 *= u;
|
||||
t = std::abs(a0);
|
||||
|
||||
/* terminating condition for asymptotic series:
|
||||
* the series is divergent (if a or b is not a negative integer),
|
||||
* but its leading part can be used as an asymptotic expansion
|
||||
*/
|
||||
if (t > tlast)
|
||||
goto ndone;
|
||||
|
||||
tlast = t;
|
||||
sum += alast; /* the sum is one term behind */
|
||||
alast = a0;
|
||||
|
||||
if (n > 200)
|
||||
goto ndone;
|
||||
|
||||
an += 1.0e0;
|
||||
bn += 1.0e0;
|
||||
n += 1.0e0;
|
||||
if (t > maxt)
|
||||
maxt = t;
|
||||
} while (t > MACHEP);
|
||||
|
||||
pdone: /* series converged! */
|
||||
|
||||
/* estimate error due to roundoff and cancellation */
|
||||
*err = std::abs(MACHEP * (n + maxt));
|
||||
|
||||
alast = a0;
|
||||
goto done;
|
||||
|
||||
ndone: /* series did not converge */
|
||||
|
||||
/* The following "Converging factors" are supposed to improve accuracy,
|
||||
* but do not actually seem to accomplish very much. */
|
||||
|
||||
n -= 1.0;
|
||||
x = 1.0 / x;
|
||||
|
||||
switch (type) { /* "type" given as subroutine argument */
|
||||
case 1:
|
||||
alast *= (0.5 + (0.125 + 0.25 * b - 0.5 * a + 0.25 * x - 0.25 * n) / x);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
alast *= 2.0 / 3.0 - b + 2.0 * a + x - n;
|
||||
break;
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
/* estimate error due to roundoff, cancellation, and nonconvergence */
|
||||
*err = MACHEP * (n + maxt) + std::abs(a0);
|
||||
|
||||
done:
|
||||
sum += alast;
|
||||
return (sum);
|
||||
|
||||
/* series blew up: */
|
||||
error:
|
||||
*err = std::numeric_limits<double>::infinity();
|
||||
set_error("hyperg", SF_ERROR_NO_RESULT, NULL);
|
||||
return (sum);
|
||||
}
|
||||
|
||||
/* asymptotic formula for hypergeometric function:
|
||||
*
|
||||
* ( -a
|
||||
* -- ( |z|
|
||||
* | (b) ( -------- 2f0( a, 1+a-b, -1/x )
|
||||
* ( --
|
||||
* ( | (b-a)
|
||||
*
|
||||
*
|
||||
* x a-b )
|
||||
* e |x| )
|
||||
* + -------- 2f0( b-a, 1-a, 1/x ) )
|
||||
* -- )
|
||||
* | (a) )
|
||||
*/
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double hy1f1a(double a, double b, double x, double *err) {
|
||||
double h1, h2, t, u, temp, acanc, asum, err1, err2;
|
||||
|
||||
if (x == 0) {
|
||||
acanc = 1.0;
|
||||
asum = std::numeric_limits<double>::infinity();
|
||||
goto adone;
|
||||
}
|
||||
temp = log(std::abs(x));
|
||||
t = x + temp * (a - b);
|
||||
u = -temp * a;
|
||||
|
||||
if (b > 0) {
|
||||
temp = special::cephes::lgam(b);
|
||||
t += temp;
|
||||
u += temp;
|
||||
}
|
||||
|
||||
h1 = hyp2f0(a, a - b + 1, -1.0 / x, 1, &err1);
|
||||
|
||||
temp = std::exp(u) / special::cephes::Gamma(b - a);
|
||||
h1 *= temp;
|
||||
err1 *= temp;
|
||||
|
||||
h2 = hyp2f0(b - a, 1.0 - a, 1.0 / x, 2, &err2);
|
||||
|
||||
if (a < 0)
|
||||
temp = std::exp(t) / special::cephes::Gamma(a);
|
||||
else
|
||||
temp = std::exp(t - special::cephes::lgam(a));
|
||||
|
||||
h2 *= temp;
|
||||
err2 *= temp;
|
||||
|
||||
if (x < 0.0)
|
||||
asum = h1;
|
||||
else
|
||||
asum = h2;
|
||||
|
||||
acanc = std::abs(err1) + std::abs(err2);
|
||||
|
||||
if (b < 0) {
|
||||
temp = special::cephes::Gamma(b);
|
||||
asum *= temp;
|
||||
acanc *= std::abs(temp);
|
||||
}
|
||||
|
||||
if (asum != 0.0)
|
||||
acanc /= std::abs(asum);
|
||||
|
||||
if (acanc != acanc)
|
||||
/* nan */
|
||||
acanc = 1.0;
|
||||
|
||||
if (std::isinf(asum))
|
||||
/* infinity */
|
||||
acanc = 0;
|
||||
|
||||
acanc *= 30.0; /* fudge factor, since error of asymptotic formula
|
||||
* often seems this much larger than advertised */
|
||||
adone:
|
||||
*err = acanc;
|
||||
return (asum);
|
||||
}
|
||||
|
||||
/* Power series summation for confluent hypergeometric function */
|
||||
SPECFUN_HOST_DEVICE inline double hy1f1p(double a, double b, double x, double *err) {
|
||||
double n, a0, sum, t, u, temp, maxn;
|
||||
double an, bn, maxt;
|
||||
double y, c, sumc;
|
||||
|
||||
/* set up for power series summation */
|
||||
an = a;
|
||||
bn = b;
|
||||
a0 = 1.0;
|
||||
sum = 1.0;
|
||||
c = 0.0;
|
||||
n = 1.0;
|
||||
t = 1.0;
|
||||
maxt = 0.0;
|
||||
*err = 1.0;
|
||||
|
||||
maxn = 200.0 + 2 * fabs(a) + 2 * fabs(b);
|
||||
|
||||
while (t > MACHEP) {
|
||||
if (bn == 0) { /* check bn first since if both */
|
||||
sf_error("hyperg", SF_ERROR_SINGULAR, NULL);
|
||||
return (std::numeric_limits<double>::infinity()); /* an and bn are zero it is */
|
||||
}
|
||||
if (an == 0) /* a singularity */
|
||||
return (sum);
|
||||
if (n > maxn) {
|
||||
/* too many terms; take the last one as error estimate */
|
||||
c = std::abs(c) + std::abs(t) * 50.0;
|
||||
goto pdone;
|
||||
}
|
||||
u = x * (an / (bn * n));
|
||||
|
||||
/* check for blowup */
|
||||
temp = std::abs(u);
|
||||
if ((temp > 1.0) && (maxt > (std::numeric_limits<double>::max() / temp))) {
|
||||
*err = 1.0; /* blowup: estimate 100% error */
|
||||
return sum;
|
||||
}
|
||||
|
||||
a0 *= u;
|
||||
|
||||
y = a0 - c;
|
||||
sumc = sum + y;
|
||||
c = (sumc - sum) - y;
|
||||
sum = sumc;
|
||||
|
||||
t = std::abs(a0);
|
||||
|
||||
an += 1.0;
|
||||
bn += 1.0;
|
||||
n += 1.0;
|
||||
}
|
||||
|
||||
pdone:
|
||||
|
||||
/* estimate error due to roundoff and cancellation */
|
||||
if (sum != 0.0) {
|
||||
*err = std::abs(c / sum);
|
||||
} else {
|
||||
*err = std::abs(c);
|
||||
}
|
||||
|
||||
if (*err != *err) {
|
||||
/* nan */
|
||||
*err = 1.0;
|
||||
}
|
||||
|
||||
return (sum);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double hyperg(double a, double b, double x) {
|
||||
double asum, psum, acanc, pcanc, temp;
|
||||
|
||||
/* See if a Kummer transformation will help */
|
||||
temp = b - a;
|
||||
if (std::abs(temp) < 0.001 * std::abs(a))
|
||||
return (exp(x) * hyperg(temp, b, -x));
|
||||
|
||||
/* Try power & asymptotic series, starting from the one that is likely OK */
|
||||
if (std::abs(x) < 10 + std::abs(a) + std::abs(b)) {
|
||||
psum = detail::hy1f1p(a, b, x, &pcanc);
|
||||
if (pcanc < 1.0e-15)
|
||||
goto done;
|
||||
asum = detail::hy1f1a(a, b, x, &acanc);
|
||||
} else {
|
||||
psum = detail::hy1f1a(a, b, x, &pcanc);
|
||||
if (pcanc < 1.0e-15)
|
||||
goto done;
|
||||
asum = detail::hy1f1p(a, b, x, &acanc);
|
||||
}
|
||||
|
||||
/* Pick the result with less estimated error */
|
||||
|
||||
if (acanc < pcanc) {
|
||||
pcanc = acanc;
|
||||
psum = asum;
|
||||
}
|
||||
|
||||
done:
|
||||
if (pcanc > 1.0e-12)
|
||||
set_error("hyperg", SF_ERROR_LOSS, NULL);
|
||||
|
||||
return (psum);
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,149 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* i0.c
|
||||
*
|
||||
* Modified Bessel function of order zero
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, i0();
|
||||
*
|
||||
* y = i0( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns modified Bessel function of order zero of the
|
||||
* argument.
|
||||
*
|
||||
* The function is defined as i0(x) = j0( ix ).
|
||||
*
|
||||
* The range is partitioned into the two intervals [0,8] and
|
||||
* (8, infinity). Chebyshev polynomial expansions are employed
|
||||
* in each interval.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,30 30000 5.8e-16 1.4e-16
|
||||
*
|
||||
*/
|
||||
/* i0e.c
|
||||
*
|
||||
* Modified Bessel function of order zero,
|
||||
* exponentially scaled
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, i0e();
|
||||
*
|
||||
* y = i0e( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns exponentially scaled modified Bessel function
|
||||
* of order zero of the argument.
|
||||
*
|
||||
* The function is defined as i0e(x) = exp(-|x|) j0( ix ).
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,30 30000 5.4e-16 1.2e-16
|
||||
* See i0().
|
||||
*
|
||||
*/
|
||||
|
||||
/* i0.c */
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "chbevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* Chebyshev coefficients for exp(-x) I0(x)
|
||||
* in the interval [0,8].
|
||||
*
|
||||
* lim(x->0){ exp(-x) I0(x) } = 1.
|
||||
*/
|
||||
constexpr double i0_A[] = {
|
||||
-4.41534164647933937950E-18, 3.33079451882223809783E-17, -2.43127984654795469359E-16,
|
||||
1.71539128555513303061E-15, -1.16853328779934516808E-14, 7.67618549860493561688E-14,
|
||||
-4.85644678311192946090E-13, 2.95505266312963983461E-12, -1.72682629144155570723E-11,
|
||||
9.67580903537323691224E-11, -5.18979560163526290666E-10, 2.65982372468238665035E-9,
|
||||
-1.30002500998624804212E-8, 6.04699502254191894932E-8, -2.67079385394061173391E-7,
|
||||
1.11738753912010371815E-6, -4.41673835845875056359E-6, 1.64484480707288970893E-5,
|
||||
-5.75419501008210370398E-5, 1.88502885095841655729E-4, -5.76375574538582365885E-4,
|
||||
1.63947561694133579842E-3, -4.32430999505057594430E-3, 1.05464603945949983183E-2,
|
||||
-2.37374148058994688156E-2, 4.93052842396707084878E-2, -9.49010970480476444210E-2,
|
||||
1.71620901522208775349E-1, -3.04682672343198398683E-1, 6.76795274409476084995E-1};
|
||||
|
||||
/* Chebyshev coefficients for exp(-x) sqrt(x) I0(x)
|
||||
* in the inverted interval [8,infinity].
|
||||
*
|
||||
* lim(x->inf){ exp(-x) sqrt(x) I0(x) } = 1/sqrt(2pi).
|
||||
*/
|
||||
constexpr double i0_B[] = {
|
||||
-7.23318048787475395456E-18, -4.83050448594418207126E-18, 4.46562142029675999901E-17,
|
||||
3.46122286769746109310E-17, -2.82762398051658348494E-16, -3.42548561967721913462E-16,
|
||||
1.77256013305652638360E-15, 3.81168066935262242075E-15, -9.55484669882830764870E-15,
|
||||
-4.15056934728722208663E-14, 1.54008621752140982691E-14, 3.85277838274214270114E-13,
|
||||
7.18012445138366623367E-13, -1.79417853150680611778E-12, -1.32158118404477131188E-11,
|
||||
-3.14991652796324136454E-11, 1.18891471078464383424E-11, 4.94060238822496958910E-10,
|
||||
3.39623202570838634515E-9, 2.26666899049817806459E-8, 2.04891858946906374183E-7,
|
||||
2.89137052083475648297E-6, 6.88975834691682398426E-5, 3.36911647825569408990E-3,
|
||||
8.04490411014108831608E-1};
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double i0(double x) {
|
||||
double y;
|
||||
|
||||
if (x < 0)
|
||||
x = -x;
|
||||
if (x <= 8.0) {
|
||||
y = (x / 2.0) - 2.0;
|
||||
return (std::exp(x) * chbevl(y, detail::i0_A, 30));
|
||||
}
|
||||
|
||||
return (std::exp(x) * chbevl(32.0 / x - 2.0, detail::i0_B, 25) / sqrt(x));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double i0e(double x) {
|
||||
double y;
|
||||
|
||||
if (x < 0)
|
||||
x = -x;
|
||||
if (x <= 8.0) {
|
||||
y = (x / 2.0) - 2.0;
|
||||
return (chbevl(y, detail::i0_A, 30));
|
||||
}
|
||||
|
||||
return (chbevl(32.0 / x - 2.0, detail::i0_B, 25) / std::sqrt(x));
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,158 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* i1.c
|
||||
*
|
||||
* Modified Bessel function of order one
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, i1();
|
||||
*
|
||||
* y = i1( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns modified Bessel function of order one of the
|
||||
* argument.
|
||||
*
|
||||
* The function is defined as i1(x) = -i j1( ix ).
|
||||
*
|
||||
* The range is partitioned into the two intervals [0,8] and
|
||||
* (8, infinity). Chebyshev polynomial expansions are employed
|
||||
* in each interval.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 1.9e-15 2.1e-16
|
||||
*
|
||||
*
|
||||
*/
|
||||
/* i1e.c
|
||||
*
|
||||
* Modified Bessel function of order one,
|
||||
* exponentially scaled
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, i1e();
|
||||
*
|
||||
* y = i1e( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns exponentially scaled modified Bessel function
|
||||
* of order one of the argument.
|
||||
*
|
||||
* The function is defined as i1(x) = -i exp(-|x|) j1( ix ).
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 2.0e-15 2.0e-16
|
||||
* See i1().
|
||||
*
|
||||
*/
|
||||
|
||||
/* i1.c 2 */
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1985, 1987, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "chbevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* Chebyshev coefficients for exp(-x) I1(x) / x
|
||||
* in the interval [0,8].
|
||||
*
|
||||
* lim(x->0){ exp(-x) I1(x) / x } = 1/2.
|
||||
*/
|
||||
|
||||
constexpr double i1_A[] = {
|
||||
2.77791411276104639959E-18, -2.11142121435816608115E-17, 1.55363195773620046921E-16,
|
||||
-1.10559694773538630805E-15, 7.60068429473540693410E-15, -5.04218550472791168711E-14,
|
||||
3.22379336594557470981E-13, -1.98397439776494371520E-12, 1.17361862988909016308E-11,
|
||||
-6.66348972350202774223E-11, 3.62559028155211703701E-10, -1.88724975172282928790E-9,
|
||||
9.38153738649577178388E-9, -4.44505912879632808065E-8, 2.00329475355213526229E-7,
|
||||
-8.56872026469545474066E-7, 3.47025130813767847674E-6, -1.32731636560394358279E-5,
|
||||
4.78156510755005422638E-5, -1.61760815825896745588E-4, 5.12285956168575772895E-4,
|
||||
-1.51357245063125314899E-3, 4.15642294431288815669E-3, -1.05640848946261981558E-2,
|
||||
2.47264490306265168283E-2, -5.29459812080949914269E-2, 1.02643658689847095384E-1,
|
||||
-1.76416518357834055153E-1, 2.52587186443633654823E-1};
|
||||
|
||||
/* Chebyshev coefficients for exp(-x) sqrt(x) I1(x)
|
||||
* in the inverted interval [8,infinity].
|
||||
*
|
||||
* lim(x->inf){ exp(-x) sqrt(x) I1(x) } = 1/sqrt(2pi).
|
||||
*/
|
||||
constexpr double i1_B[] = {
|
||||
7.51729631084210481353E-18, 4.41434832307170791151E-18, -4.65030536848935832153E-17,
|
||||
-3.20952592199342395980E-17, 2.96262899764595013876E-16, 3.30820231092092828324E-16,
|
||||
-1.88035477551078244854E-15, -3.81440307243700780478E-15, 1.04202769841288027642E-14,
|
||||
4.27244001671195135429E-14, -2.10154184277266431302E-14, -4.08355111109219731823E-13,
|
||||
-7.19855177624590851209E-13, 2.03562854414708950722E-12, 1.41258074366137813316E-11,
|
||||
3.25260358301548823856E-11, -1.89749581235054123450E-11, -5.58974346219658380687E-10,
|
||||
-3.83538038596423702205E-9, -2.63146884688951950684E-8, -2.51223623787020892529E-7,
|
||||
-3.88256480887769039346E-6, -1.10588938762623716291E-4, -9.76109749136146840777E-3,
|
||||
7.78576235018280120474E-1};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double i1(double x) {
|
||||
double y, z;
|
||||
|
||||
z = std::abs(x);
|
||||
if (z <= 8.0) {
|
||||
y = (z / 2.0) - 2.0;
|
||||
z = chbevl(y, detail::i1_A, 29) * z * std::exp(z);
|
||||
} else {
|
||||
z = std::exp(z) * chbevl(32.0 / z - 2.0, detail::i1_B, 25) / std::sqrt(z);
|
||||
}
|
||||
if (x < 0.0)
|
||||
z = -z;
|
||||
return (z);
|
||||
}
|
||||
|
||||
/* i1e() */
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double i1e(double x) {
|
||||
double y, z;
|
||||
|
||||
z = std::abs(x);
|
||||
if (z <= 8.0) {
|
||||
y = (z / 2.0) - 2.0;
|
||||
z = chbevl(y, detail::i1_A, 29) * z;
|
||||
} else {
|
||||
z = chbevl(32.0 / z - 2.0, detail::i1_B, 25) / std::sqrt(z);
|
||||
}
|
||||
if (x < 0.0)
|
||||
z = -z;
|
||||
return (z);
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,421 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* igam.c
|
||||
*
|
||||
* Incomplete Gamma integral
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double a, x, y, igam();
|
||||
*
|
||||
* y = igam( a, x );
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* The function is defined by
|
||||
*
|
||||
* x
|
||||
* -
|
||||
* 1 | | -t a-1
|
||||
* igam(a,x) = ----- | e t dt.
|
||||
* - | |
|
||||
* | (a) -
|
||||
* 0
|
||||
*
|
||||
*
|
||||
* In this implementation both arguments must be positive.
|
||||
* The integral is evaluated by either a power series or
|
||||
* continued fraction expansion, depending on the relative
|
||||
* values of a and x.
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,30 200000 3.6e-14 2.9e-15
|
||||
* IEEE 0,100 300000 9.9e-14 1.5e-14
|
||||
*/
|
||||
/* igamc()
|
||||
*
|
||||
* Complemented incomplete Gamma integral
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double a, x, y, igamc();
|
||||
*
|
||||
* y = igamc( a, x );
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* The function is defined by
|
||||
*
|
||||
*
|
||||
* igamc(a,x) = 1 - igam(a,x)
|
||||
*
|
||||
* inf.
|
||||
* -
|
||||
* 1 | | -t a-1
|
||||
* = ----- | e t dt.
|
||||
* - | |
|
||||
* | (a) -
|
||||
* x
|
||||
*
|
||||
*
|
||||
* In this implementation both arguments must be positive.
|
||||
* The integral is evaluated by either a power series or
|
||||
* continued fraction expansion, depending on the relative
|
||||
* values of a and x.
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Tested at random a, x.
|
||||
* a x Relative error:
|
||||
* arithmetic domain domain # trials peak rms
|
||||
* IEEE 0.5,100 0,100 200000 1.9e-14 1.7e-15
|
||||
* IEEE 0.01,0.5 0,100 200000 1.4e-13 1.6e-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.0: April, 1987
|
||||
* Copyright 1985, 1987 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
|
||||
/* Sources
|
||||
* [1] "The Digital Library of Mathematical Functions", dlmf.nist.gov
|
||||
* [2] Maddock et. al., "Incomplete Gamma Functions",
|
||||
* https://www.boost.org/doc/libs/1_61_0/libs/math/doc/html/math_toolkit/sf_gamma/igamma.html
|
||||
*/
|
||||
|
||||
/* Scipy changes:
|
||||
* - 05-01-2016: added asymptotic expansion for igam to improve the
|
||||
* a ~ x regime.
|
||||
* - 06-19-2016: additional series expansion added for igamc to
|
||||
* improve accuracy at small arguments.
|
||||
* - 06-24-2016: better choice of domain for the asymptotic series;
|
||||
* improvements in accuracy for the asymptotic series when a and x
|
||||
* are very close.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
#include "igam_asymp_coeff.h"
|
||||
#include "lanczos.h"
|
||||
#include "ndtr.h"
|
||||
#include "unity.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr int igam_MAXITER = 2000;
|
||||
constexpr int IGAM = 1;
|
||||
constexpr int IGAMC = 0;
|
||||
constexpr double igam_SMALL = 20;
|
||||
constexpr double igam_LARGE = 200;
|
||||
constexpr double igam_SMALLRATIO = 0.3;
|
||||
constexpr double igam_LARGERATIO = 4.5;
|
||||
|
||||
constexpr double igam_big = 4.503599627370496e15;
|
||||
constexpr double igam_biginv = 2.22044604925031308085e-16;
|
||||
|
||||
/* Compute
|
||||
*
|
||||
* x^a * exp(-x) / gamma(a)
|
||||
*
|
||||
* corrected from (15) and (16) in [2] by replacing exp(x - a) with
|
||||
* exp(a - x).
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline double igam_fac(double a, double x) {
|
||||
double ax, fac, res, num;
|
||||
|
||||
if (std::abs(a - x) > 0.4 * std::abs(a)) {
|
||||
ax = a * std::log(x) - x - special::cephes::lgam(a);
|
||||
if (ax < -MAXLOG) {
|
||||
set_error("igam", SF_ERROR_UNDERFLOW, NULL);
|
||||
return 0.0;
|
||||
}
|
||||
return std::exp(ax);
|
||||
}
|
||||
|
||||
fac = a + special::cephes::lanczos_g - 0.5;
|
||||
res = std::sqrt(fac / std::exp(1)) / special::cephes::lanczos_sum_expg_scaled(a);
|
||||
|
||||
if ((a < 200) && (x < 200)) {
|
||||
res *= std::exp(a - x) * std::pow(x / fac, a);
|
||||
} else {
|
||||
num = x - a - special::cephes::lanczos_g + 0.5;
|
||||
res *= std::exp(a * special::cephes::log1pmx(num / fac) + x * (0.5 - special::cephes::lanczos_g) / fac);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Compute igamc using DLMF 8.9.2. */
|
||||
SPECFUN_HOST_DEVICE inline double igamc_continued_fraction(double a, double x) {
|
||||
int i;
|
||||
double ans, ax, c, yc, r, t, y, z;
|
||||
double pk, pkm1, pkm2, qk, qkm1, qkm2;
|
||||
|
||||
ax = igam_fac(a, x);
|
||||
if (ax == 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* continued fraction */
|
||||
y = 1.0 - a;
|
||||
z = x + y + 1.0;
|
||||
c = 0.0;
|
||||
pkm2 = 1.0;
|
||||
qkm2 = x;
|
||||
pkm1 = x + 1.0;
|
||||
qkm1 = z * x;
|
||||
ans = pkm1 / qkm1;
|
||||
|
||||
for (i = 0; i < igam_MAXITER; i++) {
|
||||
c += 1.0;
|
||||
y += 1.0;
|
||||
z += 2.0;
|
||||
yc = y * c;
|
||||
pk = pkm1 * z - pkm2 * yc;
|
||||
qk = qkm1 * z - qkm2 * yc;
|
||||
if (qk != 0) {
|
||||
r = pk / qk;
|
||||
t = std::abs((ans - r) / r);
|
||||
ans = r;
|
||||
} else
|
||||
t = 1.0;
|
||||
pkm2 = pkm1;
|
||||
pkm1 = pk;
|
||||
qkm2 = qkm1;
|
||||
qkm1 = qk;
|
||||
if (std::abs(pk) > igam_big) {
|
||||
pkm2 *= igam_biginv;
|
||||
pkm1 *= igam_biginv;
|
||||
qkm2 *= igam_biginv;
|
||||
qkm1 *= igam_biginv;
|
||||
}
|
||||
if (t <= MACHEP) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (ans * ax);
|
||||
}
|
||||
|
||||
/* Compute igam using DLMF 8.11.4. */
|
||||
SPECFUN_HOST_DEVICE inline double igam_series(double a, double x) {
|
||||
int i;
|
||||
double ans, ax, c, r;
|
||||
|
||||
ax = igam_fac(a, x);
|
||||
if (ax == 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* power series */
|
||||
r = a;
|
||||
c = 1.0;
|
||||
ans = 1.0;
|
||||
|
||||
for (i = 0; i < igam_MAXITER; i++) {
|
||||
r += 1.0;
|
||||
c *= x / r;
|
||||
ans += c;
|
||||
if (c <= MACHEP * ans) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (ans * ax / a);
|
||||
}
|
||||
|
||||
/* Compute igamc using DLMF 8.7.3. This is related to the series in
|
||||
* igam_series but extra care is taken to avoid cancellation.
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline double igamc_series(double a, double x) {
|
||||
int n;
|
||||
double fac = 1;
|
||||
double sum = 0;
|
||||
double term, logx;
|
||||
|
||||
for (n = 1; n < igam_MAXITER; n++) {
|
||||
fac *= -x / n;
|
||||
term = fac / (a + n);
|
||||
sum += term;
|
||||
if (std::abs(term) <= MACHEP * std::abs(sum)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
logx = std::log(x);
|
||||
term = -special::cephes::expm1(a * logx - special::cephes::lgam1p(a));
|
||||
return term - std::exp(a * logx - special::cephes::lgam(a)) * sum;
|
||||
}
|
||||
|
||||
/* Compute igam/igamc using DLMF 8.12.3/8.12.4. */
|
||||
SPECFUN_HOST_DEVICE inline double asymptotic_series(double a, double x, int func) {
|
||||
int k, n, sgn;
|
||||
int maxpow = 0;
|
||||
double lambda = x / a;
|
||||
double sigma = (x - a) / a;
|
||||
double eta, res, ck, ckterm, term, absterm;
|
||||
double absoldterm = std::numeric_limits<double>::infinity();
|
||||
double etapow[detail::igam_asymp_coeff_N] = {1};
|
||||
double sum = 0;
|
||||
double afac = 1;
|
||||
|
||||
if (func == detail::IGAM) {
|
||||
sgn = -1;
|
||||
} else {
|
||||
sgn = 1;
|
||||
}
|
||||
|
||||
if (lambda > 1) {
|
||||
eta = std::sqrt(-2 * special::cephes::log1pmx(sigma));
|
||||
} else if (lambda < 1) {
|
||||
eta = -std::sqrt(-2 * special::cephes::log1pmx(sigma));
|
||||
} else {
|
||||
eta = 0;
|
||||
}
|
||||
res = 0.5 * special::cephes::erfc(sgn * eta * std::sqrt(a / 2));
|
||||
|
||||
for (k = 0; k < igam_asymp_coeff_K; k++) {
|
||||
ck = igam_asymp_coeff_d[k][0];
|
||||
for (n = 1; n < igam_asymp_coeff_N; n++) {
|
||||
if (n > maxpow) {
|
||||
etapow[n] = eta * etapow[n - 1];
|
||||
maxpow += 1;
|
||||
}
|
||||
ckterm = igam_asymp_coeff_d[k][n] * etapow[n];
|
||||
ck += ckterm;
|
||||
if (std::abs(ckterm) < MACHEP * std::abs(ck)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
term = ck * afac;
|
||||
absterm = std::abs(term);
|
||||
if (absterm > absoldterm) {
|
||||
break;
|
||||
}
|
||||
sum += term;
|
||||
if (absterm < MACHEP * std::abs(sum)) {
|
||||
break;
|
||||
}
|
||||
absoldterm = absterm;
|
||||
afac /= a;
|
||||
}
|
||||
res += sgn * std::exp(-0.5 * a * eta * eta) * sum / std::sqrt(2 * M_PI * a);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double igamc(double a, double x);
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double igam(double a, double x) {
|
||||
double absxma_a;
|
||||
|
||||
if (x < 0 || a < 0) {
|
||||
set_error("gammainc", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
} else if (a == 0) {
|
||||
if (x > 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
} else if (x == 0) {
|
||||
/* Zero integration limit */
|
||||
return 0;
|
||||
} else if (std::isinf(a)) {
|
||||
if (std::isinf(x)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
return 0;
|
||||
} else if (std::isinf(x)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Asymptotic regime where a ~ x; see [2]. */
|
||||
absxma_a = std::abs(x - a) / a;
|
||||
if ((a > detail::igam_SMALL) && (a < detail::igam_LARGE) && (absxma_a < detail::igam_SMALLRATIO)) {
|
||||
return detail::asymptotic_series(a, x, detail::IGAM);
|
||||
} else if ((a > detail::igam_LARGE) && (absxma_a < detail::igam_LARGERATIO / std::sqrt(a))) {
|
||||
return detail::asymptotic_series(a, x, detail::IGAM);
|
||||
}
|
||||
|
||||
if ((x > 1.0) && (x > a)) {
|
||||
return (1.0 - igamc(a, x));
|
||||
}
|
||||
|
||||
return detail::igam_series(a, x);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE double igamc(double a, double x) {
|
||||
double absxma_a;
|
||||
|
||||
if (x < 0 || a < 0) {
|
||||
set_error("gammaincc", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
} else if (a == 0) {
|
||||
if (x > 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
} else if (x == 0) {
|
||||
return 1;
|
||||
} else if (std::isinf(a)) {
|
||||
if (std::isinf(x)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
return 1;
|
||||
} else if (std::isinf(x)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Asymptotic regime where a ~ x; see [2]. */
|
||||
absxma_a = std::abs(x - a) / a;
|
||||
if ((a > detail::igam_SMALL) && (a < detail::igam_LARGE) && (absxma_a < detail::igam_SMALLRATIO)) {
|
||||
return detail::asymptotic_series(a, x, detail::IGAMC);
|
||||
} else if ((a > detail::igam_LARGE) && (absxma_a < detail::igam_LARGERATIO / std::sqrt(a))) {
|
||||
return detail::asymptotic_series(a, x, detail::IGAMC);
|
||||
}
|
||||
|
||||
/* Everywhere else; see [2]. */
|
||||
if (x > 1.1) {
|
||||
if (x < a) {
|
||||
return 1.0 - detail::igam_series(a, x);
|
||||
} else {
|
||||
return detail::igamc_continued_fraction(a, x);
|
||||
}
|
||||
} else if (x <= 0.5) {
|
||||
if (-0.4 / std::log(x) < a) {
|
||||
return 1.0 - detail::igam_series(a, x);
|
||||
} else {
|
||||
return detail::igamc_series(a, x);
|
||||
}
|
||||
} else {
|
||||
if (x * 1.1 < a) {
|
||||
return 1.0 - detail::igam_series(a, x);
|
||||
} else {
|
||||
return detail::igamc_series(a, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,313 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (C) Copyright John Maddock 2006.
|
||||
* Use, modification and distribution are subject to the
|
||||
* Boost Software License, Version 1.0. (See accompanying file
|
||||
* LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
#include "igam.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
SPECFUN_HOST_DEVICE double find_inverse_s(double p, double q) {
|
||||
/*
|
||||
* Computation of the Incomplete Gamma Function Ratios and their Inverse
|
||||
* ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
|
||||
* ACM Transactions on Mathematical Software, Vol. 12, No. 4,
|
||||
* December 1986, Pages 377-393.
|
||||
*
|
||||
* See equation 32.
|
||||
*/
|
||||
double s, t;
|
||||
constexpr double a[4] = {0.213623493715853, 4.28342155967104, 11.6616720288968, 3.31125922108741};
|
||||
constexpr double b[5] = {0.3611708101884203e-1, 1.27364489782223, 6.40691597760039, 6.61053765625462, 1};
|
||||
|
||||
if (p < 0.5) {
|
||||
t = std::sqrt(-2 * std::log(p));
|
||||
} else {
|
||||
t = std::sqrt(-2 * std::log(q));
|
||||
}
|
||||
s = t - polevl(t, a, 3) / polevl(t, b, 4);
|
||||
if (p < 0.5)
|
||||
s = -s;
|
||||
return s;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double didonato_SN(double a, double x, unsigned N, double tolerance) {
|
||||
/*
|
||||
* Computation of the Incomplete Gamma Function Ratios and their Inverse
|
||||
* ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
|
||||
* ACM Transactions on Mathematical Software, Vol. 12, No. 4,
|
||||
* December 1986, Pages 377-393.
|
||||
*
|
||||
* See equation 34.
|
||||
*/
|
||||
double sum = 1.0;
|
||||
|
||||
if (N >= 1) {
|
||||
unsigned i;
|
||||
double partial = x / (a + 1);
|
||||
|
||||
sum += partial;
|
||||
for (i = 2; i <= N; ++i) {
|
||||
partial *= x / (a + i);
|
||||
sum += partial;
|
||||
if (partial < tolerance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double find_inverse_gamma(double a, double p, double q) {
|
||||
/*
|
||||
* In order to understand what's going on here, you will
|
||||
* need to refer to:
|
||||
*
|
||||
* Computation of the Incomplete Gamma Function Ratios and their Inverse
|
||||
* ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
|
||||
* ACM Transactions on Mathematical Software, Vol. 12, No. 4,
|
||||
* December 1986, Pages 377-393.
|
||||
*/
|
||||
double result;
|
||||
|
||||
if (a == 1) {
|
||||
if (q > 0.9) {
|
||||
result = -std::log1p(-p);
|
||||
} else {
|
||||
result = -std::log(q);
|
||||
}
|
||||
} else if (a < 1) {
|
||||
double g = special::cephes::Gamma(a);
|
||||
double b = q * g;
|
||||
|
||||
if ((b > 0.6) || ((b >= 0.45) && (a >= 0.3))) {
|
||||
/* DiDonato & Morris Eq 21:
|
||||
*
|
||||
* There is a slight variation from DiDonato and Morris here:
|
||||
* the first form given here is unstable when p is close to 1,
|
||||
* making it impossible to compute the inverse of Q(a,x) for small
|
||||
* q. Fortunately the second form works perfectly well in this case.
|
||||
*/
|
||||
double u;
|
||||
if ((b * q > 1e-8) && (q > 1e-5)) {
|
||||
u = std::pow(p * g * a, 1 / a);
|
||||
} else {
|
||||
u = std::exp((-q / a) - SCIPY_EULER);
|
||||
}
|
||||
result = u / (1 - (u / (a + 1)));
|
||||
} else if ((a < 0.3) && (b >= 0.35)) {
|
||||
/* DiDonato & Morris Eq 22: */
|
||||
double t = std::exp(-SCIPY_EULER - b);
|
||||
double u = t * std::exp(t);
|
||||
result = t * std::exp(u);
|
||||
} else if ((b > 0.15) || (a >= 0.3)) {
|
||||
/* DiDonato & Morris Eq 23: */
|
||||
double y = -std::log(b);
|
||||
double u = y - (1 - a) * std::log(y);
|
||||
result = y - (1 - a) * std::log(u) - std::log(1 + (1 - a) / (1 + u));
|
||||
} else if (b > 0.1) {
|
||||
/* DiDonato & Morris Eq 24: */
|
||||
double y = -std::log(b);
|
||||
double u = y - (1 - a) * std::log(y);
|
||||
result = y - (1 - a) * std::log(u) -
|
||||
std::log((u * u + 2 * (3 - a) * u + (2 - a) * (3 - a)) / (u * u + (5 - a) * u + 2));
|
||||
} else {
|
||||
/* DiDonato & Morris Eq 25: */
|
||||
double y = -std::log(b);
|
||||
double c1 = (a - 1) * std::log(y);
|
||||
double c1_2 = c1 * c1;
|
||||
double c1_3 = c1_2 * c1;
|
||||
double c1_4 = c1_2 * c1_2;
|
||||
double a_2 = a * a;
|
||||
double a_3 = a_2 * a;
|
||||
|
||||
double c2 = (a - 1) * (1 + c1);
|
||||
double c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
|
||||
double c4 = (a - 1) * ((c1_3 / 3) - (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 +
|
||||
(11 * a_2 - 46 * a + 47) / 6);
|
||||
double c5 = (a - 1) * (-(c1_4 / 4) + (11 * a - 17) * c1_3 / 6 + (-3 * a_2 + 13 * a - 13) * c1_2 +
|
||||
(2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2 +
|
||||
(25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
|
||||
|
||||
double y_2 = y * y;
|
||||
double y_3 = y_2 * y;
|
||||
double y_4 = y_2 * y_2;
|
||||
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
|
||||
}
|
||||
} else {
|
||||
/* DiDonato and Morris Eq 31: */
|
||||
double s = find_inverse_s(p, q);
|
||||
|
||||
double s_2 = s * s;
|
||||
double s_3 = s_2 * s;
|
||||
double s_4 = s_2 * s_2;
|
||||
double s_5 = s_4 * s;
|
||||
double ra = std::sqrt(a);
|
||||
|
||||
double w = a + s * ra + (s_2 - 1) / 3;
|
||||
w += (s_3 - 7 * s) / (36 * ra);
|
||||
w -= (3 * s_4 + 7 * s_2 - 16) / (810 * a);
|
||||
w += (9 * s_5 + 256 * s_3 - 433 * s) / (38880 * a * ra);
|
||||
|
||||
if ((a >= 500) && (std::abs(1 - w / a) < 1e-6)) {
|
||||
result = w;
|
||||
} else if (p > 0.5) {
|
||||
if (w < 3 * a) {
|
||||
result = w;
|
||||
} else {
|
||||
double D = std::fmax(2, a * (a - 1));
|
||||
double lg = special::cephes::lgam(a);
|
||||
double lb = std::log(q) + lg;
|
||||
if (lb < -D * 2.3) {
|
||||
/* DiDonato and Morris Eq 25: */
|
||||
double y = -lb;
|
||||
double c1 = (a - 1) * std::log(y);
|
||||
double c1_2 = c1 * c1;
|
||||
double c1_3 = c1_2 * c1;
|
||||
double c1_4 = c1_2 * c1_2;
|
||||
double a_2 = a * a;
|
||||
double a_3 = a_2 * a;
|
||||
|
||||
double c2 = (a - 1) * (1 + c1);
|
||||
double c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
|
||||
double c4 = (a - 1) * ((c1_3 / 3) - (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 +
|
||||
(11 * a_2 - 46 * a + 47) / 6);
|
||||
double c5 =
|
||||
(a - 1) * (-(c1_4 / 4) + (11 * a - 17) * c1_3 / 6 + (-3 * a_2 + 13 * a - 13) * c1_2 +
|
||||
(2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2 +
|
||||
(25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
|
||||
|
||||
double y_2 = y * y;
|
||||
double y_3 = y_2 * y;
|
||||
double y_4 = y_2 * y_2;
|
||||
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
|
||||
} else {
|
||||
/* DiDonato and Morris Eq 33: */
|
||||
double u = -lb + (a - 1) * std::log(w) - std::log(1 + (1 - a) / (1 + w));
|
||||
result = -lb + (a - 1) * std::log(u) - std::log(1 + (1 - a) / (1 + u));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
double z = w;
|
||||
double ap1 = a + 1;
|
||||
double ap2 = a + 2;
|
||||
if (w < 0.15 * ap1) {
|
||||
/* DiDonato and Morris Eq 35: */
|
||||
double v = std::log(p) + special::cephes::lgam(ap1);
|
||||
z = std::exp((v + w) / a);
|
||||
s = std::log1p(z / ap1 * (1 + z / ap2));
|
||||
z = std::exp((v + z - s) / a);
|
||||
s = std::log1p(z / ap1 * (1 + z / ap2));
|
||||
z = std::exp((v + z - s) / a);
|
||||
s = std::log1p(z / ap1 * (1 + z / ap2 * (1 + z / (a + 3))));
|
||||
z = std::exp((v + z - s) / a);
|
||||
}
|
||||
|
||||
if ((z <= 0.01 * ap1) || (z > 0.7 * ap1)) {
|
||||
result = z;
|
||||
} else {
|
||||
/* DiDonato and Morris Eq 36: */
|
||||
double ls = std::log(didonato_SN(a, z, 100, 1e-4));
|
||||
double v = std::log(p) + special::cephes::lgam(ap1);
|
||||
z = std::exp((v + z - ls) / a);
|
||||
result = z * (1 - (a * std::log(z) - z - v + ls) / (a - z));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double igamci(double a, double q);
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double igami(double a, double p) {
|
||||
int i;
|
||||
double x, fac, f_fp, fpp_fp;
|
||||
|
||||
if (std::isnan(a) || std::isnan(p)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
;
|
||||
} else if ((a < 0) || (p < 0) || (p > 1)) {
|
||||
set_error("gammaincinv", SF_ERROR_DOMAIN, NULL);
|
||||
} else if (p == 0.0) {
|
||||
return 0.0;
|
||||
} else if (p == 1.0) {
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else if (p > 0.9) {
|
||||
return igamci(a, 1 - p);
|
||||
}
|
||||
|
||||
x = detail::find_inverse_gamma(a, p, 1 - p);
|
||||
/* Halley's method */
|
||||
for (i = 0; i < 3; i++) {
|
||||
fac = detail::igam_fac(a, x);
|
||||
if (fac == 0.0) {
|
||||
return x;
|
||||
}
|
||||
f_fp = (igam(a, x) - p) * x / fac;
|
||||
/* The ratio of the first and second derivatives simplifies */
|
||||
fpp_fp = -1.0 + (a - 1) / x;
|
||||
if (std::isinf(fpp_fp)) {
|
||||
/* Resort to Newton's method in the case of overflow */
|
||||
x = x - f_fp;
|
||||
} else {
|
||||
x = x - f_fp / (1.0 - 0.5 * f_fp * fpp_fp);
|
||||
}
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double igamci(double a, double q) {
|
||||
int i;
|
||||
double x, fac, f_fp, fpp_fp;
|
||||
|
||||
if (std::isnan(a) || std::isnan(q)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
} else if ((a < 0.0) || (q < 0.0) || (q > 1.0)) {
|
||||
set_error("gammainccinv", SF_ERROR_DOMAIN, NULL);
|
||||
} else if (q == 0.0) {
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else if (q == 1.0) {
|
||||
return 0.0;
|
||||
} else if (q > 0.9) {
|
||||
return igami(a, 1 - q);
|
||||
}
|
||||
|
||||
x = detail::find_inverse_gamma(a, 1 - q, q);
|
||||
for (i = 0; i < 3; i++) {
|
||||
fac = detail::igam_fac(a, x);
|
||||
if (fac == 0.0) {
|
||||
return x;
|
||||
}
|
||||
f_fp = (igamc(a, x) - q) * x / (-fac);
|
||||
fpp_fp = -1.0 + (a - 1) / x;
|
||||
if (std::isinf(fpp_fp)) {
|
||||
x = x - f_fp;
|
||||
} else {
|
||||
x = x - f_fp / (1.0 - 0.5 * f_fp * fpp_fp);
|
||||
}
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,225 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* j0.c
|
||||
*
|
||||
* Bessel function of order zero
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, j0();
|
||||
*
|
||||
* y = j0( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns Bessel function of order zero of the argument.
|
||||
*
|
||||
* The domain is divided into the intervals [0, 5] and
|
||||
* (5, infinity). In the first interval the following rational
|
||||
* approximation is used:
|
||||
*
|
||||
*
|
||||
* 2 2
|
||||
* (w - r ) (w - r ) P (w) / Q (w)
|
||||
* 1 2 3 8
|
||||
*
|
||||
* 2
|
||||
* where w = x and the two r's are zeros of the function.
|
||||
*
|
||||
* In the second interval, the Hankel asymptotic expansion
|
||||
* is employed with two rational functions of degree 6/6
|
||||
* and 7/7.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Absolute error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 60000 4.2e-16 1.1e-16
|
||||
*
|
||||
*/
|
||||
/* y0.c
|
||||
*
|
||||
* Bessel function of the second kind, order zero
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, y0();
|
||||
*
|
||||
* y = y0( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns Bessel function of the second kind, of order
|
||||
* zero, of the argument.
|
||||
*
|
||||
* The domain is divided into the intervals [0, 5] and
|
||||
* (5, infinity). In the first interval a rational approximation
|
||||
* R(x) is employed to compute
|
||||
* y0(x) = R(x) + 2 * log(x) * j0(x) / M_PI.
|
||||
* Thus a call to j0() is required.
|
||||
*
|
||||
* In the second interval, the Hankel asymptotic expansion
|
||||
* is employed with two rational functions of degree 6/6
|
||||
* and 7/7.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Absolute error, when y0(x) < 1; else relative error:
|
||||
*
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 1.3e-15 1.6e-16
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1989, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
|
||||
/* Note: all coefficients satisfy the relative error criterion
|
||||
* except YP, YQ which are designed for absolute error. */
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double j0_PP[7] = {
|
||||
7.96936729297347051624E-4, 8.28352392107440799803E-2, 1.23953371646414299388E0, 5.44725003058768775090E0,
|
||||
8.74716500199817011941E0, 5.30324038235394892183E0, 9.99999999999999997821E-1,
|
||||
};
|
||||
|
||||
constexpr double j0_PQ[7] = {
|
||||
9.24408810558863637013E-4, 8.56288474354474431428E-2, 1.25352743901058953537E0, 5.47097740330417105182E0,
|
||||
8.76190883237069594232E0, 5.30605288235394617618E0, 1.00000000000000000218E0,
|
||||
};
|
||||
|
||||
constexpr double j0_QP[8] = {
|
||||
-1.13663838898469149931E-2, -1.28252718670509318512E0, -1.95539544257735972385E1, -9.32060152123768231369E1,
|
||||
-1.77681167980488050595E2, -1.47077505154951170175E2, -5.14105326766599330220E1, -6.05014350600728481186E0,
|
||||
};
|
||||
|
||||
constexpr double j0_QQ[7] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
6.43178256118178023184E1, 8.56430025976980587198E2, 3.88240183605401609683E3, 7.24046774195652478189E3,
|
||||
5.93072701187316984827E3, 2.06209331660327847417E3, 2.42005740240291393179E2,
|
||||
};
|
||||
|
||||
constexpr double j0_YP[8] = {
|
||||
1.55924367855235737965E4, -1.46639295903971606143E7, 5.43526477051876500413E9,
|
||||
-9.82136065717911466409E11, 8.75906394395366999549E13, -3.46628303384729719441E15,
|
||||
4.42733268572569800351E16, -1.84950800436986690637E16,
|
||||
};
|
||||
|
||||
constexpr double j0_YQ[7] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
1.04128353664259848412E3, 6.26107330137134956842E5, 2.68919633393814121987E8, 8.64002487103935000337E10,
|
||||
2.02979612750105546709E13, 3.17157752842975028269E15, 2.50596256172653059228E17,
|
||||
};
|
||||
|
||||
/* 5.783185962946784521175995758455807035071 */
|
||||
constexpr double j0_DR1 = 5.78318596294678452118E0;
|
||||
|
||||
/* 30.47126234366208639907816317502275584842 */
|
||||
constexpr double j0_DR2 = 3.04712623436620863991E1;
|
||||
|
||||
constexpr double j0_RP[4] = {
|
||||
-4.79443220978201773821E9,
|
||||
1.95617491946556577543E12,
|
||||
-2.49248344360967716204E14,
|
||||
9.70862251047306323952E15,
|
||||
};
|
||||
|
||||
constexpr double j0_RQ[8] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
4.99563147152651017219E2, 1.73785401676374683123E5, 4.84409658339962045305E7, 1.11855537045356834862E10,
|
||||
2.11277520115489217587E12, 3.10518229857422583814E14, 3.18121955943204943306E16, 1.71086294081043136091E18,
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double j0(double x) {
|
||||
double w, z, p, q, xn;
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (x <= 5.0) {
|
||||
z = x * x;
|
||||
if (x < 1.0e-5) {
|
||||
return (1.0 - z / 4.0);
|
||||
}
|
||||
|
||||
p = (z - detail::j0_DR1) * (z - detail::j0_DR2);
|
||||
p = p * polevl(z, detail::j0_RP, 3) / p1evl(z, detail::j0_RQ, 8);
|
||||
return (p);
|
||||
}
|
||||
|
||||
w = 5.0 / x;
|
||||
q = 25.0 / (x * x);
|
||||
p = polevl(q, detail::j0_PP, 6) / polevl(q, detail::j0_PQ, 6);
|
||||
q = polevl(q, detail::j0_QP, 7) / p1evl(q, detail::j0_QQ, 7);
|
||||
xn = x - M_PI_4;
|
||||
p = p * std::cos(xn) - w * q * std::sin(xn);
|
||||
return (p * detail::SQRT2OPI / std::sqrt(x));
|
||||
}
|
||||
|
||||
/* y0() 2 */
|
||||
/* Bessel function of second kind, order zero */
|
||||
|
||||
/* Rational approximation coefficients YP[], YQ[] are used here.
|
||||
* The function computed is y0(x) - 2 * log(x) * j0(x) / M_PI,
|
||||
* whose value at x = 0 is 2 * ( log(0.5) + EUL ) / M_PI
|
||||
* = 0.073804295108687225.
|
||||
*/
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double y0(double x) {
|
||||
double w, z, p, q, xn;
|
||||
|
||||
if (x <= 5.0) {
|
||||
if (x == 0.0) {
|
||||
set_error("y0", SF_ERROR_SINGULAR, NULL);
|
||||
return -std::numeric_limits<double>::infinity();
|
||||
} else if (x < 0.0) {
|
||||
set_error("y0", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
z = x * x;
|
||||
w = polevl(z, detail::j0_YP, 7) / p1evl(z, detail::j0_YQ, 7);
|
||||
w += M_2_PI * std::log(x) * j0(x);
|
||||
return (w);
|
||||
}
|
||||
|
||||
w = 5.0 / x;
|
||||
z = 25.0 / (x * x);
|
||||
p = polevl(z, detail::j0_PP, 6) / polevl(z, detail::j0_PQ, 6);
|
||||
q = polevl(z, detail::j0_QP, 7) / p1evl(z, detail::j0_QQ, 7);
|
||||
xn = x - M_PI_4;
|
||||
p = p * std::sin(xn) + w * q * std::cos(xn);
|
||||
return (p * detail::SQRT2OPI / std::sqrt(x));
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,198 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* j1.c
|
||||
*
|
||||
* Bessel function of order one
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, j1();
|
||||
*
|
||||
* y = j1( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns Bessel function of order one of the argument.
|
||||
*
|
||||
* The domain is divided into the intervals [0, 8] and
|
||||
* (8, infinity). In the first interval a 24 term Chebyshev
|
||||
* expansion is used. In the second, the asymptotic
|
||||
* trigonometric representation is employed using two
|
||||
* rational functions of degree 5/5.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Absolute error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 2.6e-16 1.1e-16
|
||||
*
|
||||
*
|
||||
*/
|
||||
/* y1.c
|
||||
*
|
||||
* Bessel function of second kind of order one
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, y1();
|
||||
*
|
||||
* y = y1( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns Bessel function of the second kind of order one
|
||||
* of the argument.
|
||||
*
|
||||
* The domain is divided into the intervals [0, 8] and
|
||||
* (8, infinity). In the first interval a 25 term Chebyshev
|
||||
* expansion is used, and a call to j1() is required.
|
||||
* In the second, the asymptotic trigonometric representation
|
||||
* is employed using two rational functions of degree 5/5.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Absolute error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 1.0e-15 1.3e-16
|
||||
*
|
||||
* (error criterion relative when |y1| > 1).
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1989, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
|
||||
/*
|
||||
* #define PIO4 .78539816339744830962
|
||||
* #define THPIO4 2.35619449019234492885
|
||||
* #define SQ2OPI .79788456080286535588
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
constexpr double j1_RP[4] = {
|
||||
-8.99971225705559398224E8,
|
||||
4.52228297998194034323E11,
|
||||
-7.27494245221818276015E13,
|
||||
3.68295732863852883286E15,
|
||||
};
|
||||
|
||||
constexpr double j1_RQ[8] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
6.20836478118054335476E2, 2.56987256757748830383E5, 8.35146791431949253037E7, 2.21511595479792499675E10,
|
||||
4.74914122079991414898E12, 7.84369607876235854894E14, 8.95222336184627338078E16, 5.32278620332680085395E18,
|
||||
};
|
||||
|
||||
constexpr double j1_PP[7] = {
|
||||
7.62125616208173112003E-4, 7.31397056940917570436E-2, 1.12719608129684925192E0, 5.11207951146807644818E0,
|
||||
8.42404590141772420927E0, 5.21451598682361504063E0, 1.00000000000000000254E0,
|
||||
};
|
||||
|
||||
constexpr double j1_PQ[7] = {
|
||||
5.71323128072548699714E-4, 6.88455908754495404082E-2, 1.10514232634061696926E0, 5.07386386128601488557E0,
|
||||
8.39985554327604159757E0, 5.20982848682361821619E0, 9.99999999999999997461E-1,
|
||||
};
|
||||
|
||||
constexpr double j1_QP[8] = {
|
||||
5.10862594750176621635E-2, 4.98213872951233449420E0, 7.58238284132545283818E1, 3.66779609360150777800E2,
|
||||
7.10856304998926107277E2, 5.97489612400613639965E2, 2.11688757100572135698E2, 2.52070205858023719784E1,
|
||||
};
|
||||
|
||||
constexpr double j1_QQ[7] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
7.42373277035675149943E1, 1.05644886038262816351E3, 4.98641058337653607651E3, 9.56231892404756170795E3,
|
||||
7.99704160447350683650E3, 2.82619278517639096600E3, 3.36093607810698293419E2,
|
||||
};
|
||||
|
||||
constexpr double j1_YP[6] = {
|
||||
1.26320474790178026440E9, -6.47355876379160291031E11, 1.14509511541823727583E14,
|
||||
-8.12770255501325109621E15, 2.02439475713594898196E17, -7.78877196265950026825E17,
|
||||
};
|
||||
|
||||
constexpr double j1_YQ[8] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
5.94301592346128195359E2, 2.35564092943068577943E5, 7.34811944459721705660E7, 1.87601316108706159478E10,
|
||||
3.88231277496238566008E12, 6.20557727146953693363E14, 6.87141087355300489866E16, 3.97270608116560655612E18,
|
||||
};
|
||||
|
||||
constexpr double j1_Z1 = 1.46819706421238932572E1;
|
||||
constexpr double j1_Z2 = 4.92184563216946036703E1;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double j1(double x) {
|
||||
double w, z, p, q, xn;
|
||||
|
||||
w = x;
|
||||
if (x < 0) {
|
||||
return -j1(-x);
|
||||
}
|
||||
|
||||
if (w <= 5.0) {
|
||||
z = x * x;
|
||||
w = polevl(z, detail::j1_RP, 3) / p1evl(z, detail::j1_RQ, 8);
|
||||
w = w * x * (z - detail::j1_Z1) * (z - detail::j1_Z2);
|
||||
return (w);
|
||||
}
|
||||
|
||||
w = 5.0 / x;
|
||||
z = w * w;
|
||||
p = polevl(z, detail::j1_PP, 6) / polevl(z, detail::j1_PQ, 6);
|
||||
q = polevl(z, detail::j1_QP, 7) / p1evl(z, detail::j1_QQ, 7);
|
||||
xn = x - detail::THPIO4;
|
||||
p = p * std::cos(xn) - w * q * std::sin(xn);
|
||||
return (p * detail::SQRT2OPI / std::sqrt(x));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double y1(double x) {
|
||||
double w, z, p, q, xn;
|
||||
|
||||
if (x <= 5.0) {
|
||||
if (x == 0.0) {
|
||||
set_error("y1", SF_ERROR_SINGULAR, NULL);
|
||||
return -std::numeric_limits<double>::infinity();
|
||||
} else if (x <= 0.0) {
|
||||
set_error("y1", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
z = x * x;
|
||||
w = x * (polevl(z, detail::j1_YP, 5) / p1evl(z, detail::j1_YQ, 8));
|
||||
w += M_2_PI * (j1(x) * std::log(x) - 1.0 / x);
|
||||
return (w);
|
||||
}
|
||||
|
||||
w = 5.0 / x;
|
||||
z = w * w;
|
||||
p = polevl(z, detail::j1_PP, 6) / polevl(z, detail::j1_PQ, 6);
|
||||
q = polevl(z, detail::j1_QP, 7) / p1evl(z, detail::j1_QQ, 7);
|
||||
xn = x - detail::THPIO4;
|
||||
p = p * std::sin(xn) + w * q * std::cos(xn);
|
||||
return (p * detail::SQRT2OPI / std::sqrt(x));
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,715 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* jv.c
|
||||
*
|
||||
* Bessel function of noninteger order
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double v, x, y, jv();
|
||||
*
|
||||
* y = jv( v, x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns Bessel function of order v of the argument,
|
||||
* where v is real. Negative x is allowed if v is an integer.
|
||||
*
|
||||
* Several expansions are included: the ascending power
|
||||
* series, the Hankel expansion, and two transitional
|
||||
* expansions for large v. If v is not too large, it
|
||||
* is reduced by recurrence to a region of best accuracy.
|
||||
* The transitional expansions give 12D accuracy for v > 500.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
* Results for integer v are indicated by *, where x and v
|
||||
* both vary from -125 to +125. Otherwise,
|
||||
* x ranges from 0 to 125, v ranges as indicated by "domain."
|
||||
* Error criterion is absolute, except relative when |jv()| > 1.
|
||||
*
|
||||
* arithmetic v domain x domain # trials peak rms
|
||||
* IEEE 0,125 0,125 100000 4.6e-15 2.2e-16
|
||||
* IEEE -125,0 0,125 40000 5.4e-11 3.7e-13
|
||||
* IEEE 0,500 0,500 20000 4.4e-15 4.0e-16
|
||||
* Integer v:
|
||||
* IEEE -125,125 -125,125 50000 3.5e-15* 1.9e-16*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "airy.h"
|
||||
#include "cbrt.h"
|
||||
#include "gamma.h"
|
||||
#include "j0.h"
|
||||
#include "j1.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double jv_BIG = 1.44115188075855872E+17;
|
||||
|
||||
/* Reduce the order by backward recurrence.
|
||||
* AMS55 #9.1.27 and 9.1.73.
|
||||
*/
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double jv_recur(double *n, double x, double *newn, int cancel) {
|
||||
double pkm2, pkm1, pk, qkm2, qkm1;
|
||||
|
||||
/* double pkp1; */
|
||||
double k, ans, qk, xk, yk, r, t, kf;
|
||||
constexpr double big = jv_BIG;
|
||||
int nflag, ctr;
|
||||
int miniter, maxiter;
|
||||
|
||||
/* Continued fraction for Jn(x)/Jn-1(x)
|
||||
* AMS 9.1.73
|
||||
*
|
||||
* x -x^2 -x^2
|
||||
* ------ --------- --------- ...
|
||||
* 2 n + 2(n+1) + 2(n+2) +
|
||||
*
|
||||
* Compute it with the simplest possible algorithm.
|
||||
*
|
||||
* This continued fraction starts to converge when (|n| + m) > |x|.
|
||||
* Hence, at least |x|-|n| iterations are necessary before convergence is
|
||||
* achieved. There is a hard limit set below, m <= 30000, which is chosen
|
||||
* so that no branch in `jv` requires more iterations to converge.
|
||||
* The exact maximum number is (500/3.6)^2 - 500 ~ 19000
|
||||
*/
|
||||
|
||||
maxiter = 22000;
|
||||
miniter = std::abs(x) - std::abs(*n);
|
||||
if (miniter < 1) {
|
||||
miniter = 1;
|
||||
}
|
||||
|
||||
if (*n < 0.0) {
|
||||
nflag = 1;
|
||||
} else {
|
||||
nflag = 0;
|
||||
}
|
||||
|
||||
fstart:
|
||||
pkm2 = 0.0;
|
||||
qkm2 = 1.0;
|
||||
pkm1 = x;
|
||||
qkm1 = *n + *n;
|
||||
xk = -x * x;
|
||||
yk = qkm1;
|
||||
ans = 0.0; /* ans=0.0 ensures that t=1.0 in the first iteration */
|
||||
ctr = 0;
|
||||
do {
|
||||
yk += 2.0;
|
||||
pk = pkm1 * yk + pkm2 * xk;
|
||||
qk = qkm1 * yk + qkm2 * xk;
|
||||
pkm2 = pkm1;
|
||||
pkm1 = pk;
|
||||
qkm2 = qkm1;
|
||||
qkm1 = qk;
|
||||
|
||||
/* check convergence */
|
||||
if (qk != 0 && ctr > miniter)
|
||||
r = pk / qk;
|
||||
else
|
||||
r = 0.0;
|
||||
|
||||
if (r != 0) {
|
||||
t = std::abs((ans - r) / r);
|
||||
ans = r;
|
||||
} else {
|
||||
t = 1.0;
|
||||
}
|
||||
|
||||
if (++ctr > maxiter) {
|
||||
set_error("jv", SF_ERROR_UNDERFLOW, NULL);
|
||||
goto done;
|
||||
}
|
||||
if (t < MACHEP) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* renormalize coefficients */
|
||||
if (std::abs(pk) > big) {
|
||||
pkm2 /= big;
|
||||
pkm1 /= big;
|
||||
qkm2 /= big;
|
||||
qkm1 /= big;
|
||||
}
|
||||
} while (t > MACHEP);
|
||||
|
||||
done:
|
||||
if (ans == 0)
|
||||
ans = 1.0;
|
||||
|
||||
/* Change n to n-1 if n < 0 and the continued fraction is small */
|
||||
if (nflag > 0) {
|
||||
if (std::abs(ans) < 0.125) {
|
||||
nflag = -1;
|
||||
*n = *n - 1.0;
|
||||
goto fstart;
|
||||
}
|
||||
}
|
||||
|
||||
kf = *newn;
|
||||
|
||||
/* backward recurrence
|
||||
* 2k
|
||||
* J (x) = --- J (x) - J (x)
|
||||
* k-1 x k k+1
|
||||
*/
|
||||
|
||||
pk = 1.0;
|
||||
pkm1 = 1.0 / ans;
|
||||
k = *n - 1.0;
|
||||
r = 2 * k;
|
||||
do {
|
||||
pkm2 = (pkm1 * r - pk * x) / x;
|
||||
/* pkp1 = pk; */
|
||||
pk = pkm1;
|
||||
pkm1 = pkm2;
|
||||
r -= 2.0;
|
||||
/*
|
||||
* t = fabs(pkp1) + fabs(pk);
|
||||
* if( (k > (kf + 2.5)) && (fabs(pkm1) < 0.25*t) )
|
||||
* {
|
||||
* k -= 1.0;
|
||||
* t = x*x;
|
||||
* pkm2 = ( (r*(r+2.0)-t)*pk - r*x*pkp1 )/t;
|
||||
* pkp1 = pk;
|
||||
* pk = pkm1;
|
||||
* pkm1 = pkm2;
|
||||
* r -= 2.0;
|
||||
* }
|
||||
*/
|
||||
k -= 1.0;
|
||||
} while (k > (kf + 0.5));
|
||||
|
||||
/* Take the larger of the last two iterates
|
||||
* on the theory that it may have less cancellation error.
|
||||
*/
|
||||
|
||||
if (cancel) {
|
||||
if ((kf >= 0.0) && (std::abs(pk) > std::abs(pkm1))) {
|
||||
k += 1.0;
|
||||
pkm2 = pk;
|
||||
}
|
||||
}
|
||||
*newn = k;
|
||||
return (pkm2);
|
||||
}
|
||||
|
||||
/* Ascending power series for Jv(x).
|
||||
* AMS55 #9.1.10.
|
||||
*/
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double jv_jvs(double n, double x) {
|
||||
double t, u, y, z, k;
|
||||
int ex, sgngam;
|
||||
|
||||
z = -x * x / 4.0;
|
||||
u = 1.0;
|
||||
y = u;
|
||||
k = 1.0;
|
||||
t = 1.0;
|
||||
|
||||
while (t > MACHEP) {
|
||||
u *= z / (k * (n + k));
|
||||
y += u;
|
||||
k += 1.0;
|
||||
if (y != 0)
|
||||
t = std::abs(u / y);
|
||||
}
|
||||
t = std::frexp(0.5 * x, &ex);
|
||||
ex = ex * n;
|
||||
if ((ex > -1023) && (ex < 1023) && (n > 0.0) && (n < (MAXGAM - 1.0))) {
|
||||
t = std::pow(0.5 * x, n) / special::cephes::Gamma(n + 1.0);
|
||||
y *= t;
|
||||
} else {
|
||||
t = n * std::log(0.5 * x) - lgam_sgn(n + 1.0, &sgngam);
|
||||
if (y < 0) {
|
||||
sgngam = -sgngam;
|
||||
y = -y;
|
||||
}
|
||||
t += std::log(y);
|
||||
if (t < -MAXLOG) {
|
||||
return (0.0);
|
||||
}
|
||||
if (t > MAXLOG) {
|
||||
set_error("Jv", SF_ERROR_OVERFLOW, NULL);
|
||||
return (std::numeric_limits<double>::infinity());
|
||||
}
|
||||
y = sgngam * std::exp(t);
|
||||
}
|
||||
return (y);
|
||||
}
|
||||
|
||||
/* Hankel's asymptotic expansion
|
||||
* for large x.
|
||||
* AMS55 #9.2.5.
|
||||
*/
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double jv_hankel(double n, double x) {
|
||||
double t, u, z, k, sign, conv;
|
||||
double p, q, j, m, pp, qq;
|
||||
int flag;
|
||||
|
||||
m = 4.0 * n * n;
|
||||
j = 1.0;
|
||||
z = 8.0 * x;
|
||||
k = 1.0;
|
||||
p = 1.0;
|
||||
u = (m - 1.0) / z;
|
||||
q = u;
|
||||
sign = 1.0;
|
||||
conv = 1.0;
|
||||
flag = 0;
|
||||
t = 1.0;
|
||||
pp = 1.0e38;
|
||||
qq = 1.0e38;
|
||||
|
||||
while (t > MACHEP) {
|
||||
k += 2.0;
|
||||
j += 1.0;
|
||||
sign = -sign;
|
||||
u *= (m - k * k) / (j * z);
|
||||
p += sign * u;
|
||||
k += 2.0;
|
||||
j += 1.0;
|
||||
u *= (m - k * k) / (j * z);
|
||||
q += sign * u;
|
||||
t = std::abs(u / p);
|
||||
if (t < conv) {
|
||||
conv = t;
|
||||
qq = q;
|
||||
pp = p;
|
||||
flag = 1;
|
||||
}
|
||||
/* stop if the terms start getting larger */
|
||||
if ((flag != 0) && (t > conv)) {
|
||||
goto hank1;
|
||||
}
|
||||
}
|
||||
|
||||
hank1:
|
||||
u = x - (0.5 * n + 0.25) * M_PI;
|
||||
t = std::sqrt(2.0 / (M_PI * x)) * (pp * std::cos(u) - qq * std::sin(u));
|
||||
return (t);
|
||||
}
|
||||
|
||||
/* Asymptotic expansion for transition region,
|
||||
* n large and x close to n.
|
||||
* AMS55 #9.3.23.
|
||||
*/
|
||||
|
||||
constexpr double jv_PF2[] = {-9.0000000000000000000e-2, 8.5714285714285714286e-2};
|
||||
|
||||
constexpr double jv_PF3[] = {1.3671428571428571429e-1, -5.4920634920634920635e-2, -4.4444444444444444444e-3};
|
||||
|
||||
constexpr double jv_PF4[] = {1.3500000000000000000e-3, -1.6036054421768707483e-1, 4.2590187590187590188e-2,
|
||||
2.7330447330447330447e-3};
|
||||
|
||||
constexpr double jv_PG1[] = {-2.4285714285714285714e-1, 1.4285714285714285714e-2};
|
||||
|
||||
constexpr double jv_PG2[] = {-9.0000000000000000000e-3, 1.9396825396825396825e-1, -1.1746031746031746032e-2};
|
||||
|
||||
constexpr double jv_PG3[] = {1.9607142857142857143e-2, -1.5983694083694083694e-1, 6.3838383838383838384e-3};
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double jv_jnt(double n, double x) {
|
||||
double z, zz, z3;
|
||||
double cbn, n23, cbtwo;
|
||||
double ai, aip, bi, bip; /* Airy functions */
|
||||
double nk, fk, gk, pp, qq;
|
||||
double F[5], G[4];
|
||||
int k;
|
||||
|
||||
cbn = cbrt(n);
|
||||
z = (x - n) / cbn;
|
||||
cbtwo = cbrt(2.0);
|
||||
|
||||
/* Airy function */
|
||||
zz = -cbtwo * z;
|
||||
special::cephes::airy(zz, &ai, &aip, &bi, &bip);
|
||||
|
||||
/* polynomials in expansion */
|
||||
zz = z * z;
|
||||
z3 = zz * z;
|
||||
F[0] = 1.0;
|
||||
F[1] = -z / 5.0;
|
||||
F[2] = special::cephes::polevl(z3, jv_PF2, 1) * zz;
|
||||
F[3] = special::cephes::polevl(z3, jv_PF3, 2);
|
||||
F[4] = special::cephes::polevl(z3, jv_PF4, 3) * z;
|
||||
G[0] = 0.3 * zz;
|
||||
G[1] = special::cephes::polevl(z3, jv_PG1, 1);
|
||||
G[2] = special::cephes::polevl(z3, jv_PG2, 2) * z;
|
||||
G[3] = special::cephes::polevl(z3, jv_PG3, 2) * zz;
|
||||
|
||||
pp = 0.0;
|
||||
qq = 0.0;
|
||||
nk = 1.0;
|
||||
n23 = cbrt(n * n);
|
||||
|
||||
for (k = 0; k <= 4; k++) {
|
||||
fk = F[k] * nk;
|
||||
pp += fk;
|
||||
if (k != 4) {
|
||||
gk = G[k] * nk;
|
||||
qq += gk;
|
||||
}
|
||||
nk /= n23;
|
||||
}
|
||||
|
||||
fk = cbtwo * ai * pp / cbn + cbrt(4.0) * aip * qq / n;
|
||||
return (fk);
|
||||
}
|
||||
|
||||
/* Asymptotic expansion for large n.
|
||||
* AMS55 #9.3.35.
|
||||
*/
|
||||
|
||||
constexpr double jv_lambda[] = {1.0,
|
||||
1.041666666666666666666667E-1,
|
||||
8.355034722222222222222222E-2,
|
||||
1.282265745563271604938272E-1,
|
||||
2.918490264641404642489712E-1,
|
||||
8.816272674437576524187671E-1,
|
||||
3.321408281862767544702647E+0,
|
||||
1.499576298686255465867237E+1,
|
||||
7.892301301158651813848139E+1,
|
||||
4.744515388682643231611949E+2,
|
||||
3.207490090890661934704328E+3};
|
||||
|
||||
constexpr double jv_mu[] = {1.0,
|
||||
-1.458333333333333333333333E-1,
|
||||
-9.874131944444444444444444E-2,
|
||||
-1.433120539158950617283951E-1,
|
||||
-3.172272026784135480967078E-1,
|
||||
-9.424291479571202491373028E-1,
|
||||
-3.511203040826354261542798E+0,
|
||||
-1.572726362036804512982712E+1,
|
||||
-8.228143909718594444224656E+1,
|
||||
-4.923553705236705240352022E+2,
|
||||
-3.316218568547972508762102E+3};
|
||||
|
||||
constexpr double jv_P1[] = {-2.083333333333333333333333E-1, 1.250000000000000000000000E-1};
|
||||
|
||||
constexpr double jv_P2[] = {3.342013888888888888888889E-1, -4.010416666666666666666667E-1,
|
||||
7.031250000000000000000000E-2};
|
||||
|
||||
constexpr double jv_P3[] = {-1.025812596450617283950617E+0, 1.846462673611111111111111E+0,
|
||||
-8.912109375000000000000000E-1, 7.324218750000000000000000E-2};
|
||||
|
||||
constexpr double jv_P4[] = {4.669584423426247427983539E+0, -1.120700261622299382716049E+1,
|
||||
8.789123535156250000000000E+0, -2.364086914062500000000000E+0,
|
||||
1.121520996093750000000000E-1};
|
||||
|
||||
constexpr double jv_P5[] = {-2.8212072558200244877E1, 8.4636217674600734632E1, -9.1818241543240017361E1,
|
||||
4.2534998745388454861E1, -7.3687943594796316964E0, 2.27108001708984375E-1};
|
||||
|
||||
constexpr double jv_P6[] = {2.1257013003921712286E2, -7.6525246814118164230E2, 1.0599904525279998779E3,
|
||||
-6.9957962737613254123E2, 2.1819051174421159048E2, -2.6491430486951555525E1,
|
||||
5.7250142097473144531E-1};
|
||||
|
||||
constexpr double jv_P7[] = {-1.9194576623184069963E3, 8.0617221817373093845E3, -1.3586550006434137439E4,
|
||||
1.1655393336864533248E4, -5.3056469786134031084E3, 1.2009029132163524628E3,
|
||||
-1.0809091978839465550E2, 1.7277275025844573975E0};
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double jv_jnx(double n, double x) {
|
||||
double zeta, sqz, zz, zp, np;
|
||||
double cbn, n23, t, z, sz;
|
||||
double pp, qq, z32i, zzi;
|
||||
double ak, bk, akl, bkl;
|
||||
int sign, doa, dob, nflg, k, s, tk, tkp1, m;
|
||||
double u[8];
|
||||
double ai, aip, bi, bip;
|
||||
|
||||
/* Test for x very close to n. Use expansion for transition region if so. */
|
||||
cbn = cbrt(n);
|
||||
z = (x - n) / cbn;
|
||||
if (std::abs(z) <= 0.7) {
|
||||
return (jv_jnt(n, x));
|
||||
}
|
||||
|
||||
z = x / n;
|
||||
zz = 1.0 - z * z;
|
||||
if (zz == 0.0) {
|
||||
return (0.0);
|
||||
}
|
||||
|
||||
if (zz > 0.0) {
|
||||
sz = std::sqrt(zz);
|
||||
t = 1.5 * (std::log((1.0 + sz) / z) - sz); /* zeta ** 3/2 */
|
||||
zeta = cbrt(t * t);
|
||||
nflg = 1;
|
||||
} else {
|
||||
sz = std::sqrt(-zz);
|
||||
t = 1.5 * (sz - std::acos(1.0 / z));
|
||||
zeta = -cbrt(t * t);
|
||||
nflg = -1;
|
||||
}
|
||||
z32i = std::abs(1.0 / t);
|
||||
sqz = cbrt(t);
|
||||
|
||||
/* Airy function */
|
||||
n23 = cbrt(n * n);
|
||||
t = n23 * zeta;
|
||||
|
||||
special::cephes::airy(t, &ai, &aip, &bi, &bip);
|
||||
|
||||
/* polynomials in expansion */
|
||||
u[0] = 1.0;
|
||||
zzi = 1.0 / zz;
|
||||
u[1] = special::cephes::polevl(zzi, jv_P1, 1) / sz;
|
||||
u[2] = special::cephes::polevl(zzi, jv_P2, 2) / zz;
|
||||
u[3] = special::cephes::polevl(zzi, jv_P3, 3) / (sz * zz);
|
||||
pp = zz * zz;
|
||||
u[4] = special::cephes::polevl(zzi, jv_P4, 4) / pp;
|
||||
u[5] = special::cephes::polevl(zzi, jv_P5, 5) / (pp * sz);
|
||||
pp *= zz;
|
||||
u[6] = special::cephes::polevl(zzi, jv_P6, 6) / pp;
|
||||
u[7] = special::cephes::polevl(zzi, jv_P7, 7) / (pp * sz);
|
||||
|
||||
pp = 0.0;
|
||||
qq = 0.0;
|
||||
np = 1.0;
|
||||
/* flags to stop when terms get larger */
|
||||
doa = 1;
|
||||
dob = 1;
|
||||
akl = std::numeric_limits<double>::infinity();
|
||||
bkl = std::numeric_limits<double>::infinity();
|
||||
|
||||
for (k = 0; k <= 3; k++) {
|
||||
tk = 2 * k;
|
||||
tkp1 = tk + 1;
|
||||
zp = 1.0;
|
||||
ak = 0.0;
|
||||
bk = 0.0;
|
||||
for (s = 0; s <= tk; s++) {
|
||||
if (doa) {
|
||||
if ((s & 3) > 1)
|
||||
sign = nflg;
|
||||
else
|
||||
sign = 1;
|
||||
ak += sign * jv_mu[s] * zp * u[tk - s];
|
||||
}
|
||||
|
||||
if (dob) {
|
||||
m = tkp1 - s;
|
||||
if (((m + 1) & 3) > 1)
|
||||
sign = nflg;
|
||||
else
|
||||
sign = 1;
|
||||
bk += sign * jv_lambda[s] * zp * u[m];
|
||||
}
|
||||
zp *= z32i;
|
||||
}
|
||||
|
||||
if (doa) {
|
||||
ak *= np;
|
||||
t = std::abs(ak);
|
||||
if (t < akl) {
|
||||
akl = t;
|
||||
pp += ak;
|
||||
} else
|
||||
doa = 0;
|
||||
}
|
||||
|
||||
if (dob) {
|
||||
bk += jv_lambda[tkp1] * zp * u[0];
|
||||
bk *= -np / sqz;
|
||||
t = std::abs(bk);
|
||||
if (t < bkl) {
|
||||
bkl = t;
|
||||
qq += bk;
|
||||
} else
|
||||
dob = 0;
|
||||
}
|
||||
if (np < MACHEP)
|
||||
break;
|
||||
np /= n * n;
|
||||
}
|
||||
|
||||
/* normalizing factor ( 4*zeta/(1 - z**2) )**1/4 */
|
||||
t = 4.0 * zeta / zz;
|
||||
t = sqrt(sqrt(t));
|
||||
|
||||
t *= ai * pp / cbrt(n) + aip * qq / (n23 * n);
|
||||
return (t);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double jv(double n, double x) {
|
||||
double k, q, t, y, an;
|
||||
int i, sign, nint;
|
||||
|
||||
nint = 0; /* Flag for integer n */
|
||||
sign = 1; /* Flag for sign inversion */
|
||||
an = std::abs(n);
|
||||
y = std::floor(an);
|
||||
if (y == an) {
|
||||
nint = 1;
|
||||
i = an - 16384.0 * std::floor(an / 16384.0);
|
||||
if (n < 0.0) {
|
||||
if (i & 1)
|
||||
sign = -sign;
|
||||
n = an;
|
||||
}
|
||||
if (x < 0.0) {
|
||||
if (i & 1)
|
||||
sign = -sign;
|
||||
x = -x;
|
||||
}
|
||||
if (n == 0.0)
|
||||
return (j0(x));
|
||||
if (n == 1.0)
|
||||
return (sign * j1(x));
|
||||
}
|
||||
|
||||
if ((x < 0.0) && (y != an)) {
|
||||
set_error("Jv", SF_ERROR_DOMAIN, NULL);
|
||||
y = std::numeric_limits<double>::quiet_NaN();
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (x == 0 && n < 0 && !nint) {
|
||||
set_error("Jv", SF_ERROR_OVERFLOW, NULL);
|
||||
return std::numeric_limits<double>::infinity() / Gamma(n + 1);
|
||||
}
|
||||
|
||||
y = std::abs(x);
|
||||
|
||||
if (y * y < std::abs(n + 1) * detail::MACHEP) {
|
||||
return std::pow(0.5 * x, n) / Gamma(n + 1);
|
||||
}
|
||||
|
||||
k = 3.6 * std::sqrt(y);
|
||||
t = 3.6 * std::sqrt(an);
|
||||
if ((y < t) && (an > 21.0)) {
|
||||
return (sign * detail::jv_jvs(n, x));
|
||||
}
|
||||
if ((an < k) && (y > 21.0))
|
||||
return (sign * detail::jv_hankel(n, x));
|
||||
|
||||
if (an < 500.0) {
|
||||
/* Note: if x is too large, the continued fraction will fail; but then the
|
||||
* Hankel expansion can be used. */
|
||||
if (nint != 0) {
|
||||
k = 0.0;
|
||||
q = detail::jv_recur(&n, x, &k, 1);
|
||||
if (k == 0.0) {
|
||||
y = j0(x) / q;
|
||||
goto done;
|
||||
}
|
||||
if (k == 1.0) {
|
||||
y = j1(x) / q;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (an > 2.0 * y)
|
||||
goto rlarger;
|
||||
|
||||
if ((n >= 0.0) && (n < 20.0) && (y > 6.0) && (y < 20.0)) {
|
||||
/* Recur backwards from a larger value of n */
|
||||
rlarger:
|
||||
k = n;
|
||||
|
||||
y = y + an + 1.0;
|
||||
if (y < 30.0)
|
||||
y = 30.0;
|
||||
y = n + std::floor(y - n);
|
||||
q = detail::jv_recur(&y, x, &k, 0);
|
||||
y = detail::jv_jvs(y, x) * q;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (k <= 30.0) {
|
||||
k = 2.0;
|
||||
} else if (k < 90.0) {
|
||||
k = (3 * k) / 4;
|
||||
}
|
||||
if (an > (k + 3.0)) {
|
||||
if (n < 0.0) {
|
||||
k = -k;
|
||||
}
|
||||
q = n - std::floor(n);
|
||||
k = std::floor(k) + q;
|
||||
if (n > 0.0) {
|
||||
q = detail::jv_recur(&n, x, &k, 1);
|
||||
} else {
|
||||
t = k;
|
||||
k = n;
|
||||
q = detail::jv_recur(&t, x, &k, 1);
|
||||
k = t;
|
||||
}
|
||||
if (q == 0.0) {
|
||||
y = 0.0;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
k = n;
|
||||
q = 1.0;
|
||||
}
|
||||
|
||||
/* boundary between convergence of
|
||||
* power series and Hankel expansion
|
||||
*/
|
||||
y = std::abs(k);
|
||||
if (y < 26.0)
|
||||
t = (0.0083 * y + 0.09) * y + 12.9;
|
||||
else
|
||||
t = 0.9 * y;
|
||||
|
||||
if (x > t)
|
||||
y = detail::jv_hankel(k, x);
|
||||
else
|
||||
y = detail::jv_jvs(k, x);
|
||||
if (n > 0.0)
|
||||
y /= q;
|
||||
else
|
||||
y *= q;
|
||||
}
|
||||
|
||||
else {
|
||||
/* For large n, use the uniform expansion or the transitional expansion.
|
||||
* But if x is of the order of n**2, these may blow up, whereas the
|
||||
* Hankel expansion will then work.
|
||||
*/
|
||||
if (n < 0.0) {
|
||||
set_error("jv", SF_ERROR_LOSS, NULL);
|
||||
y = std::numeric_limits<double>::quiet_NaN();
|
||||
goto done;
|
||||
}
|
||||
t = x / n;
|
||||
t /= n;
|
||||
if (t > 0.3)
|
||||
y = detail::jv_hankel(n, x);
|
||||
else
|
||||
y = detail::jv_jnx(n, x);
|
||||
}
|
||||
|
||||
done:
|
||||
return (sign * y);
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,164 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* k0.c
|
||||
*
|
||||
* Modified Bessel function, third kind, order zero
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, k0();
|
||||
*
|
||||
* y = k0( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns modified Bessel function of the third kind
|
||||
* of order zero of the argument.
|
||||
*
|
||||
* The range is partitioned into the two intervals [0,8] and
|
||||
* (8, infinity). Chebyshev polynomial expansions are employed
|
||||
* in each interval.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Tested at 2000 random points between 0 and 8. Peak absolute
|
||||
* error (relative when K0 > 1) was 1.46e-14; rms, 4.26e-15.
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 1.2e-15 1.6e-16
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* message condition value returned
|
||||
* K0 domain x <= 0 INFINITY
|
||||
*
|
||||
*/
|
||||
/* k0e()
|
||||
*
|
||||
* Modified Bessel function, third kind, order zero,
|
||||
* exponentially scaled
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, k0e();
|
||||
*
|
||||
* y = k0e( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns exponentially scaled modified Bessel function
|
||||
* of the third kind of order zero of the argument.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 1.4e-15 1.4e-16
|
||||
* See k0().
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "chbevl.h"
|
||||
#include "i0.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
/* Chebyshev coefficients for K0(x) + log(x/2) I0(x)
|
||||
* in the interval [0,2]. The odd order coefficients are all
|
||||
* zero; only the even order coefficients are listed.
|
||||
*
|
||||
* lim(x->0){ K0(x) + log(x/2) I0(x) } = -EUL.
|
||||
*/
|
||||
|
||||
constexpr double k0_A[] = {1.37446543561352307156E-16, 4.25981614279661018399E-14, 1.03496952576338420167E-11,
|
||||
1.90451637722020886025E-9, 2.53479107902614945675E-7, 2.28621210311945178607E-5,
|
||||
1.26461541144692592338E-3, 3.59799365153615016266E-2, 3.44289899924628486886E-1,
|
||||
-5.35327393233902768720E-1};
|
||||
|
||||
/* Chebyshev coefficients for exp(x) sqrt(x) K0(x)
|
||||
* in the inverted interval [2,infinity].
|
||||
*
|
||||
* lim(x->inf){ exp(x) sqrt(x) K0(x) } = sqrt(pi/2).
|
||||
*/
|
||||
constexpr double k0_B[] = {
|
||||
5.30043377268626276149E-18, -1.64758043015242134646E-17, 5.21039150503902756861E-17,
|
||||
-1.67823109680541210385E-16, 5.51205597852431940784E-16, -1.84859337734377901440E-15,
|
||||
6.34007647740507060557E-15, -2.22751332699166985548E-14, 8.03289077536357521100E-14,
|
||||
-2.98009692317273043925E-13, 1.14034058820847496303E-12, -4.51459788337394416547E-12,
|
||||
1.85594911495471785253E-11, -7.95748924447710747776E-11, 3.57739728140030116597E-10,
|
||||
-1.69753450938905987466E-9, 8.57403401741422608519E-9, -4.66048989768794782956E-8,
|
||||
2.76681363944501510342E-7, -1.83175552271911948767E-6, 1.39498137188764993662E-5,
|
||||
-1.28495495816278026384E-4, 1.56988388573005337491E-3, -3.14481013119645005427E-2,
|
||||
2.44030308206595545468E0};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double k0(double x) {
|
||||
double y, z;
|
||||
|
||||
if (x == 0.0) {
|
||||
set_error("k0", SF_ERROR_SINGULAR, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else if (x < 0.0) {
|
||||
set_error("k0", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (x <= 2.0) {
|
||||
y = x * x - 2.0;
|
||||
y = chbevl(y, detail::k0_A, 10) - std::log(0.5 * x) * i0(x);
|
||||
return (y);
|
||||
}
|
||||
z = 8.0 / x - 2.0;
|
||||
y = std::exp(-x) * chbevl(z, detail::k0_B, 25) / std::sqrt(x);
|
||||
return (y);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE double inline k0e(double x) {
|
||||
double y;
|
||||
|
||||
if (x == 0.0) {
|
||||
set_error("k0e", SF_ERROR_SINGULAR, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else if (x < 0.0) {
|
||||
set_error("k0e", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (x <= 2.0) {
|
||||
y = x * x - 2.0;
|
||||
y = chbevl(y, detail::k0_A, 10) - std::log(0.5 * x) * i0(x);
|
||||
return (y * exp(x));
|
||||
}
|
||||
|
||||
y = chbevl(8.0 / x - 2.0, detail::k0_B, 25) / std::sqrt(x);
|
||||
return (y);
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,163 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* k1.c
|
||||
*
|
||||
* Modified Bessel function, third kind, order one
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, k1();
|
||||
*
|
||||
* y = k1( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Computes the modified Bessel function of the third kind
|
||||
* of order one of the argument.
|
||||
*
|
||||
* The range is partitioned into the two intervals [0,2] and
|
||||
* (2, infinity). Chebyshev polynomial expansions are employed
|
||||
* in each interval.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 1.2e-15 1.6e-16
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* message condition value returned
|
||||
* k1 domain x <= 0 INFINITY
|
||||
*
|
||||
*/
|
||||
/* k1e.c
|
||||
*
|
||||
* Modified Bessel function, third kind, order one,
|
||||
* exponentially scaled
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, k1e();
|
||||
*
|
||||
* y = k1e( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns exponentially scaled modified Bessel function
|
||||
* of the third kind of order one of the argument:
|
||||
*
|
||||
* k1e(x) = exp(x) * k1(x).
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0, 30 30000 7.8e-16 1.2e-16
|
||||
* See k1().
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "chbevl.h"
|
||||
#include "const.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
/* Chebyshev coefficients for x(K1(x) - log(x/2) I1(x))
|
||||
* in the interval [0,2].
|
||||
*
|
||||
* lim(x->0){ x(K1(x) - log(x/2) I1(x)) } = 1.
|
||||
*/
|
||||
|
||||
constexpr double k1_A[] = {
|
||||
-7.02386347938628759343E-18, -2.42744985051936593393E-15, -6.66690169419932900609E-13,
|
||||
-1.41148839263352776110E-10, -2.21338763073472585583E-8, -2.43340614156596823496E-6,
|
||||
-1.73028895751305206302E-4, -6.97572385963986435018E-3, -1.22611180822657148235E-1,
|
||||
-3.53155960776544875667E-1, 1.52530022733894777053E0};
|
||||
|
||||
/* Chebyshev coefficients for exp(x) sqrt(x) K1(x)
|
||||
* in the interval [2,infinity].
|
||||
*
|
||||
* lim(x->inf){ exp(x) sqrt(x) K1(x) } = sqrt(pi/2).
|
||||
*/
|
||||
constexpr double k1_B[] = {
|
||||
-5.75674448366501715755E-18, 1.79405087314755922667E-17, -5.68946255844285935196E-17,
|
||||
1.83809354436663880070E-16, -6.05704724837331885336E-16, 2.03870316562433424052E-15,
|
||||
-7.01983709041831346144E-15, 2.47715442448130437068E-14, -8.97670518232499435011E-14,
|
||||
3.34841966607842919884E-13, -1.28917396095102890680E-12, 5.13963967348173025100E-12,
|
||||
-2.12996783842756842877E-11, 9.21831518760500529508E-11, -4.19035475934189648750E-10,
|
||||
2.01504975519703286596E-9, -1.03457624656780970260E-8, 5.74108412545004946722E-8,
|
||||
-3.50196060308781257119E-7, 2.40648494783721712015E-6, -1.93619797416608296024E-5,
|
||||
1.95215518471351631108E-4, -2.85781685962277938680E-3, 1.03923736576817238437E-1,
|
||||
2.72062619048444266945E0};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double k1(double x) {
|
||||
double y, z;
|
||||
|
||||
if (x == 0.0) {
|
||||
set_error("k1", SF_ERROR_SINGULAR, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else if (x < 0.0) {
|
||||
set_error("k1", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
z = 0.5 * x;
|
||||
|
||||
if (x <= 2.0) {
|
||||
y = x * x - 2.0;
|
||||
y = std::log(z) * i1(x) + chbevl(y, detail::k1_A, 11) / x;
|
||||
return (y);
|
||||
}
|
||||
|
||||
return (std::exp(-x) * chbevl(8.0 / x - 2.0, detail::k1_B, 25) / std::sqrt(x));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE double k1e(double x) {
|
||||
double y;
|
||||
|
||||
if (x == 0.0) {
|
||||
set_error("k1e", SF_ERROR_SINGULAR, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else if (x < 0.0) {
|
||||
set_error("k1e", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (x <= 2.0) {
|
||||
y = x * x - 2.0;
|
||||
y = std::log(0.5 * x) * i1(x) + chbevl(y, detail::k1_A, 11) / x;
|
||||
return (y * exp(x));
|
||||
}
|
||||
|
||||
return (chbevl(8.0 / x - 2.0, detail::k1_B, 25) / std::sqrt(x));
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,243 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* kn.c
|
||||
*
|
||||
* Modified Bessel function, third kind, integer order
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, kn();
|
||||
* int n;
|
||||
*
|
||||
* y = kn( n, x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns modified Bessel function of the third kind
|
||||
* of order n of the argument.
|
||||
*
|
||||
* The range is partitioned into the two intervals [0,9.55] and
|
||||
* (9.55, infinity). An ascending power series is used in the
|
||||
* low range, and an asymptotic expansion in the high range.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,30 90000 1.8e-8 3.0e-10
|
||||
*
|
||||
* Error is high only near the crossover point x = 9.55
|
||||
* between the two expansions used.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1988, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
|
||||
/*
|
||||
* Algorithm for Kn.
|
||||
* n-1
|
||||
* -n - (n-k-1)! 2 k
|
||||
* K (x) = 0.5 (x/2) > -------- (-x /4)
|
||||
* n - k!
|
||||
* k=0
|
||||
*
|
||||
* inf. 2 k
|
||||
* n n - (x /4)
|
||||
* + (-1) 0.5(x/2) > {p(k+1) + p(n+k+1) - 2log(x/2)} ---------
|
||||
* - k! (n+k)!
|
||||
* k=0
|
||||
*
|
||||
* where p(m) is the psi function: p(1) = -EUL and
|
||||
*
|
||||
* m-1
|
||||
* -
|
||||
* p(m) = -EUL + > 1/k
|
||||
* -
|
||||
* k=1
|
||||
*
|
||||
* For large x,
|
||||
* 2 2 2
|
||||
* u-1 (u-1 )(u-3 )
|
||||
* K (z) = sqrt(pi/2z) exp(-z) { 1 + ------- + ------------ + ...}
|
||||
* v 1 2
|
||||
* 1! (8z) 2! (8z)
|
||||
* asymptotically, where
|
||||
*
|
||||
* 2
|
||||
* u = 4 v .
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr int kn_MAXFAC = 31;
|
||||
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double kn(int nn, double x) {
|
||||
double k, kf, nk1f, nkf, zn, t, s, z0, z;
|
||||
double ans, fn, pn, pk, zmn, tlg, tox;
|
||||
int i, n;
|
||||
|
||||
if (nn < 0)
|
||||
n = -nn;
|
||||
else
|
||||
n = nn;
|
||||
|
||||
if (n > detail::kn_MAXFAC) {
|
||||
overf:
|
||||
set_error("kn", SF_ERROR_OVERFLOW, NULL);
|
||||
return (std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
if (x <= 0.0) {
|
||||
if (x < 0.0) {
|
||||
set_error("kn", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
} else {
|
||||
set_error("kn", SF_ERROR_SINGULAR, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
}
|
||||
|
||||
if (x > 9.55)
|
||||
goto asymp;
|
||||
|
||||
ans = 0.0;
|
||||
z0 = 0.25 * x * x;
|
||||
fn = 1.0;
|
||||
pn = 0.0;
|
||||
zmn = 1.0;
|
||||
tox = 2.0 / x;
|
||||
|
||||
if (n > 0) {
|
||||
/* compute factorial of n and psi(n) */
|
||||
pn = -detail::SCIPY_EULER;
|
||||
k = 1.0;
|
||||
for (i = 1; i < n; i++) {
|
||||
pn += 1.0 / k;
|
||||
k += 1.0;
|
||||
fn *= k;
|
||||
}
|
||||
|
||||
zmn = tox;
|
||||
|
||||
if (n == 1) {
|
||||
ans = 1.0 / x;
|
||||
} else {
|
||||
nk1f = fn / n;
|
||||
kf = 1.0;
|
||||
s = nk1f;
|
||||
z = -z0;
|
||||
zn = 1.0;
|
||||
for (i = 1; i < n; i++) {
|
||||
nk1f = nk1f / (n - i);
|
||||
kf = kf * i;
|
||||
zn *= z;
|
||||
t = nk1f * zn / kf;
|
||||
s += t;
|
||||
if ((std::numeric_limits<double>::max() - std::abs(t)) < std::abs(s)) {
|
||||
goto overf;
|
||||
}
|
||||
if ((tox > 1.0) && ((std::numeric_limits<double>::max() / tox) < zmn)) {
|
||||
goto overf;
|
||||
}
|
||||
zmn *= tox;
|
||||
}
|
||||
s *= 0.5;
|
||||
t = std::abs(s);
|
||||
if ((zmn > 1.0) && ((std::numeric_limits<double>::max() / zmn) < t)) {
|
||||
goto overf;
|
||||
}
|
||||
if ((t > 1.0) && ((std::numeric_limits<double>::max() / t) < zmn)) {
|
||||
goto overf;
|
||||
}
|
||||
ans = s * zmn;
|
||||
}
|
||||
}
|
||||
|
||||
tlg = 2.0 * log(0.5 * x);
|
||||
pk = -detail::SCIPY_EULER;
|
||||
if (n == 0) {
|
||||
pn = pk;
|
||||
t = 1.0;
|
||||
} else {
|
||||
pn = pn + 1.0 / n;
|
||||
t = 1.0 / fn;
|
||||
}
|
||||
s = (pk + pn - tlg) * t;
|
||||
k = 1.0;
|
||||
do {
|
||||
t *= z0 / (k * (k + n));
|
||||
pk += 1.0 / k;
|
||||
pn += 1.0 / (k + n);
|
||||
s += (pk + pn - tlg) * t;
|
||||
k += 1.0;
|
||||
} while (fabs(t / s) > detail::MACHEP);
|
||||
|
||||
s = 0.5 * s / zmn;
|
||||
if (n & 1) {
|
||||
s = -s;
|
||||
}
|
||||
ans += s;
|
||||
|
||||
return (ans);
|
||||
|
||||
/* Asymptotic expansion for Kn(x) */
|
||||
/* Converges to 1.4e-17 for x > 18.4 */
|
||||
|
||||
asymp:
|
||||
|
||||
if (x > detail::MAXLOG) {
|
||||
set_error("kn", SF_ERROR_UNDERFLOW, NULL);
|
||||
return (0.0);
|
||||
}
|
||||
k = n;
|
||||
pn = 4.0 * k * k;
|
||||
pk = 1.0;
|
||||
z0 = 8.0 * x;
|
||||
fn = 1.0;
|
||||
t = 1.0;
|
||||
s = t;
|
||||
nkf = std::numeric_limits<double>::infinity();
|
||||
i = 0;
|
||||
do {
|
||||
z = pn - pk * pk;
|
||||
t = t * z / (fn * z0);
|
||||
nk1f = std::abs(t);
|
||||
if ((i >= n) && (nk1f > nkf)) {
|
||||
goto adone;
|
||||
}
|
||||
nkf = nk1f;
|
||||
s += t;
|
||||
fn += 1.0;
|
||||
pk += 2.0;
|
||||
i += 1;
|
||||
} while (std::abs(t / s) > detail::MACHEP);
|
||||
|
||||
adone:
|
||||
ans = std::exp(-x) * std::sqrt(M_PI / (2.0 * x)) * s;
|
||||
return (ans);
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,112 @@
|
||||
/* (C) Copyright John Maddock 2006.
|
||||
* Use, modification and distribution are subject to the
|
||||
* Boost Software License, Version 1.0. (See accompanying file
|
||||
* LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/* Both lanczos.h and lanczos.c were formed from Boost's lanczos.hpp
|
||||
*
|
||||
* Scipy changes:
|
||||
* - 06-22-2016: Removed all code not related to double precision and
|
||||
* ported to c for use in Cephes. Note that the order of the
|
||||
* coefficients is reversed to match the behavior of polevl.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Optimal values for G for each N are taken from
|
||||
* https://web.viu.ca/pughg/phdThesis/phdThesis.pdf,
|
||||
* as are the theoretical error bounds.
|
||||
*
|
||||
* Constants calculated using the method described by Godfrey
|
||||
* https://my.fit.edu/~gabdo/gamma.txt and elaborated by Toth at
|
||||
* https://www.rskey.org/gamma.htm using NTL::RR at 1000 bit precision.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Lanczos Coefficients for N=13 G=6.024680040776729583740234375
|
||||
* Max experimental error (with arbitrary precision arithmetic) 1.196214e-17
|
||||
* Generated with compiler: Microsoft Visual C++ version 8.0 on Win32 at Mar 23 2006
|
||||
*
|
||||
* Use for double precision.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double lanczos_num[] = {
|
||||
2.506628274631000270164908177133837338626, 210.8242777515793458725097339207133627117,
|
||||
8071.672002365816210638002902272250613822, 186056.2653952234950402949897160456992822,
|
||||
2876370.628935372441225409051620849613599, 31426415.58540019438061423162831820536287,
|
||||
248874557.8620541565114603864132294232163, 1439720407.311721673663223072794912393972,
|
||||
6039542586.35202800506429164430729792107, 17921034426.03720969991975575445893111267,
|
||||
35711959237.35566804944018545154716670596, 42919803642.64909876895789904700198885093,
|
||||
23531376880.41075968857200767445163675473};
|
||||
|
||||
constexpr double lanczos_denom[] = {1, 66, 1925, 32670, 357423, 2637558, 13339535,
|
||||
45995730, 105258076, 150917976, 120543840, 39916800, 0};
|
||||
|
||||
constexpr double lanczos_sum_expg_scaled_num[] = {
|
||||
0.006061842346248906525783753964555936883222, 0.5098416655656676188125178644804694509993,
|
||||
19.51992788247617482847860966235652136208, 449.9445569063168119446858607650988409623,
|
||||
6955.999602515376140356310115515198987526, 75999.29304014542649875303443598909137092,
|
||||
601859.6171681098786670226533699352302507, 3481712.15498064590882071018964774556468,
|
||||
14605578.08768506808414169982791359218571, 43338889.32467613834773723740590533316085,
|
||||
86363131.28813859145546927288977868422342, 103794043.1163445451906271053616070238554,
|
||||
56906521.91347156388090791033559122686859};
|
||||
|
||||
constexpr double lanczos_sum_expg_scaled_denom[] = {
|
||||
1, 66, 1925, 32670, 357423, 2637558, 13339535, 45995730, 105258076, 150917976, 120543840, 39916800, 0};
|
||||
|
||||
constexpr double lanczos_sum_near_1_d[] = {
|
||||
0.3394643171893132535170101292240837927725e-9, -0.2499505151487868335680273909354071938387e-8,
|
||||
0.8690926181038057039526127422002498960172e-8, -0.1933117898880828348692541394841204288047e-7,
|
||||
0.3075580174791348492737947340039992829546e-7, -0.2752907702903126466004207345038327818713e-7,
|
||||
-0.1515973019871092388943437623825208095123e-5, 0.004785200610085071473880915854204301886437,
|
||||
-0.1993758927614728757314233026257810172008, 1.483082862367253753040442933770164111678,
|
||||
-3.327150580651624233553677113928873034916, 2.208709979316623790862569924861841433016};
|
||||
|
||||
constexpr double lanczos_sum_near_2_d[] = {
|
||||
0.1009141566987569892221439918230042368112e-8, -0.7430396708998719707642735577238449585822e-8,
|
||||
0.2583592566524439230844378948704262291927e-7, -0.5746670642147041587497159649318454348117e-7,
|
||||
0.9142922068165324132060550591210267992072e-7, -0.8183698410724358930823737982119474130069e-7,
|
||||
-0.4506604409707170077136555010018549819192e-5, 0.01422519127192419234315002746252160965831,
|
||||
-0.5926941084905061794445733628891024027949, 4.408830289125943377923077727900630927902,
|
||||
-9.8907772644920670589288081640128194231, 6.565936202082889535528455955485877361223};
|
||||
|
||||
SPECFUN_HOST_DEVICE double lanczos_sum(double x) { return ratevl(x, lanczos_num, 12, lanczos_denom, 12); }
|
||||
|
||||
SPECFUN_HOST_DEVICE double lanczos_sum_near_1(double dx) {
|
||||
double result = 0;
|
||||
unsigned k;
|
||||
|
||||
for (k = 1; k <= 12; ++k) {
|
||||
result += (-lanczos_sum_near_1_d[k - 1] * dx) / (k * dx + k * k);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE double lanczos_sum_near_2(double dx) {
|
||||
double result = 0;
|
||||
double x = dx + 2;
|
||||
unsigned k;
|
||||
|
||||
for (k = 1; k <= 12; ++k) {
|
||||
result += (-lanczos_sum_near_2_d[k - 1] * dx) / (x + k * x + k * k - 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
constexpr double lanczos_g = 6.024680040776729583740234375;
|
||||
SPECFUN_HOST_DEVICE double lanczos_sum_expg_scaled(double x) {
|
||||
return ratevl(x, detail::lanczos_sum_expg_scaled_num, 12, detail::lanczos_sum_expg_scaled_denom, 12);
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,275 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* ndtr.c
|
||||
*
|
||||
* Normal distribution function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, ndtr();
|
||||
*
|
||||
* y = ndtr( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns the area under the Gaussian probability density
|
||||
* function, integrated from minus infinity to x:
|
||||
*
|
||||
* x
|
||||
* -
|
||||
* 1 | | 2
|
||||
* ndtr(x) = --------- | exp( - t /2 ) dt
|
||||
* sqrt(2pi) | |
|
||||
* -
|
||||
* -inf.
|
||||
*
|
||||
* = ( 1 + erf(z) ) / 2
|
||||
* = erfc(z) / 2
|
||||
*
|
||||
* where z = x/sqrt(2). Computation is via the functions
|
||||
* erf and erfc.
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE -13,0 30000 3.4e-14 6.7e-15
|
||||
*
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
*
|
||||
* message condition value returned
|
||||
* erfc underflow x > 37.519379347 0.0
|
||||
*
|
||||
*/
|
||||
/* erf.c
|
||||
*
|
||||
* Error function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, erf();
|
||||
*
|
||||
* y = erf( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* The integral is
|
||||
*
|
||||
* x
|
||||
* -
|
||||
* 2 | | 2
|
||||
* erf(x) = -------- | exp( - t ) dt.
|
||||
* sqrt(pi) | |
|
||||
* -
|
||||
* 0
|
||||
*
|
||||
* For 0 <= |x| < 1, erf(x) = x * P4(x**2)/Q5(x**2); otherwise
|
||||
* erf(x) = 1 - erfc(x).
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,1 30000 3.7e-16 1.0e-16
|
||||
*
|
||||
*/
|
||||
/* erfc.c
|
||||
*
|
||||
* Complementary error function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, erfc();
|
||||
*
|
||||
* y = erfc( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
*
|
||||
* 1 - erf(x) =
|
||||
*
|
||||
* inf.
|
||||
* -
|
||||
* 2 | | 2
|
||||
* erfc(x) = -------- | exp( - t ) dt
|
||||
* sqrt(pi) | |
|
||||
* -
|
||||
* x
|
||||
*
|
||||
*
|
||||
* For small x, erfc(x) = 1 - erf(x); otherwise rational
|
||||
* approximations are computed.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,26.6417 30000 5.7e-14 1.5e-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.2: June, 1992
|
||||
* Copyright 1984, 1987, 1988, 1992 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double ndtr_P[] = {2.46196981473530512524E-10, 5.64189564831068821977E-1, 7.46321056442269912687E0,
|
||||
4.86371970985681366614E1, 1.96520832956077098242E2, 5.26445194995477358631E2,
|
||||
9.34528527171957607540E2, 1.02755188689515710272E3, 5.57535335369399327526E2};
|
||||
|
||||
constexpr double ndtr_Q[] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
1.32281951154744992508E1, 8.67072140885989742329E1, 3.54937778887819891062E2, 9.75708501743205489753E2,
|
||||
1.82390916687909736289E3, 2.24633760818710981792E3, 1.65666309194161350182E3, 5.57535340817727675546E2};
|
||||
|
||||
constexpr double ndtr_R[] = {5.64189583547755073984E-1, 1.27536670759978104416E0, 5.01905042251180477414E0,
|
||||
6.16021097993053585195E0, 7.40974269950448939160E0, 2.97886665372100240670E0};
|
||||
|
||||
constexpr double ndtr_S[] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
2.26052863220117276590E0, 9.39603524938001434673E0, 1.20489539808096656605E1,
|
||||
1.70814450747565897222E1, 9.60896809063285878198E0, 3.36907645100081516050E0};
|
||||
|
||||
constexpr double ndtr_T[] = {9.60497373987051638749E0, 9.00260197203842689217E1, 2.23200534594684319226E3,
|
||||
7.00332514112805075473E3, 5.55923013010394962768E4};
|
||||
|
||||
constexpr double ndtr_U[] = {
|
||||
/* 1.00000000000000000000E0, */
|
||||
3.35617141647503099647E1, 5.21357949780152679795E2, 4.59432382970980127987E3, 2.26290000613890934246E4,
|
||||
4.92673942608635921086E4};
|
||||
|
||||
constexpr double ndtri_UTHRESH = 37.519379347;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double erf(double x);
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double erfc(double a) {
|
||||
double p, q, x, y, z;
|
||||
|
||||
if (std::isnan(a)) {
|
||||
set_error("erfc", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (a < 0.0) {
|
||||
x = -a;
|
||||
} else {
|
||||
x = a;
|
||||
}
|
||||
|
||||
if (x < 1.0) {
|
||||
return 1.0 - erf(a);
|
||||
}
|
||||
|
||||
z = -a * a;
|
||||
|
||||
if (z < -detail::MAXLOG) {
|
||||
goto under;
|
||||
}
|
||||
|
||||
z = std::exp(z);
|
||||
|
||||
if (x < 8.0) {
|
||||
p = polevl(x, detail::ndtr_P, 8);
|
||||
q = p1evl(x, detail::ndtr_Q, 8);
|
||||
} else {
|
||||
p = polevl(x, detail::ndtr_R, 5);
|
||||
q = p1evl(x, detail::ndtr_S, 6);
|
||||
}
|
||||
y = (z * p) / q;
|
||||
|
||||
if (a < 0) {
|
||||
y = 2.0 - y;
|
||||
}
|
||||
|
||||
if (y != 0.0) {
|
||||
return y;
|
||||
}
|
||||
|
||||
under:
|
||||
set_error("erfc", SF_ERROR_UNDERFLOW, NULL);
|
||||
if (a < 0) {
|
||||
return 2.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double erf(double x) {
|
||||
double y, z;
|
||||
|
||||
if (std::isnan(x)) {
|
||||
set_error("erf", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
if (x < 0.0) {
|
||||
return -erf(-x);
|
||||
}
|
||||
|
||||
if (std::abs(x) > 1.0) {
|
||||
return (1.0 - erfc(x));
|
||||
}
|
||||
z = x * x;
|
||||
|
||||
y = x * polevl(z, detail::ndtr_T, 4) / p1evl(z, detail::ndtr_U, 5);
|
||||
return y;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double ndtr(double a) {
|
||||
double x, y, z;
|
||||
|
||||
if (std::isnan(a)) {
|
||||
set_error("ndtr", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
x = a * M_SQRT1_2;
|
||||
z = std::abs(x);
|
||||
|
||||
if (z < M_SQRT1_2) {
|
||||
y = 0.5 + 0.5 * erf(x);
|
||||
} else {
|
||||
y = 0.5 * erfc(z);
|
||||
if (x > 0) {
|
||||
y = 1.0 - y;
|
||||
}
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Pochhammer symbol (a)_m = gamma(a + m) / gamma(a)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "gamma.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double is_nonpos_int(double x) {
|
||||
return x <= 0 && x == std::ceil(x) && std::abs(x) < 1e13;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double poch(double a, double m) {
|
||||
double r = 1.0;
|
||||
|
||||
/*
|
||||
* 1. Reduce magnitude of `m` to |m| < 1 by using recurrence relations.
|
||||
*
|
||||
* This may end up in over/underflow, but then the function itself either
|
||||
* diverges or goes to zero. In case the remainder goes to the opposite
|
||||
* direction, we end up returning 0*INF = NAN, which is OK.
|
||||
*/
|
||||
|
||||
/* Recurse down */
|
||||
while (m >= 1.0) {
|
||||
if (a + m == 1) {
|
||||
break;
|
||||
}
|
||||
m -= 1.0;
|
||||
r *= (a + m);
|
||||
if (!std::isfinite(r) || r == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Recurse up */
|
||||
while (m <= -1.0) {
|
||||
if (a + m == 0) {
|
||||
break;
|
||||
}
|
||||
r /= (a + m);
|
||||
m += 1.0;
|
||||
if (!std::isfinite(r) || r == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 2. Evaluate function with reduced `m`
|
||||
*
|
||||
* Now either `m` is not big, or the `r` product has over/underflown.
|
||||
* If so, the function itself does similarly.
|
||||
*/
|
||||
|
||||
if (m == 0) {
|
||||
/* Easy case */
|
||||
return r;
|
||||
} else if (a > 1e4 && std::abs(m) <= 1) {
|
||||
/* Avoid loss of precision */
|
||||
return r * std::pow(a, m) *
|
||||
(1 + m * (m - 1) / (2 * a) + m * (m - 1) * (m - 2) * (3 * m - 1) / (24 * a * a) +
|
||||
m * m * (m - 1) * (m - 1) * (m - 2) * (m - 3) / (48 * a * a * a));
|
||||
}
|
||||
|
||||
/* Check for infinity */
|
||||
if (detail::is_nonpos_int(a + m) && !detail::is_nonpos_int(a) && a + m != m) {
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
/* Check for zero */
|
||||
if (!detail::is_nonpos_int(a + m) && detail::is_nonpos_int(a)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r * std::exp(lgam(a + m) - lgam(a)) * gammasgn(a + m) * gammasgn(a);
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,167 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* polevl.c
|
||||
* p1evl.c
|
||||
*
|
||||
* Evaluate polynomial
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* int N;
|
||||
* double x, y, coef[N+1], polevl[];
|
||||
*
|
||||
* y = polevl( x, coef, N );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Evaluates polynomial of degree N:
|
||||
*
|
||||
* 2 N
|
||||
* y = C + C x + C x +...+ C x
|
||||
* 0 1 2 N
|
||||
*
|
||||
* Coefficients are stored in reverse order:
|
||||
*
|
||||
* coef[0] = C , ..., coef[N] = C .
|
||||
* N 0
|
||||
*
|
||||
* The function p1evl() assumes that c_N = 1.0 so that coefficent
|
||||
* is omitted from the array. Its calling arguments are
|
||||
* otherwise the same as polevl().
|
||||
*
|
||||
*
|
||||
* SPEED:
|
||||
*
|
||||
* In the interest of speed, there are no checks for out
|
||||
* of bounds arithmetic. This routine is used by most of
|
||||
* the functions in the library. Depending on available
|
||||
* equipment features, the user may wish to rewrite the
|
||||
* program in microcode or assembly language.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.1: December, 1988
|
||||
* Copyright 1984, 1987, 1988 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
|
||||
/* Sources:
|
||||
* [1] Holin et. al., "Polynomial and Rational Function Evaluation",
|
||||
* https://www.boost.org/doc/libs/1_61_0/libs/math/doc/html/math_toolkit/roots/rational.html
|
||||
*/
|
||||
|
||||
/* Scipy changes:
|
||||
* - 06-23-2016: add code for evaluating rational functions
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
SPECFUN_HOST_DEVICE inline double polevl(double x, const double coef[], int N) {
|
||||
double ans;
|
||||
int i;
|
||||
const double *p;
|
||||
|
||||
p = coef;
|
||||
ans = *p++;
|
||||
i = N;
|
||||
|
||||
do {
|
||||
ans = ans * x + *p++;
|
||||
} while (--i);
|
||||
|
||||
return (ans);
|
||||
}
|
||||
|
||||
/* p1evl() */
|
||||
/* N
|
||||
* Evaluate polynomial when coefficient of x is 1.0.
|
||||
* That is, C_{N} is assumed to be 1, and that coefficient
|
||||
* is not included in the input array coef.
|
||||
* coef must have length N and contain the polynomial coefficients
|
||||
* stored as
|
||||
* coef[0] = C_{N-1}
|
||||
* coef[1] = C_{N-2}
|
||||
* ...
|
||||
* coef[N-2] = C_1
|
||||
* coef[N-1] = C_0
|
||||
* Otherwise same as polevl.
|
||||
*/
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double p1evl(double x, const double coef[], int N) {
|
||||
double ans;
|
||||
const double *p;
|
||||
int i;
|
||||
|
||||
p = coef;
|
||||
ans = x + *p++;
|
||||
i = N - 1;
|
||||
|
||||
do
|
||||
ans = ans * x + *p++;
|
||||
while (--i);
|
||||
|
||||
return (ans);
|
||||
}
|
||||
|
||||
/* Evaluate a rational function. See [1]. */
|
||||
|
||||
/* The function ratevl is only used once in cephes/lanczos.h. */
|
||||
SPECFUN_HOST_DEVICE inline double ratevl(double x, const double num[], int M, const double denom[], int N) {
|
||||
int i, dir;
|
||||
double y, num_ans, denom_ans;
|
||||
double absx = std::abs(x);
|
||||
const double *p;
|
||||
|
||||
if (absx > 1) {
|
||||
/* Evaluate as a polynomial in 1/x. */
|
||||
dir = -1;
|
||||
p = num + M;
|
||||
y = 1 / x;
|
||||
} else {
|
||||
dir = 1;
|
||||
p = num;
|
||||
y = x;
|
||||
}
|
||||
|
||||
/* Evaluate the numerator */
|
||||
num_ans = *p;
|
||||
p += dir;
|
||||
for (i = 1; i <= M; i++) {
|
||||
num_ans = num_ans * y + *p;
|
||||
p += dir;
|
||||
}
|
||||
|
||||
/* Evaluate the denominator */
|
||||
if (absx > 1) {
|
||||
p = denom + N;
|
||||
} else {
|
||||
p = denom;
|
||||
}
|
||||
|
||||
denom_ans = *p;
|
||||
p += dir;
|
||||
for (i = 1; i <= N; i++) {
|
||||
denom_ans = denom_ans * y + *p;
|
||||
p += dir;
|
||||
}
|
||||
|
||||
if (absx > 1) {
|
||||
i = M - N;
|
||||
return std::pow(x, i) * num_ans / denom_ans;
|
||||
} else {
|
||||
return num_ans / denom_ans;
|
||||
}
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,194 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* psi.c
|
||||
*
|
||||
* Psi (digamma) function
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, psi();
|
||||
*
|
||||
* y = psi( x );
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* d -
|
||||
* psi(x) = -- ln | (x)
|
||||
* dx
|
||||
*
|
||||
* is the logarithmic derivative of the gamma function.
|
||||
* For integer x,
|
||||
* n-1
|
||||
* -
|
||||
* psi(n) = -EUL + > 1/k.
|
||||
* -
|
||||
* k=1
|
||||
*
|
||||
* This formula is used for 0 < n <= 10. If x is negative, it
|
||||
* is transformed to a positive argument by the reflection
|
||||
* formula psi(1-x) = psi(x) + pi cot(pi x).
|
||||
* For general positive x, the argument is made greater than 10
|
||||
* using the recurrence psi(x+1) = psi(x) + 1/x.
|
||||
* Then the following asymptotic expansion is applied:
|
||||
*
|
||||
* inf. B
|
||||
* - 2k
|
||||
* psi(x) = log(x) - 1/2x - > -------
|
||||
* - 2k
|
||||
* k=1 2k x
|
||||
*
|
||||
* where the B2k are Bernoulli numbers.
|
||||
*
|
||||
* ACCURACY:
|
||||
* Relative error (except absolute when |psi| < 1):
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE 0,30 30000 1.3e-15 1.4e-16
|
||||
* IEEE -30,0 40000 1.5e-15 2.2e-16
|
||||
*
|
||||
* ERROR MESSAGES:
|
||||
* message condition value returned
|
||||
* psi singularity x integer <=0 INFINITY
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1992, 2000 by Stephen L. Moshier
|
||||
*/
|
||||
|
||||
/*
|
||||
* Code for the rational approximation on [1, 2] is:
|
||||
*
|
||||
* (C) Copyright John Maddock 2006.
|
||||
* Use, modification and distribution are subject to the
|
||||
* Boost Software License, Version 1.0. (See accompanying file
|
||||
* LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
#include "const.h"
|
||||
#include "polevl.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
namespace detail {
|
||||
constexpr double psi_A[] = {8.33333333333333333333E-2, -2.10927960927960927961E-2, 7.57575757575757575758E-3,
|
||||
-4.16666666666666666667E-3, 3.96825396825396825397E-3, -8.33333333333333333333E-3,
|
||||
8.33333333333333333333E-2};
|
||||
|
||||
constexpr float psi_Y = 0.99558162689208984f;
|
||||
|
||||
constexpr double psi_root1 = 1569415565.0 / 1073741824.0;
|
||||
constexpr double psi_root2 = (381566830.0 / 1073741824.0) / 1073741824.0;
|
||||
constexpr double psi_root3 = 0.9016312093258695918615325266959189453125e-19;
|
||||
|
||||
constexpr double psi_P[] = {-0.0020713321167745952, -0.045251321448739056, -0.28919126444774784,
|
||||
-0.65031853770896507, -0.32555031186804491, 0.25479851061131551};
|
||||
constexpr double psi_Q[] = {-0.55789841321675513e-6,
|
||||
0.0021284987017821144,
|
||||
0.054151797245674225,
|
||||
0.43593529692665969,
|
||||
1.4606242909763515,
|
||||
2.0767117023730469,
|
||||
1.0};
|
||||
|
||||
SPECFUN_HOST_DEVICE double digamma_imp_1_2(double x) {
|
||||
/*
|
||||
* Rational approximation on [1, 2] taken from Boost.
|
||||
*
|
||||
* Now for the approximation, we use the form:
|
||||
*
|
||||
* digamma(x) = (x - root) * (Y + R(x-1))
|
||||
*
|
||||
* Where root is the location of the positive root of digamma,
|
||||
* Y is a constant, and R is optimised for low absolute error
|
||||
* compared to Y.
|
||||
*
|
||||
* Maximum Deviation Found: 1.466e-18
|
||||
* At double precision, max error found: 2.452e-17
|
||||
*/
|
||||
double r, g;
|
||||
|
||||
g = x - psi_root1;
|
||||
g -= psi_root2;
|
||||
g -= psi_root3;
|
||||
r = special::cephes::polevl(x - 1.0, psi_P, 5) / special::cephes::polevl(x - 1.0, psi_Q, 6);
|
||||
|
||||
return g * psi_Y + g * r;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE double psi_asy(double x) {
|
||||
double y, z;
|
||||
|
||||
if (x < 1.0e17) {
|
||||
z = 1.0 / (x * x);
|
||||
y = z * special::cephes::polevl(z, psi_A, 6);
|
||||
} else {
|
||||
y = 0.0;
|
||||
}
|
||||
|
||||
return std::log(x) - (0.5 / x) - y;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE double psi(double x) {
|
||||
double y = 0.0;
|
||||
double q, r;
|
||||
int i, n;
|
||||
|
||||
if (std::isnan(x)) {
|
||||
return x;
|
||||
} else if (x == std::numeric_limits<double>::infinity()) {
|
||||
return x;
|
||||
} else if (x == -std::numeric_limits<double>::infinity()) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
} else if (x == 0) {
|
||||
set_error("psi", SF_ERROR_SINGULAR, NULL);
|
||||
return std::copysign(std::numeric_limits<double>::infinity(), -x);
|
||||
} else if (x < 0.0) {
|
||||
/* argument reduction before evaluating tan(pi * x) */
|
||||
r = std::modf(x, &q);
|
||||
if (r == 0.0) {
|
||||
set_error("psi", SF_ERROR_SINGULAR, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
y = -M_PI / std::tan(M_PI * r);
|
||||
x = 1.0 - x;
|
||||
}
|
||||
|
||||
/* check for positive integer up to 10 */
|
||||
if ((x <= 10.0) && (x == std::floor(x))) {
|
||||
n = static_cast<int>(x);
|
||||
for (i = 1; i < n; i++) {
|
||||
y += 1.0 / i;
|
||||
}
|
||||
y -= detail::SCIPY_EULER;
|
||||
return y;
|
||||
}
|
||||
|
||||
/* use the recurrence relation to move x into [1, 2] */
|
||||
if (x < 1.0) {
|
||||
y -= 1.0 / x;
|
||||
x += 1.0;
|
||||
} else if (x < 10.0) {
|
||||
while (x > 2.0) {
|
||||
x -= 1.0;
|
||||
y += 1.0 / x;
|
||||
}
|
||||
}
|
||||
if ((1.0 <= x) && (x <= 2.0)) {
|
||||
y += detail::digamma_imp_1_2(x);
|
||||
return y;
|
||||
}
|
||||
|
||||
/* x is large, use the asymptotic series */
|
||||
y += detail::psi_asy(x);
|
||||
return y;
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,125 @@
|
||||
/* rgamma.c
|
||||
*
|
||||
* Reciprocal Gamma function
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, y, rgamma();
|
||||
*
|
||||
* y = rgamma( x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns one divided by the Gamma function of the argument.
|
||||
*
|
||||
* The function is approximated by a Chebyshev expansion in
|
||||
* the interval [0,1]. Range reduction is by recurrence
|
||||
* for arguments between -34.034 and +34.84425627277176174.
|
||||
* 0 is returned for positive arguments outside this
|
||||
* range. For arguments less than -34.034 the cosecant
|
||||
* reflection formula is applied; lograrithms are employed
|
||||
* to avoid unnecessary overflow.
|
||||
*
|
||||
* The reciprocal Gamma function has no singularities,
|
||||
* but overflow and underflow may occur for large arguments.
|
||||
* These conditions return either INFINITY or 0 with
|
||||
* appropriate sign.
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE -30,+30 30000 1.1e-15 2.0e-16
|
||||
* For arguments less than -34.034 the peak error is on the
|
||||
* order of 5e-15 (DEC), excepting overflow or underflow.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.0: April, 1987
|
||||
* Copyright 1985, 1987 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
#include "chbevl.h"
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
#include "trig.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* Chebyshev coefficients for reciprocal Gamma function
|
||||
* in interval 0 to 1. Function is 1/(x Gamma(x)) - 1
|
||||
*/
|
||||
|
||||
constexpr double rgamma_R[] = {
|
||||
3.13173458231230000000E-17, -6.70718606477908000000E-16, 2.20039078172259550000E-15,
|
||||
2.47691630348254132600E-13, -6.60074100411295197440E-12, 5.13850186324226978840E-11,
|
||||
1.08965386454418662084E-9, -3.33964630686836942556E-8, 2.68975996440595483619E-7,
|
||||
2.96001177518801696639E-6, -8.04814124978471142852E-5, 4.16609138709688864714E-4,
|
||||
5.06579864028608725080E-3, -6.41925436109158228810E-2, -4.98558728684003594785E-3,
|
||||
1.27546015610523951063E-1};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE double rgamma(double x) {
|
||||
double w, y, z;
|
||||
int sign;
|
||||
|
||||
if (x > 34.84425627277176174) {
|
||||
return std::exp(-lgam(x));
|
||||
}
|
||||
if (x < -34.034) {
|
||||
w = -x;
|
||||
z = sinpi(w);
|
||||
if (z == 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
if (z < 0.0) {
|
||||
sign = 1;
|
||||
z = -z;
|
||||
} else {
|
||||
sign = -1;
|
||||
}
|
||||
|
||||
y = std::log(w * z) - std::log(M_PI) + lgam(w);
|
||||
if (y < -detail::MAXLOG) {
|
||||
set_error("rgamma", SF_ERROR_UNDERFLOW, NULL);
|
||||
return (sign * 0.0);
|
||||
}
|
||||
if (y > detail::MAXLOG) {
|
||||
set_error("rgamma", SF_ERROR_OVERFLOW, NULL);
|
||||
return (sign * std::numeric_limits<double>::infinity());
|
||||
}
|
||||
return (sign * std::exp(y));
|
||||
}
|
||||
z = 1.0;
|
||||
w = x;
|
||||
|
||||
while (w > 1.0) { /* Downward recurrence */
|
||||
w -= 1.0;
|
||||
z *= w;
|
||||
}
|
||||
while (w < 0.0) { /* Upward recurrence */
|
||||
z /= w;
|
||||
w += 1.0;
|
||||
}
|
||||
if (w == 0.0) /* Nonpositive integer */
|
||||
return (0.0);
|
||||
if (w == 1.0) /* Other integer */
|
||||
return (1.0 / z);
|
||||
|
||||
y = w * (1.0 + chbevl(4.0 * w - 2.0, detail::rgamma_R, 16)) / z;
|
||||
return (y);
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,811 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* iv.c
|
||||
*
|
||||
* Modified Bessel function of noninteger order
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double v, x, y, iv();
|
||||
*
|
||||
* y = iv( v, x );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns modified Bessel function of order v of the
|
||||
* argument. If x is negative, v must be integer valued.
|
||||
*
|
||||
*/
|
||||
/* iv.c */
|
||||
/* Modified Bessel function of noninteger order */
|
||||
/* If x < 0, then v must be an integer. */
|
||||
|
||||
/*
|
||||
* Parts of the code are copyright:
|
||||
*
|
||||
* Cephes Math Library Release 2.8: June, 2000
|
||||
* Copyright 1984, 1987, 1988, 2000 by Stephen L. Moshier
|
||||
*
|
||||
* And other parts:
|
||||
*
|
||||
* Copyright (c) 2006 Xiaogang Zhang
|
||||
* Use, modification and distribution are subject to the
|
||||
* Boost Software License, Version 1.0.
|
||||
*
|
||||
* Boost Software License - Version 1.0 - August 17th, 2003
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person or
|
||||
* organization obtaining a copy of the software and accompanying
|
||||
* documentation covered by this license (the "Software") to use, reproduce,
|
||||
* display, distribute, execute, and transmit the Software, and to prepare
|
||||
* derivative works of the Software, and to permit third-parties to whom the
|
||||
* Software is furnished to do so, all subject to the following:
|
||||
*
|
||||
* The copyright notices in the Software and this entire statement,
|
||||
* including the above license grant, this restriction and the following
|
||||
* disclaimer, must be included in all copies of the Software, in whole or
|
||||
* in part, and all derivative works of the Software, unless such copies or
|
||||
* derivative works are solely in the form of machine-executable object code
|
||||
* generated by a source language processor.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
|
||||
* NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
|
||||
* DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* And the rest are:
|
||||
*
|
||||
* Copyright (C) 2009 Pauli Virtanen
|
||||
* Distributed under the same license as Scipy.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
#include "trig.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Compute Iv from (AMS5 9.7.1), asymptotic expansion for large |z|
|
||||
* Iv ~ exp(x)/sqrt(2 pi x) ( 1 + (4*v*v-1)/8x + (4*v*v-1)(4*v*v-9)/8x/2! + ...)
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline double iv_asymptotic(double v, double x) {
|
||||
double mu;
|
||||
double sum, term, prefactor, factor;
|
||||
int k;
|
||||
|
||||
prefactor = std::exp(x) / std::sqrt(2 * M_PI * x);
|
||||
|
||||
if (prefactor == std::numeric_limits<double>::infinity()) {
|
||||
return prefactor;
|
||||
}
|
||||
|
||||
mu = 4 * v * v;
|
||||
sum = 1.0;
|
||||
term = 1.0;
|
||||
k = 1;
|
||||
|
||||
do {
|
||||
factor = (mu - (2 * k - 1) * (2 * k - 1)) / (8 * x) / k;
|
||||
if (k > 100) {
|
||||
/* didn't converge */
|
||||
set_error("iv(iv_asymptotic)", SF_ERROR_NO_RESULT, NULL);
|
||||
break;
|
||||
}
|
||||
term *= -factor;
|
||||
sum += term;
|
||||
++k;
|
||||
} while (std::abs(term) > MACHEP * std::abs(sum));
|
||||
return sum * prefactor;
|
||||
}
|
||||
|
||||
/*
|
||||
* Uniform asymptotic expansion factors, (AMS5 9.3.9; AMS5 9.3.10)
|
||||
*
|
||||
* Computed with:
|
||||
* --------------------
|
||||
import numpy as np
|
||||
t = np.poly1d([1,0])
|
||||
def up1(p):
|
||||
return .5*t*t*(1-t*t)*p.deriv() + 1/8. * ((1-5*t*t)*p).integ()
|
||||
us = [np.poly1d([1])]
|
||||
for k in range(10):
|
||||
us.append(up1(us[-1]))
|
||||
n = us[-1].order
|
||||
for p in us:
|
||||
print "{" + ", ".join(["0"]*(n-p.order) + map(repr, p)) + "},"
|
||||
print "N_UFACTORS", len(us)
|
||||
print "N_UFACTOR_TERMS", us[-1].order + 1
|
||||
* --------------------
|
||||
*/
|
||||
constexpr int iv_N_UFACTORS = 11;
|
||||
constexpr int iv_N_UFACTOR_TERMS = 31;
|
||||
|
||||
constexpr double iv_asymptotic_ufactors[iv_N_UFACTORS][iv_N_UFACTOR_TERMS] = {
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.20833333333333334,
|
||||
0.0, 0.125, 0.0},
|
||||
{0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.3342013888888889,
|
||||
0.0,
|
||||
-0.40104166666666669,
|
||||
0.0,
|
||||
0.0703125,
|
||||
0.0,
|
||||
0.0},
|
||||
{0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, -1.0258125964506173,
|
||||
0.0, 1.8464626736111112,
|
||||
0.0, -0.89121093750000002,
|
||||
0.0, 0.0732421875,
|
||||
0.0, 0.0,
|
||||
0.0},
|
||||
{0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
4.6695844234262474,
|
||||
0.0,
|
||||
-11.207002616222995,
|
||||
0.0,
|
||||
8.78912353515625,
|
||||
0.0,
|
||||
-2.3640869140624998,
|
||||
0.0,
|
||||
0.112152099609375,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0},
|
||||
{0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, -28.212072558200244,
|
||||
0.0, 84.636217674600744,
|
||||
0.0, -91.818241543240035,
|
||||
0.0, 42.534998745388457,
|
||||
0.0, -7.3687943594796312,
|
||||
0.0, 0.22710800170898438,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0},
|
||||
{0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
212.5701300392171,
|
||||
0.0,
|
||||
-765.25246814118157,
|
||||
0.0,
|
||||
1059.9904525279999,
|
||||
0.0,
|
||||
-699.57962737613275,
|
||||
0.0,
|
||||
218.19051174421159,
|
||||
0.0,
|
||||
-26.491430486951554,
|
||||
0.0,
|
||||
0.57250142097473145,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0},
|
||||
{0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, -1919.4576623184068,
|
||||
0.0, 8061.7221817373083,
|
||||
0.0, -13586.550006434136,
|
||||
0.0, 11655.393336864536,
|
||||
0.0, -5305.6469786134048,
|
||||
0.0, 1200.9029132163525,
|
||||
0.0, -108.09091978839464,
|
||||
0.0, 1.7277275025844574,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0},
|
||||
{0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
20204.291330966149,
|
||||
0.0,
|
||||
-96980.598388637503,
|
||||
0.0,
|
||||
192547.0012325315,
|
||||
0.0,
|
||||
-203400.17728041555,
|
||||
0.0,
|
||||
122200.46498301747,
|
||||
0.0,
|
||||
-41192.654968897557,
|
||||
0.0,
|
||||
7109.5143024893641,
|
||||
0.0,
|
||||
-493.915304773088,
|
||||
0.0,
|
||||
6.074042001273483,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0},
|
||||
{0, 0,
|
||||
0, -242919.18790055133,
|
||||
0.0, 1311763.6146629769,
|
||||
0.0, -2998015.9185381061,
|
||||
0.0, 3763271.2976564039,
|
||||
0.0, -2813563.2265865342,
|
||||
0.0, 1268365.2733216248,
|
||||
0.0, -331645.17248456361,
|
||||
0.0, 45218.768981362737,
|
||||
0.0, -2499.8304818112092,
|
||||
0.0, 24.380529699556064,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0},
|
||||
{3284469.8530720375,
|
||||
0.0,
|
||||
-19706819.11843222,
|
||||
0.0,
|
||||
50952602.492664628,
|
||||
0.0,
|
||||
-74105148.211532637,
|
||||
0.0,
|
||||
66344512.274729028,
|
||||
0.0,
|
||||
-37567176.660763353,
|
||||
0.0,
|
||||
13288767.166421819,
|
||||
0.0,
|
||||
-2785618.1280864552,
|
||||
0.0,
|
||||
308186.40461266245,
|
||||
0.0,
|
||||
-13886.089753717039,
|
||||
0.0,
|
||||
110.01714026924674,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0}};
|
||||
|
||||
/*
|
||||
* Compute Iv, Kv from (AMS5 9.7.7 + 9.7.8), asymptotic expansion for large v
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline void ikv_asymptotic_uniform(double v, double x, double *i_value, double *k_value) {
|
||||
double i_prefactor, k_prefactor;
|
||||
double t, t2, eta, z;
|
||||
double i_sum, k_sum, term, divisor;
|
||||
int k, n;
|
||||
int sign = 1;
|
||||
|
||||
if (v < 0) {
|
||||
/* Negative v; compute I_{-v} and K_{-v} and use (AMS 9.6.2) */
|
||||
sign = -1;
|
||||
v = -v;
|
||||
}
|
||||
|
||||
z = x / v;
|
||||
t = 1 / std::sqrt(1 + z * z);
|
||||
t2 = t * t;
|
||||
eta = std::sqrt(1 + z * z) + std::log(z / (1 + 1 / t));
|
||||
|
||||
i_prefactor = std::sqrt(t / (2 * M_PI * v)) * std::exp(v * eta);
|
||||
i_sum = 1.0;
|
||||
|
||||
k_prefactor = std::sqrt(M_PI * t / (2 * v)) * std::exp(-v * eta);
|
||||
k_sum = 1.0;
|
||||
|
||||
divisor = v;
|
||||
for (n = 1; n < iv_N_UFACTORS; ++n) {
|
||||
/*
|
||||
* Evaluate u_k(t) with Horner's scheme;
|
||||
* (using the knowledge about which coefficients are zero)
|
||||
*/
|
||||
term = 0;
|
||||
for (k = iv_N_UFACTOR_TERMS - 1 - 3 * n; k < iv_N_UFACTOR_TERMS - n; k += 2) {
|
||||
term *= t2;
|
||||
term += iv_asymptotic_ufactors[n][k];
|
||||
}
|
||||
for (k = 1; k < n; k += 2) {
|
||||
term *= t2;
|
||||
}
|
||||
if (n % 2 == 1) {
|
||||
term *= t;
|
||||
}
|
||||
|
||||
/* Sum terms */
|
||||
term /= divisor;
|
||||
i_sum += term;
|
||||
k_sum += (n % 2 == 0) ? term : -term;
|
||||
|
||||
/* Check convergence */
|
||||
if (std::abs(term) < MACHEP) {
|
||||
break;
|
||||
}
|
||||
|
||||
divisor *= v;
|
||||
}
|
||||
|
||||
if (std::abs(term) > 1e-3 * std::abs(i_sum)) {
|
||||
/* Didn't converge */
|
||||
set_error("ikv_asymptotic_uniform", SF_ERROR_NO_RESULT, NULL);
|
||||
}
|
||||
if (std::abs(term) > MACHEP * std::abs(i_sum)) {
|
||||
/* Some precision lost */
|
||||
set_error("ikv_asymptotic_uniform", SF_ERROR_LOSS, NULL);
|
||||
}
|
||||
|
||||
if (k_value != NULL) {
|
||||
/* symmetric in v */
|
||||
*k_value = k_prefactor * k_sum;
|
||||
}
|
||||
|
||||
if (i_value != NULL) {
|
||||
if (sign == 1) {
|
||||
*i_value = i_prefactor * i_sum;
|
||||
} else {
|
||||
/* (AMS 9.6.2) */
|
||||
*i_value = (i_prefactor * i_sum + (2 / M_PI) * special::cephes::sinpi(v) * k_prefactor * k_sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The following code originates from the Boost C++ library,
|
||||
* from file `boost/math/special_functions/detail/bessel_ik.hpp`,
|
||||
* converted from C++ to C.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified Bessel functions of the first and second kind of fractional order
|
||||
*
|
||||
* Calculate K(v, x) and K(v+1, x) by method analogous to
|
||||
* Temme, Journal of Computational Physics, vol 21, 343 (1976)
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline int temme_ik_series(double v, double x, double *K, double *K1) {
|
||||
double f, h, p, q, coef, sum, sum1, tolerance;
|
||||
double a, b, c, d, sigma, gamma1, gamma2;
|
||||
std::uint64_t k;
|
||||
double gp;
|
||||
double gm;
|
||||
|
||||
/*
|
||||
* |x| <= 2, Temme series converge rapidly
|
||||
* |x| > 2, the larger the |x|, the slower the convergence
|
||||
*/
|
||||
SPECFUN_ASSERT(std::abs(x) <= 2);
|
||||
SPECFUN_ASSERT(std::abs(v) <= 0.5f);
|
||||
|
||||
gp = special::cephes::Gamma(v + 1) - 1;
|
||||
gm = special::cephes::Gamma(-v + 1) - 1;
|
||||
|
||||
a = std::log(x / 2);
|
||||
b = std::exp(v * a);
|
||||
sigma = -a * v;
|
||||
c = std::abs(v) < MACHEP ? 1 : special::cephes::sinpi(v) / (v * M_PI);
|
||||
d = std::abs(sigma) < MACHEP ? 1 : std::sinh(sigma) / sigma;
|
||||
gamma1 = std::abs(v) < MACHEP ? -SCIPY_EULER : (0.5 / v) * (gp - gm) * c;
|
||||
gamma2 = (2 + gp + gm) * c / 2;
|
||||
|
||||
/* initial values */
|
||||
p = (gp + 1) / (2 * b);
|
||||
q = (1 + gm) * b / 2;
|
||||
f = (std::cosh(sigma) * gamma1 + d * (-a) * gamma2) / c;
|
||||
h = p;
|
||||
coef = 1;
|
||||
sum = coef * f;
|
||||
sum1 = coef * h;
|
||||
|
||||
/* series summation */
|
||||
tolerance = MACHEP;
|
||||
for (k = 1; k < MAXITER; k++) {
|
||||
f = (k * f + p + q) / (k * k - v * v);
|
||||
p /= k - v;
|
||||
q /= k + v;
|
||||
h = p - k * f;
|
||||
coef *= x * x / (4 * k);
|
||||
sum += coef * f;
|
||||
sum1 += coef * h;
|
||||
if (std::abs(coef * f) < std::abs(sum) * tolerance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == MAXITER) {
|
||||
set_error("ikv_temme(temme_ik_series)", SF_ERROR_NO_RESULT, NULL);
|
||||
}
|
||||
|
||||
*K = sum;
|
||||
*K1 = 2 * sum1 / x;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Evaluate continued fraction fv = I_(v+1) / I_v, derived from
|
||||
* Abramowitz and Stegun, Handbook of Mathematical Functions, 1972, 9.1.73 */
|
||||
SPECFUN_HOST_DEVICE inline int CF1_ik(double v, double x, double *fv) {
|
||||
double C, D, f, a, b, delta, tiny, tolerance;
|
||||
std::uint64_t k;
|
||||
|
||||
/*
|
||||
* |x| <= |v|, CF1_ik converges rapidly
|
||||
* |x| > |v|, CF1_ik needs O(|x|) iterations to converge
|
||||
*/
|
||||
|
||||
/*
|
||||
* modified Lentz's method, see
|
||||
* Lentz, Applied Optics, vol 15, 668 (1976)
|
||||
*/
|
||||
tolerance = 2 * MACHEP;
|
||||
tiny = 1 / std::sqrt(std::numeric_limits<double>::max());
|
||||
C = f = tiny; /* b0 = 0, replace with tiny */
|
||||
D = 0;
|
||||
for (k = 1; k < MAXITER; k++) {
|
||||
a = 1;
|
||||
b = 2 * (v + k) / x;
|
||||
C = b + a / C;
|
||||
D = b + a * D;
|
||||
if (C == 0) {
|
||||
C = tiny;
|
||||
}
|
||||
if (D == 0) {
|
||||
D = tiny;
|
||||
}
|
||||
D = 1 / D;
|
||||
delta = C * D;
|
||||
f *= delta;
|
||||
if (std::abs(delta - 1) <= tolerance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == MAXITER) {
|
||||
set_error("ikv_temme(CF1_ik)", SF_ERROR_NO_RESULT, NULL);
|
||||
}
|
||||
|
||||
*fv = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate K(v, x) and K(v+1, x) by evaluating continued fraction
|
||||
* z1 / z0 = U(v+1.5, 2v+1, 2x) / U(v+0.5, 2v+1, 2x), see
|
||||
* Thompson and Barnett, Computer Physics Communications, vol 47, 245 (1987)
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline int CF2_ik(double v, double x, double *Kv, double *Kv1) {
|
||||
|
||||
double S, C, Q, D, f, a, b, q, delta, tolerance, current, prev;
|
||||
std::uint64_t k;
|
||||
|
||||
/*
|
||||
* |x| >= |v|, CF2_ik converges rapidly
|
||||
* |x| -> 0, CF2_ik fails to converge
|
||||
*/
|
||||
|
||||
SPECFUN_ASSERT(std::abs(x) > 1);
|
||||
|
||||
/*
|
||||
* Steed's algorithm, see Thompson and Barnett,
|
||||
* Journal of Computational Physics, vol 64, 490 (1986)
|
||||
*/
|
||||
tolerance = MACHEP;
|
||||
a = v * v - 0.25;
|
||||
b = 2 * (x + 1); /* b1 */
|
||||
D = 1 / b; /* D1 = 1 / b1 */
|
||||
f = delta = D; /* f1 = delta1 = D1, coincidence */
|
||||
prev = 0; /* q0 */
|
||||
current = 1; /* q1 */
|
||||
Q = C = -a; /* Q1 = C1 because q1 = 1 */
|
||||
S = 1 + Q * delta; /* S1 */
|
||||
for (k = 2; k < MAXITER; k++) { /* starting from 2 */
|
||||
/* continued fraction f = z1 / z0 */
|
||||
a -= 2 * (k - 1);
|
||||
b += 2;
|
||||
D = 1 / (b + a * D);
|
||||
delta *= b * D - 1;
|
||||
f += delta;
|
||||
|
||||
/* series summation S = 1 + \sum_{n=1}^{\infty} C_n * z_n / z_0 */
|
||||
q = (prev - (b - 2) * current) / a;
|
||||
prev = current;
|
||||
current = q; /* forward recurrence for q */
|
||||
C *= -a / k;
|
||||
Q += C * q;
|
||||
S += Q * delta;
|
||||
|
||||
/* S converges slower than f */
|
||||
if (std::abs(Q * delta) < std::abs(S) * tolerance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == MAXITER) {
|
||||
set_error("ikv_temme(CF2_ik)", SF_ERROR_NO_RESULT, NULL);
|
||||
}
|
||||
|
||||
*Kv = std::sqrt(M_PI / (2 * x)) * std::exp(-x) / S;
|
||||
*Kv1 = *Kv * (0.5 + v + x + (v * v - 0.25) * f) / x;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flags for what to compute */
|
||||
enum { ikv_temme_need_i = 0x1, ikv_temme_need_k = 0x2 };
|
||||
|
||||
/*
|
||||
* Compute I(v, x) and K(v, x) simultaneously by Temme's method, see
|
||||
* Temme, Journal of Computational Physics, vol 19, 324 (1975)
|
||||
*/
|
||||
SPECFUN_HOST_DEVICE inline void ikv_temme(double v, double x, double *Iv_p, double *Kv_p) {
|
||||
/* Kv1 = K_(v+1), fv = I_(v+1) / I_v */
|
||||
/* Ku1 = K_(u+1), fu = I_(u+1) / I_u */
|
||||
double u, Iv, Kv, Kv1, Ku, Ku1, fv;
|
||||
double W, current, prev, next;
|
||||
int reflect = 0;
|
||||
unsigned n, k;
|
||||
int kind;
|
||||
|
||||
kind = 0;
|
||||
if (Iv_p != NULL) {
|
||||
kind |= ikv_temme_need_i;
|
||||
}
|
||||
if (Kv_p != NULL) {
|
||||
kind |= ikv_temme_need_k;
|
||||
}
|
||||
|
||||
if (v < 0) {
|
||||
reflect = 1;
|
||||
v = -v; /* v is non-negative from here */
|
||||
kind |= ikv_temme_need_k;
|
||||
}
|
||||
n = std::round(v);
|
||||
u = v - n; /* -1/2 <= u < 1/2 */
|
||||
|
||||
if (x < 0) {
|
||||
if (Iv_p != NULL)
|
||||
*Iv_p = std::numeric_limits<double>::quiet_NaN();
|
||||
if (Kv_p != NULL)
|
||||
*Kv_p = std::numeric_limits<double>::quiet_NaN();
|
||||
set_error("ikv_temme", SF_ERROR_DOMAIN, NULL);
|
||||
return;
|
||||
}
|
||||
if (x == 0) {
|
||||
Iv = (v == 0) ? 1 : 0;
|
||||
if (kind & ikv_temme_need_k) {
|
||||
set_error("ikv_temme", SF_ERROR_OVERFLOW, NULL);
|
||||
Kv = std::numeric_limits<double>::infinity();
|
||||
} else {
|
||||
Kv = std::numeric_limits<double>::quiet_NaN(); /* any value will do */
|
||||
}
|
||||
|
||||
if (reflect && (kind & ikv_temme_need_i)) {
|
||||
double z = (u + n % 2);
|
||||
|
||||
Iv = special::cephes::sinpi(z) == 0 ? Iv : std::numeric_limits<double>::infinity();
|
||||
if (std::isinf(Iv)) {
|
||||
set_error("ikv_temme", SF_ERROR_OVERFLOW, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (Iv_p != NULL) {
|
||||
*Iv_p = Iv;
|
||||
}
|
||||
if (Kv_p != NULL) {
|
||||
*Kv_p = Kv;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* x is positive until reflection */
|
||||
W = 1 / x; /* Wronskian */
|
||||
if (x <= 2) { /* x in (0, 2] */
|
||||
temme_ik_series(u, x, &Ku, &Ku1); /* Temme series */
|
||||
} else { /* x in (2, \infty) */
|
||||
CF2_ik(u, x, &Ku, &Ku1); /* continued fraction CF2_ik */
|
||||
}
|
||||
prev = Ku;
|
||||
current = Ku1;
|
||||
for (k = 1; k <= n; k++) { /* forward recurrence for K */
|
||||
next = 2 * (u + k) * current / x + prev;
|
||||
prev = current;
|
||||
current = next;
|
||||
}
|
||||
Kv = prev;
|
||||
Kv1 = current;
|
||||
if (kind & ikv_temme_need_i) {
|
||||
double lim = (4 * v * v + 10) / (8 * x);
|
||||
|
||||
lim *= lim;
|
||||
lim *= lim;
|
||||
lim /= 24;
|
||||
if ((lim < MACHEP * 10) && (x > 100)) {
|
||||
/*
|
||||
* x is huge compared to v, CF1 may be very slow
|
||||
* to converge so use asymptotic expansion for large
|
||||
* x case instead. Note that the asymptotic expansion
|
||||
* isn't very accurate - so it's deliberately very hard
|
||||
* to get here - probably we're going to overflow:
|
||||
*/
|
||||
Iv = iv_asymptotic(v, x);
|
||||
} else {
|
||||
CF1_ik(v, x, &fv); /* continued fraction CF1_ik */
|
||||
Iv = W / (Kv * fv + Kv1); /* Wronskian relation */
|
||||
}
|
||||
} else {
|
||||
Iv = std::numeric_limits<double>::quiet_NaN(); /* any value will do */
|
||||
}
|
||||
|
||||
if (reflect) {
|
||||
double z = (u + n % 2);
|
||||
|
||||
if (Iv_p != NULL) {
|
||||
*Iv_p = Iv + (2 / M_PI) * special::cephes::sinpi(z) * Kv; /* reflection formula */
|
||||
}
|
||||
if (Kv_p != NULL) {
|
||||
*Kv_p = Kv;
|
||||
}
|
||||
} else {
|
||||
if (Iv_p != NULL) {
|
||||
*Iv_p = Iv;
|
||||
}
|
||||
if (Kv_p != NULL) {
|
||||
*Kv_p = Kv;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double iv(double v, double x) {
|
||||
int sign;
|
||||
double t, ax, res;
|
||||
|
||||
if (std::isnan(v) || std::isnan(x)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
/* If v is a negative integer, invoke symmetry */
|
||||
t = std::floor(v);
|
||||
if (v < 0.0) {
|
||||
if (t == v) {
|
||||
v = -v; /* symmetry */
|
||||
t = -t;
|
||||
}
|
||||
}
|
||||
/* If x is negative, require v to be an integer */
|
||||
sign = 1;
|
||||
if (x < 0.0) {
|
||||
if (t != v) {
|
||||
set_error("iv", SF_ERROR_DOMAIN, NULL);
|
||||
return (std::numeric_limits<double>::quiet_NaN());
|
||||
}
|
||||
if (v != 2.0 * std::floor(v / 2.0)) {
|
||||
sign = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid logarithm singularity */
|
||||
if (x == 0.0) {
|
||||
if (v == 0.0) {
|
||||
return 1.0;
|
||||
}
|
||||
if (v < 0.0) {
|
||||
set_error("iv", SF_ERROR_OVERFLOW, NULL);
|
||||
return std::numeric_limits<double>::infinity();
|
||||
} else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
ax = std::abs(x);
|
||||
if (std::abs(v) > 50) {
|
||||
/*
|
||||
* Uniform asymptotic expansion for large orders.
|
||||
*
|
||||
* This appears to overflow slightly later than the Boost
|
||||
* implementation of Temme's method.
|
||||
*/
|
||||
detail::ikv_asymptotic_uniform(v, ax, &res, NULL);
|
||||
} else {
|
||||
/* Otherwise: Temme's method */
|
||||
detail::ikv_temme(v, ax, &res, NULL);
|
||||
}
|
||||
res *= sign;
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,58 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
*
|
||||
* Original author: Josh Wilson, 2020.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implement sin(pi * x) and cos(pi * x) for real x. Since the periods
|
||||
* of these functions are integral (and thus representable in double
|
||||
* precision), it's possible to compute them with greater accuracy
|
||||
* than sin(x) and cos(x).
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
/* Compute sin(pi * x). */
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE T sinpi(T x) {
|
||||
T s = 1.0;
|
||||
|
||||
if (x < 0.0) {
|
||||
x = -x;
|
||||
s = -1.0;
|
||||
}
|
||||
|
||||
T r = std::fmod(x, 2.0);
|
||||
if (r < 0.5) {
|
||||
return s * std::sin(M_PI * r);
|
||||
} else if (r > 1.5) {
|
||||
return s * std::sin(M_PI * (r - 2.0));
|
||||
} else {
|
||||
return -s * std::sin(M_PI * (r - 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute cos(pi * x) */
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE T cospi(T x) {
|
||||
if (x < 0.0) {
|
||||
x = -x;
|
||||
}
|
||||
|
||||
T r = std::fmod(x, 2.0);
|
||||
if (r == 0.5) {
|
||||
// We don't want to return -0.0
|
||||
return 0.0;
|
||||
}
|
||||
if (r < 1.0) {
|
||||
return -std::sin(M_PI * (r - 0.5));
|
||||
} else {
|
||||
return std::sin(M_PI * (r - 1.5));
|
||||
}
|
||||
}
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,186 @@
|
||||
/* Translated into C++ by SciPy developers in 2024. */
|
||||
|
||||
/* unity.c
|
||||
*
|
||||
* Relative error approximations for function arguments near
|
||||
* unity.
|
||||
*
|
||||
* log1p(x) = log(1+x)
|
||||
* expm1(x) = exp(x) - 1
|
||||
* cosm1(x) = cos(x) - 1
|
||||
* lgam1p(x) = lgam(1+x)
|
||||
*
|
||||
*/
|
||||
|
||||
/* Scipy changes:
|
||||
* - 06-10-2016: added lgam1p
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include "const.h"
|
||||
#include "gamma.h"
|
||||
#include "polevl.h"
|
||||
#include "zeta.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* log1p(x) = log(1 + x) */
|
||||
|
||||
/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
|
||||
* 1/sqrt(2) <= x < sqrt(2)
|
||||
* Theoretical peak relative error = 2.32e-20
|
||||
*/
|
||||
|
||||
constexpr double unity_LP[] = {
|
||||
4.5270000862445199635215E-5, 4.9854102823193375972212E-1, 6.5787325942061044846969E0,
|
||||
2.9911919328553073277375E1, 6.0949667980987787057556E1, 5.7112963590585538103336E1,
|
||||
2.0039553499201281259648E1,
|
||||
};
|
||||
|
||||
constexpr double unity_LQ[] = {
|
||||
/* 1.0000000000000000000000E0, */
|
||||
1.5062909083469192043167E1, 8.3047565967967209469434E1, 2.2176239823732856465394E2,
|
||||
3.0909872225312059774938E2, 2.1642788614495947685003E2, 6.0118660497603843919306E1,
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double log1p(double x) {
|
||||
double z;
|
||||
|
||||
z = 1.0 + x;
|
||||
if ((z < M_SQRT1_2) || (z > M_SQRT2))
|
||||
return (std::log(z));
|
||||
z = x * x;
|
||||
z = -0.5 * z + x * (z * polevl(x, detail::unity_LP, 6) / p1evl(x, detail::unity_LQ, 6));
|
||||
return (x + z);
|
||||
}
|
||||
|
||||
/* log(1 + x) - x */
|
||||
SPECFUN_HOST_DEVICE inline double log1pmx(double x) {
|
||||
if (std::abs(x) < 0.5) {
|
||||
uint64_t n;
|
||||
double xfac = x;
|
||||
double term;
|
||||
double res = 0;
|
||||
|
||||
for (n = 2; n < detail::MAXITER; n++) {
|
||||
xfac *= -x;
|
||||
term = xfac / n;
|
||||
res += term;
|
||||
if (std::abs(term) < detail::MACHEP * std::abs(res)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return log1p(x) - x;
|
||||
}
|
||||
}
|
||||
|
||||
/* expm1(x) = exp(x) - 1 */
|
||||
|
||||
/* e^x = 1 + 2x P(x^2)/( Q(x^2) - P(x^2) )
|
||||
* -0.5 <= x <= 0.5
|
||||
*/
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double unity_EP[3] = {
|
||||
1.2617719307481059087798E-4,
|
||||
3.0299440770744196129956E-2,
|
||||
9.9999999999999999991025E-1,
|
||||
};
|
||||
|
||||
constexpr double unity_EQ[4] = {
|
||||
3.0019850513866445504159E-6,
|
||||
2.5244834034968410419224E-3,
|
||||
2.2726554820815502876593E-1,
|
||||
2.0000000000000000000897E0,
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double expm1(double x) {
|
||||
double r, xx;
|
||||
|
||||
if (!std::isfinite(x)) {
|
||||
if (std::isnan(x)) {
|
||||
return x;
|
||||
} else if (x > 0) {
|
||||
return x;
|
||||
} else {
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
if ((x < -0.5) || (x > 0.5))
|
||||
return (std::exp(x) - 1.0);
|
||||
xx = x * x;
|
||||
r = x * polevl(xx, detail::unity_EP, 2);
|
||||
r = r / (polevl(xx, detail::unity_EQ, 3) - r);
|
||||
return (r + r);
|
||||
}
|
||||
|
||||
/* cosm1(x) = cos(x) - 1 */
|
||||
|
||||
namespace detail {
|
||||
constexpr double unity_coscof[7] = {
|
||||
4.7377507964246204691685E-14, -1.1470284843425359765671E-11, 2.0876754287081521758361E-9,
|
||||
-2.7557319214999787979814E-7, 2.4801587301570552304991E-5, -1.3888888888888872993737E-3,
|
||||
4.1666666666666666609054E-2,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double cosm1(double x) {
|
||||
double xx;
|
||||
|
||||
if ((x < -M_PI_4) || (x > M_PI_4))
|
||||
return (std::cos(x) - 1.0);
|
||||
xx = x * x;
|
||||
xx = -0.5 * xx + xx * xx * polevl(xx, detail::unity_coscof, 6);
|
||||
return xx;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
/* Compute lgam(x + 1) around x = 0 using its Taylor series. */
|
||||
SPECFUN_HOST_DEVICE inline double lgam1p_taylor(double x) {
|
||||
int n;
|
||||
double xfac, coeff, res;
|
||||
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
}
|
||||
res = -SCIPY_EULER * x;
|
||||
xfac = -x;
|
||||
for (n = 2; n < 42; n++) {
|
||||
xfac *= -x;
|
||||
coeff = special::cephes::zeta(n, 1) * xfac / n;
|
||||
res += coeff;
|
||||
if (std::abs(coeff) < detail::MACHEP * std::abs(res)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/* Compute lgam(x + 1). */
|
||||
SPECFUN_HOST_DEVICE inline double lgam1p(double x) {
|
||||
if (std::abs(x) <= 0.5) {
|
||||
return detail::lgam1p_taylor(x);
|
||||
} else if (std::abs(x - 1) < 0.5) {
|
||||
return std::log(x) + detail::lgam1p_taylor(x - 1);
|
||||
} else {
|
||||
return lgam(x + 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
@ -0,0 +1,172 @@
|
||||
/* Translated into C++ by SciPy developers in 2024.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* zeta.c
|
||||
*
|
||||
* Riemann zeta function of two arguments
|
||||
*
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* double x, q, y, zeta();
|
||||
*
|
||||
* y = zeta( x, q );
|
||||
*
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
*
|
||||
*
|
||||
* inf.
|
||||
* - -x
|
||||
* zeta(x,q) = > (k+q)
|
||||
* -
|
||||
* k=0
|
||||
*
|
||||
* where x > 1 and q is not a negative integer or zero.
|
||||
* The Euler-Maclaurin summation formula is used to obtain
|
||||
* the expansion
|
||||
*
|
||||
* n
|
||||
* - -x
|
||||
* zeta(x,q) = > (k+q)
|
||||
* -
|
||||
* k=1
|
||||
*
|
||||
* 1-x inf. B x(x+1)...(x+2j)
|
||||
* (n+q) 1 - 2j
|
||||
* + --------- - ------- + > --------------------
|
||||
* x-1 x - x+2j+1
|
||||
* 2(n+q) j=1 (2j)! (n+q)
|
||||
*
|
||||
* where the B2j are Bernoulli numbers. Note that (see zetac.c)
|
||||
* zeta(x,1) = zetac(x) + 1.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
*
|
||||
*
|
||||
* REFERENCE:
|
||||
*
|
||||
* Gradshteyn, I. S., and I. M. Ryzhik, Tables of Integrals,
|
||||
* Series, and Products, p. 1073; Academic Press, 1980.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cephes Math Library Release 2.0: April, 1987
|
||||
* Copyright 1984, 1987 by Stephen L. Moshier
|
||||
* Direct inquiries to 30 Frost Street, Cambridge, MA 02140
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "../error.h"
|
||||
#include "const.h"
|
||||
|
||||
namespace special {
|
||||
namespace cephes {
|
||||
|
||||
namespace detail {
|
||||
/* Expansion coefficients
|
||||
* for Euler-Maclaurin summation formula
|
||||
* (2k)! / B2k
|
||||
* where B2k are Bernoulli numbers
|
||||
*/
|
||||
constexpr double zeta_A[] = {
|
||||
12.0,
|
||||
-720.0,
|
||||
30240.0,
|
||||
-1209600.0,
|
||||
47900160.0,
|
||||
-1.8924375803183791606e9, /*1.307674368e12/691 */
|
||||
7.47242496e10,
|
||||
-2.950130727918164224e12, /*1.067062284288e16/3617 */
|
||||
1.1646782814350067249e14, /*5.109094217170944e18/43867 */
|
||||
-4.5979787224074726105e15, /*8.028576626982912e20/174611 */
|
||||
1.8152105401943546773e17, /*1.5511210043330985984e23/854513 */
|
||||
-7.1661652561756670113e18 /*1.6938241367317436694528e27/236364091 */
|
||||
};
|
||||
|
||||
/* 30 Nov 86 -- error in third coefficient fixed */
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE double inline zeta(double x, double q) {
|
||||
int i;
|
||||
double a, b, k, s, t, w;
|
||||
|
||||
if (x == 1.0)
|
||||
goto retinf;
|
||||
|
||||
if (x < 1.0) {
|
||||
domerr:
|
||||
set_error("zeta", SF_ERROR_DOMAIN, NULL);
|
||||
return (std::numeric_limits<double>::quiet_NaN());
|
||||
}
|
||||
|
||||
if (q <= 0.0) {
|
||||
if (q == floor(q)) {
|
||||
set_error("zeta", SF_ERROR_SINGULAR, NULL);
|
||||
retinf:
|
||||
return (std::numeric_limits<double>::infinity());
|
||||
}
|
||||
if (x != std::floor(x))
|
||||
goto domerr; /* because q^-x not defined */
|
||||
}
|
||||
|
||||
/* Asymptotic expansion
|
||||
* https://dlmf.nist.gov/25.11#E43
|
||||
*/
|
||||
if (q > 1e8) {
|
||||
return (1 / (x - 1) + 1 / (2 * q)) * std::pow(q, 1 - x);
|
||||
}
|
||||
|
||||
/* Euler-Maclaurin summation formula */
|
||||
|
||||
/* Permit negative q but continue sum until n+q > +9 .
|
||||
* This case should be handled by a reflection formula.
|
||||
* If q<0 and x is an integer, there is a relation to
|
||||
* the polyGamma function.
|
||||
*/
|
||||
s = std::pow(q, -x);
|
||||
a = q;
|
||||
i = 0;
|
||||
b = 0.0;
|
||||
while ((i < 9) || (a <= 9.0)) {
|
||||
i += 1;
|
||||
a += 1.0;
|
||||
b = std::pow(a, -x);
|
||||
s += b;
|
||||
if (std::abs(b / s) < detail::MACHEP)
|
||||
goto done;
|
||||
}
|
||||
|
||||
w = a;
|
||||
s += b * w / (x - 1.0);
|
||||
s -= 0.5 * b;
|
||||
a = 1.0;
|
||||
k = 0.0;
|
||||
for (i = 0; i < 12; i++) {
|
||||
a *= x + k;
|
||||
b /= w;
|
||||
t = a * b / detail::zeta_A[i];
|
||||
s = s + t;
|
||||
t = std::abs(t / s);
|
||||
if (t < detail::MACHEP)
|
||||
goto done;
|
||||
k += 1.0;
|
||||
a *= x + k;
|
||||
b /= w;
|
||||
k += 1.0;
|
||||
}
|
||||
done:
|
||||
return (s);
|
||||
}
|
||||
|
||||
} // namespace cephes
|
||||
} // namespace special
|
||||
226
venv/lib/python3.12/site-packages/scipy/special/special/config.h
Normal file
226
venv/lib/python3.12/site-packages/scipy/special/special/config.h
Normal file
@ -0,0 +1,226 @@
|
||||
#pragma once
|
||||
|
||||
// Define math constants if they are not available
|
||||
#ifndef M_E
|
||||
#define M_E 2.71828182845904523536
|
||||
#endif
|
||||
|
||||
#ifndef M_LOG2E
|
||||
#define M_LOG2E 1.44269504088896340736
|
||||
#endif
|
||||
|
||||
#ifndef M_LOG10E
|
||||
#define M_LOG10E 0.434294481903251827651
|
||||
#endif
|
||||
|
||||
#ifndef M_LN2
|
||||
#define M_LN2 0.693147180559945309417
|
||||
#endif
|
||||
|
||||
#ifndef M_LN10
|
||||
#define M_LN10 2.30258509299404568402
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#ifndef M_PI_2
|
||||
#define M_PI_2 1.57079632679489661923
|
||||
#endif
|
||||
|
||||
#ifndef M_PI_4
|
||||
#define M_PI_4 0.785398163397448309616
|
||||
#endif
|
||||
|
||||
#ifndef M_1_PI
|
||||
#define M_1_PI 0.318309886183790671538
|
||||
#endif
|
||||
|
||||
#ifndef M_2_PI
|
||||
#define M_2_PI 0.636619772367581343076
|
||||
#endif
|
||||
|
||||
#ifndef M_2_SQRTPI
|
||||
#define M_2_SQRTPI 1.12837916709551257390
|
||||
#endif
|
||||
|
||||
#ifndef M_SQRT2
|
||||
#define M_SQRT2 1.41421356237309504880
|
||||
#endif
|
||||
|
||||
#ifndef M_SQRT1_2
|
||||
#define M_SQRT1_2 0.707106781186547524401
|
||||
#endif
|
||||
|
||||
#ifdef __CUDACC__
|
||||
#define SPECFUN_HOST_DEVICE __host__ __device__
|
||||
|
||||
#include <cuda/std/algorithm>
|
||||
#include <cuda/std/cmath>
|
||||
#include <cuda/std/cstdint>
|
||||
#include <cuda/std/limits>
|
||||
#include <cuda/std/type_traits>
|
||||
|
||||
// Fallback to global namespace for functions unsupported on NVRTC Jit
|
||||
#ifdef _LIBCUDACXX_COMPILER_NVRTC
|
||||
#include <cuda_runtime.h>
|
||||
#endif
|
||||
|
||||
namespace std {
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double abs(double num) { return cuda::std::abs(num); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double exp(double num) { return cuda::std::exp(num); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double log(double num) { return cuda::std::log(num); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double sqrt(double num) { return cuda::std::sqrt(num); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline bool isinf(double num) { return cuda::std::isinf(num); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline bool isnan(double num) { return cuda::std::isnan(num); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline bool isfinite(double num) { return cuda::std::isfinite(num); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double pow(double x, double y) { return cuda::std::pow(x, y); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double sin(double x) { return cuda::std::sin(x); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double cos(double x) { return cuda::std::cos(x); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double tan(double x) { return cuda::std::tan(x); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double atan(double x) { return cuda::std::atan(x); }
|
||||
|
||||
SPECFUN_HOSt_DEVICE inline double acos(double x) { return cuda::std::acos(x); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double sinh(double x) { return cuda::std::sinh(x); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double cosh(double x) { return cuda::std::cosh(x); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double asinh(double x) { return cuda::std::asinh(x); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline bool signbit(double x) { return cuda::std::signbit(x); }
|
||||
|
||||
// Fallback to global namespace for functions unsupported on NVRTC
|
||||
#ifndef _LIBCUDACXX_COMPILER_NVRTC
|
||||
SPECFUN_HOST_DEVICE inline double ceil(double x) { return cuda::std::ceil(x); }
|
||||
SPECFUN_HOST_DEVICE inline double floor(double x) { return cuda::std::floor(x); }
|
||||
SPECFUN_HOST_DEVICE inline double round(double x) { return cuda::std::round(x); }
|
||||
SPECFUN_HOST_DEVICE inline double trunc(double x) { return cuda::std::trunc(x); }
|
||||
SPECFUN_HOST_DEVICE inline double fma(double x, double y, double z) { return cuda::std::fma(x, y, z); }
|
||||
SPECFUN_HOST_DEVICE inline double copysign(double x, double y) { return cuda::std::copysign(x, y); }
|
||||
SPECFUN_HOST_DEVICE inline double modf(double value, double *iptr) { return cuda::std::modf(value, iptr); }
|
||||
SPECFUN_HOST_DEVICE inline double fmax(double x, double y) { return cuda::std::fmax(x, y); }
|
||||
SPECFUN_HOST_DEVICE inline double fmin(double x, double y) { return cuda::std::fmin(x, y); }
|
||||
SPECFUN_HOST_DEVICE inline double log10(double num) { return cuda::std::log10(num); }
|
||||
SPECFUN_HOST_DEVICE inline double log1p(double num) { return cuda::std::log1p(num); }
|
||||
SPECFUN_HOST_DEVICE inline double frexp(double num, int *exp) { return cuda::std::frexp(num); }
|
||||
SPECFUN_HOST_DEVICE inline double ldexp(double num, int *exp) { return cuda::std::ldexp(num); }
|
||||
SPECFUN_HOST_DEVICE inline double fmod(double x, double y) { return cuda::std::fmod(x, y); }
|
||||
#else
|
||||
SPECFUN_HOST_DEVICE inline double ceil(double x) { return ::ceil(x); }
|
||||
SPECFUN_HOST_DEVICE inline double floor(double x) { return ::floor(x); }
|
||||
SPECFUN_HOST_DEVICE inline double round(double x) { return ::round(x); }
|
||||
SPECFUN_HOST_DEVICE inline double trunc(double x) { return ::trunc(x); }
|
||||
SPECFUN_HOST_DEVICE inline double fma(double x, double y, double z) { return ::fma(x, y, z); }
|
||||
SPECFUN_HOST_DEVICE inline double copysign(double x, double y) { return ::copysign(x, y); }
|
||||
SPECFUN_HOST_DEVICE inline double modf(double value, double *iptr) { return ::modf(value, iptr); }
|
||||
SPECFUN_HOST_DEVICE inline double fmax(double x, double y) { return ::fmax(x, y); }
|
||||
SPECFUN_HOST_DEVICE inline double fmin(double x, double y) { return ::fmin(x, y); }
|
||||
SPECFUN_HOST_DEVICE inline double log10(double num) { return ::log10(num); }
|
||||
SPECFUN_HOST_DEVICE inline double log1p(double num) { return ::log1p(num); }
|
||||
SPECFUN_HOST_DEVICE inline double frexp(double num, int *exp) { return ::frexp(num); }
|
||||
SPECFUN_HOST_DEVICE inline double ldexp(double num, int *exp) { return ::ldexp(num); }
|
||||
SPECFUN_HOST_DEVICE inline double fmod(double x, double y) { return ::fmod(x, y); }
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE void swap(T &a, T &b) {
|
||||
cuda::std::swap(a, b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE const T &clamp(const T &v, const T &lo, const T &hi) {
|
||||
return cuda::std::clamp(v, lo, hi);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using numeric_limits = cuda::std::numeric_limits<T>;
|
||||
|
||||
// Must use thrust for complex types in order to support CuPy
|
||||
template <typename T>
|
||||
using complex = thrust::complex<T>;
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE T abs(const complex<T> &z) {
|
||||
return thrust::abs(z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE complex<T> exp(const complex<T> &z) {
|
||||
return thrust::exp(z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE complex<T> log(const complex<T> &z) {
|
||||
return thrust::log(z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE T norm(const complex<T> &z) {
|
||||
return thrust::norm(z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE complex<T> sqrt(const complex<T> &z) {
|
||||
return thrust::sqrt(z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE complex<T> conj(const complex<T> &z) {
|
||||
return thrust::conj(z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE complex<T> pow(const complex<T> &x, const complex<T> &y) {
|
||||
return thrust::pow(x, y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE complex<T> pow(const complex<T> &x, const T &y) {
|
||||
return thrust::pow(x, y);
|
||||
}
|
||||
|
||||
// Other types and utilities
|
||||
using cuda::std::is_floating_point;
|
||||
using cuda::std::pair;
|
||||
using cuda::std::uint64_t;
|
||||
|
||||
#define SPECFUN_ASSERT(a)
|
||||
|
||||
} // namespace std
|
||||
|
||||
#else
|
||||
#define SPECFUN_HOST_DEVICE
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SPECFUN_ASSERT(a) assert(a)
|
||||
#else
|
||||
#define SPECFUN_ASSERT(a)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,204 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2024.
|
||||
* Original header comment appears below.
|
||||
*/
|
||||
|
||||
/* An implementation of the digamma function for complex arguments.
|
||||
*
|
||||
* Author: Josh Wilson
|
||||
*
|
||||
* Distributed under the same license as Scipy.
|
||||
*
|
||||
* Sources:
|
||||
* [1] "The Digital Library of Mathematical Functions", dlmf.nist.gov
|
||||
*
|
||||
* [2] mpmath (version 0.19), http://mpmath.org
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cephes/psi.h"
|
||||
#include "cephes/zeta.h"
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "trig.h"
|
||||
|
||||
namespace special {
|
||||
namespace detail {
|
||||
// All of the following were computed with mpmath
|
||||
// Location of the positive root
|
||||
constexpr double digamma_posroot = 1.4616321449683623;
|
||||
// Value of the positive root
|
||||
constexpr double digamma_posrootval = -9.2412655217294275e-17;
|
||||
// Location of the negative root
|
||||
constexpr double digamma_negroot = -0.504083008264455409;
|
||||
// Value of the negative root
|
||||
constexpr double digamma_negrootval = 7.2897639029768949e-17;
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE T digamma_zeta_series(T z, double root, double rootval) {
|
||||
T res = rootval;
|
||||
T coeff = -1.0;
|
||||
|
||||
z = z - root;
|
||||
T term;
|
||||
for (int n = 1; n < 100; n++) {
|
||||
coeff *= -z;
|
||||
term = coeff * cephes::zeta(n + 1, root);
|
||||
res += term;
|
||||
if (std::abs(term) < std::numeric_limits<double>::epsilon() * std::abs(res)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> digamma_forward_recurrence(std::complex<double> z,
|
||||
std::complex<double> psiz, int n) {
|
||||
/* Compute digamma(z + n) using digamma(z) using the recurrence
|
||||
* relation
|
||||
*
|
||||
* digamma(z + 1) = digamma(z) + 1/z.
|
||||
*
|
||||
* See https://dlmf.nist.gov/5.5#E2 */
|
||||
std::complex<double> res = psiz;
|
||||
|
||||
for (int k = 0; k < n; k++) {
|
||||
res += 1.0 / (z + static_cast<double>(k));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> digamma_backward_recurrence(std::complex<double> z,
|
||||
std::complex<double> psiz, int n) {
|
||||
/* Compute digamma(z - n) using digamma(z) and a recurrence relation. */
|
||||
std::complex<double> res = psiz;
|
||||
|
||||
for (int k = 1; k < n + 1; k++) {
|
||||
res -= 1.0 / (z - static_cast<double>(k));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> digamma_asymptotic_series(std::complex<double> z) {
|
||||
/* Evaluate digamma using an asymptotic series. See
|
||||
*
|
||||
* https://dlmf.nist.gov/5.11#E2 */
|
||||
double bernoulli2k[] = {
|
||||
0.166666666666666667, -0.0333333333333333333, 0.0238095238095238095, -0.0333333333333333333,
|
||||
0.0757575757575757576, -0.253113553113553114, 1.16666666666666667, -7.09215686274509804,
|
||||
54.9711779448621554, -529.124242424242424, 6192.12318840579710, -86580.2531135531136,
|
||||
1425517.16666666667, -27298231.0678160920, 601580873.900642368, -15116315767.0921569};
|
||||
std::complex<double> rzz = 1.0 / z / z;
|
||||
std::complex<double> zfac = 1.0;
|
||||
std::complex<double> term;
|
||||
std::complex<double> res;
|
||||
|
||||
if (!(std::isfinite(z.real()) && std::isfinite(z.imag()))) {
|
||||
/* Check for infinity (or nan) and return early.
|
||||
* Result of division by complex infinity is implementation dependent.
|
||||
* and has been observed to vary between C++ stdlib and CUDA stdlib.
|
||||
*/
|
||||
return std::log(z);
|
||||
}
|
||||
|
||||
res = std::log(z) - 0.5 / z;
|
||||
|
||||
for (int k = 1; k < 17; k++) {
|
||||
zfac *= rzz;
|
||||
term = -bernoulli2k[k - 1] * zfac / (2 * static_cast<double>(k));
|
||||
res += term;
|
||||
if (std::abs(term) < std::numeric_limits<double>::epsilon() * std::abs(res)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double digamma(double z) {
|
||||
/* Wrap Cephes' psi to take advantage of the series expansion around
|
||||
* the smallest negative zero.
|
||||
*/
|
||||
if (std::abs(z - detail::digamma_negroot) < 0.3) {
|
||||
return detail::digamma_zeta_series(z, detail::digamma_negroot, detail::digamma_negrootval);
|
||||
}
|
||||
return cephes::psi(z);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline float digamma(float z) { return static_cast<float>(digamma(static_cast<double>(z))); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> digamma(std::complex<double> z) {
|
||||
/*
|
||||
* Compute the digamma function for complex arguments. The strategy
|
||||
* is:
|
||||
*
|
||||
* - Around the two zeros closest to the origin (posroot and negroot)
|
||||
* use a Taylor series with precomputed zero order coefficient.
|
||||
* - If close to the origin, use a recurrence relation to step away
|
||||
* from the origin.
|
||||
* - If close to the negative real axis, use the reflection formula
|
||||
* to move to the right halfplane.
|
||||
* - If |z| is large (> 16), use the asymptotic series.
|
||||
* - If |z| is small, use a recurrence relation to make |z| large
|
||||
* enough to use the asymptotic series.
|
||||
*/
|
||||
double absz = std::abs(z);
|
||||
std::complex<double> res = 0;
|
||||
/* Use the asymptotic series for z away from the negative real axis
|
||||
* with abs(z) > smallabsz. */
|
||||
int smallabsz = 16;
|
||||
/* Use the reflection principle for z with z.real < 0 that are within
|
||||
* smallimag of the negative real axis.
|
||||
* int smallimag = 6 # unused below except in a comment */
|
||||
|
||||
if (z.real() <= 0.0 && std::ceil(z.real()) == z) {
|
||||
// Poles
|
||||
set_error("digamma", SF_ERROR_SINGULAR, NULL);
|
||||
return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()};
|
||||
}
|
||||
if (std::abs(z - detail::digamma_negroot) < 0.3) {
|
||||
// First negative root.
|
||||
return detail::digamma_zeta_series(z, detail::digamma_negroot, detail::digamma_negrootval);
|
||||
}
|
||||
|
||||
if (z.real() < 0 and std::abs(z.imag()) < smallabsz) {
|
||||
/* Reflection formula for digamma. See
|
||||
*
|
||||
*https://dlmf.nist.gov/5.5#E4
|
||||
*/
|
||||
res = -M_PI * cospi(z) / sinpi(z);
|
||||
z = 1.0 - z;
|
||||
absz = std::abs(z);
|
||||
}
|
||||
|
||||
if (absz < 0.5) {
|
||||
/* Use one step of the recurrence relation to step away from
|
||||
* the pole. */
|
||||
res = -1.0 / z;
|
||||
z += 1.0;
|
||||
absz = std::abs(z);
|
||||
}
|
||||
|
||||
if (std::abs(z - detail::digamma_posroot) < 0.5) {
|
||||
res += detail::digamma_zeta_series(z, detail::digamma_posroot, detail::digamma_posrootval);
|
||||
} else if (absz > smallabsz) {
|
||||
res += detail::digamma_asymptotic_series(z);
|
||||
} else if (z.real() >= 0.0) {
|
||||
double n = std::trunc(smallabsz - absz) + 1;
|
||||
std::complex<double> init = detail::digamma_asymptotic_series(z + n);
|
||||
res += detail::digamma_backward_recurrence(z + n, init, n);
|
||||
} else {
|
||||
// z.real() < 0, absz < smallabsz, and z.imag() > smallimag
|
||||
double n = std::trunc(smallabsz - absz) - 1;
|
||||
std::complex<double> init = detail::digamma_asymptotic_series(z - n);
|
||||
res += detail::digamma_forward_recurrence(z - n, init, n);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<float> digamma(std::complex<float> z) {
|
||||
return static_cast<std::complex<float>>(digamma(static_cast<std::complex<double>>(z)));
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
// should be included from config.h, but that won't work until we've cleanly separated out the C and C++ parts of the
|
||||
// code
|
||||
#ifdef __CUDACC__
|
||||
#define SPECFUN_HOST_DEVICE __host__ __device__
|
||||
#else
|
||||
#define SPECFUN_HOST_DEVICE
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SF_ERROR_OK = 0, /* no error */
|
||||
SF_ERROR_SINGULAR, /* singularity encountered */
|
||||
SF_ERROR_UNDERFLOW, /* floating point underflow */
|
||||
SF_ERROR_OVERFLOW, /* floating point overflow */
|
||||
SF_ERROR_SLOW, /* too many iterations required */
|
||||
SF_ERROR_LOSS, /* loss of precision */
|
||||
SF_ERROR_NO_RESULT, /* no result obtained */
|
||||
SF_ERROR_DOMAIN, /* out of domain */
|
||||
SF_ERROR_ARG, /* invalid input parameter */
|
||||
SF_ERROR_OTHER, /* unclassified error */
|
||||
SF_ERROR__LAST
|
||||
} sf_error_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <complex>
|
||||
|
||||
namespace special {
|
||||
|
||||
#ifndef SP_SPECFUN_ERROR
|
||||
SPECFUN_HOST_DEVICE inline void set_error(const char *func_name, sf_error_t code, const char *fmt, ...) {
|
||||
// nothing
|
||||
}
|
||||
#else
|
||||
void set_error(const char *func_name, sf_error_t code, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
void set_error_and_nan(const char *name, sf_error_t code, T &value) {
|
||||
if (code != SF_ERROR_OK) {
|
||||
set_error(name, code, nullptr);
|
||||
|
||||
if (code == SF_ERROR_DOMAIN || code == SF_ERROR_OVERFLOW || code == SF_ERROR_NO_RESULT) {
|
||||
value = std::numeric_limits<T>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_error_and_nan(const char *name, sf_error_t code, std::complex<T> &value) {
|
||||
if (code != SF_ERROR_OK) {
|
||||
set_error(name, code, nullptr);
|
||||
|
||||
if (code == SF_ERROR_DOMAIN || code == SF_ERROR_OVERFLOW || code == SF_ERROR_NO_RESULT) {
|
||||
value.real(std::numeric_limits<T>::quiet_NaN());
|
||||
value.imag(std::numeric_limits<T>::quiet_NaN());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,47 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2024.
|
||||
*
|
||||
* Original author: Josh Wilson, 2016.
|
||||
*/
|
||||
|
||||
/* Evaluate polynomials.
|
||||
*
|
||||
* All of the coefficients are stored in reverse order, i.e. if the
|
||||
* polynomial is
|
||||
*
|
||||
* u_n x^n + u_{n - 1} x^{n - 1} + ... + u_0,
|
||||
*
|
||||
* then coeffs[0] = u_n, coeffs[1] = u_{n - 1}, ..., coeffs[n] = u_0.
|
||||
*
|
||||
* References
|
||||
* ----------
|
||||
* [1] Knuth, "The Art of Computer Programming, Volume II"
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
namespace special {
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> cevalpoly(const double *coeffs, int degree, std::complex<double> z) {
|
||||
/* Evaluate a polynomial with real coefficients at a complex point.
|
||||
*
|
||||
* Uses equation (3) in section 4.6.4 of [1]. Note that it is more
|
||||
* efficient than Horner's method.
|
||||
*/
|
||||
double a = coeffs[0];
|
||||
double b = coeffs[1];
|
||||
double r = 2 * z.real();
|
||||
double s = std::norm(z);
|
||||
double tmp;
|
||||
|
||||
for (int j = 2; j < degree + 1; j++) {
|
||||
tmp = b;
|
||||
b = std::fma(-s, a, coeffs[j]);
|
||||
a = std::fma(r, a, tmp);
|
||||
}
|
||||
|
||||
return z * a + b;
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
694
venv/lib/python3.12/site-packages/scipy/special/special/hyp2f1.h
Normal file
694
venv/lib/python3.12/site-packages/scipy/special/special/hyp2f1.h
Normal file
@ -0,0 +1,694 @@
|
||||
/* Implementation of Gauss's hypergeometric function for complex values.
|
||||
*
|
||||
* This implementation is based on the Fortran implementation by Shanjie Zhang and
|
||||
* Jianming Jin included in specfun.f [1]_. Computation of Gauss's hypergeometric
|
||||
* function involves handling a patchwork of special cases. By default the Zhang and
|
||||
* Jin implementation has been followed as closely as possible except for situations where
|
||||
* an improvement was obvious. We've attempted to document the reasons behind decisions
|
||||
* made by Zhang and Jin and to document the reasons for deviating from their implementation
|
||||
* when this has been done. References to the NIST Digital Library of Mathematical
|
||||
* Functions [2]_ have been added where they are appropriate. The review paper by
|
||||
* Pearson et al [3]_ is an excellent resource for best practices for numerical
|
||||
* computation of hypergeometric functions. We have followed this review paper
|
||||
* when making improvements to and correcting defects in Zhang and Jin's
|
||||
* implementation. When Pearson et al propose several competing alternatives for a
|
||||
* given case, we've used our best judgment to decide on the method to use.
|
||||
*
|
||||
* Author: Albert Steppi
|
||||
*
|
||||
* Distributed under the same license as Scipy.
|
||||
*
|
||||
* References
|
||||
* ----------
|
||||
* .. [1] S. Zhang and J.M. Jin, "Computation of Special Functions", Wiley 1996
|
||||
* .. [2] NIST Digital Library of Mathematical Functions. http://dlmf.nist.gov/,
|
||||
* Release 1.1.1 of 2021-03-15. F. W. J. Olver, A. B. Olde Daalhuis,
|
||||
* D. W. Lozier, B. I. Schneider, R. F. Boisvert, C. W. Clark, B. R. Miller,
|
||||
* B. V. Saunders, H. S. Cohl, and M. A. McClain, eds.
|
||||
* .. [3] Pearson, J.W., Olver, S. & Porter, M.A.
|
||||
* "Numerical methods for the computation of the confluent and Gauss
|
||||
* hypergeometric functions."
|
||||
* Numer Algor 74, 821-866 (2017). https://doi.org/10.1007/s11075-016-0173-0
|
||||
* .. [4] Raimundas Vidunas, "Degenerate Gauss Hypergeometric Functions",
|
||||
* Kyushu Journal of Mathematics, 2007, Volume 61, Issue 1, Pages 109-135,
|
||||
* .. [5] López, J.L., Temme, N.M. New series expansions of the Gauss hypergeometric
|
||||
* function. Adv Comput Math 39, 349-365 (2013).
|
||||
* https://doi.org/10.1007/s10444-012-9283-y
|
||||
* """
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "tools.h"
|
||||
|
||||
#include "binom.h"
|
||||
#include "cephes/gamma.h"
|
||||
#include "cephes/lanczos.h"
|
||||
#include "cephes/poch.h"
|
||||
#include "cephes/hyp2f1.h"
|
||||
#include "digamma.h"
|
||||
|
||||
namespace special {
|
||||
namespace detail {
|
||||
constexpr double hyp2f1_EPS = 1e-15;
|
||||
/* The original implementation in SciPy from Zhang and Jin used 1500 for the
|
||||
* maximum number of series iterations in some cases and 500 in others.
|
||||
* Through the empirical results on the test cases in
|
||||
* scipy/special/_precompute/hyp2f1_data.py, it was determined that these values
|
||||
* can lead to early termination of series which would have eventually converged
|
||||
* at a reasonable level of accuracy. We've bumped the iteration limit to 3000,
|
||||
* and may adjust it again based on further analysis. */
|
||||
constexpr std::uint64_t hyp2f1_MAXITER = 3000;
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double four_gammas_lanczos(double u, double v, double w, double x) {
|
||||
/* Compute ratio of gamma functions using lanczos approximation.
|
||||
*
|
||||
* Computes gamma(u)*gamma(v)/(gamma(w)*gamma(x))
|
||||
*
|
||||
* It is assumed that x = u + v - w, but it is left to the user to
|
||||
* ensure this.
|
||||
*
|
||||
* The lanczos approximation takes the form
|
||||
*
|
||||
* gamma(x) = factor(x) * lanczos_sum_expg_scaled(x)
|
||||
*
|
||||
* where factor(x) = ((x + lanczos_g - 0.5)/e)**(x - 0.5).
|
||||
*
|
||||
* The formula above is only valid for x >= 0.5, but can be extended to
|
||||
* x < 0.5 with the reflection principle.
|
||||
*
|
||||
* Using the lanczos approximation when computing this ratio of gamma functions
|
||||
* allows factors to be combined analytically to avoid underflow and overflow
|
||||
* and produce a more accurate result. The condition x = u + v - w makes it
|
||||
* possible to cancel the factors in the expression
|
||||
*
|
||||
* factor(u) * factor(v) / (factor(w) * factor(x))
|
||||
*
|
||||
* by taking one factor and absorbing it into the others. Currently, this
|
||||
* implementation takes the factor corresponding to the argument with largest
|
||||
* absolute value and absorbs it into the others.
|
||||
*
|
||||
* Since this is only called internally by four_gammas. It is assumed that
|
||||
* |u| >= |v| and |w| >= |x|.
|
||||
*/
|
||||
|
||||
/* The below implementation may incorrectly return finite results
|
||||
* at poles of the gamma function. Handle these cases explicitly. */
|
||||
if ((u == std::trunc(u) && u <= 0) || (v == std::trunc(v) && v <= 0)) {
|
||||
/* Return nan if numerator has pole. Diverges to +- infinity
|
||||
* depending on direction so value is undefined. */
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
if ((w == std::trunc(w) && w <= 0) || (x == std::trunc(x) && x <= 0)) {
|
||||
// Return 0 if denominator has pole but not numerator.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double result = 1.0;
|
||||
double ugh, vgh, wgh, xgh, u_prime, v_prime, w_prime, x_prime;
|
||||
|
||||
if (u >= 0.5) {
|
||||
result *= cephes::lanczos_sum_expg_scaled(u);
|
||||
ugh = u + cephes::lanczos_g - 0.5;
|
||||
u_prime = u;
|
||||
} else {
|
||||
result /= cephes::lanczos_sum_expg_scaled(1 - u) * std::sin(M_PI * u) * M_1_PI;
|
||||
ugh = 0.5 - u + cephes::lanczos_g;
|
||||
u_prime = 1 - u;
|
||||
}
|
||||
|
||||
if (v >= 0.5) {
|
||||
result *= cephes::lanczos_sum_expg_scaled(v);
|
||||
vgh = v + cephes::lanczos_g - 0.5;
|
||||
v_prime = v;
|
||||
} else {
|
||||
result /= cephes::lanczos_sum_expg_scaled(1 - v) * std::sin(M_PI * v) * M_1_PI;
|
||||
vgh = 0.5 - v + cephes::lanczos_g;
|
||||
v_prime = 1 - v;
|
||||
}
|
||||
|
||||
if (w >= 0.5) {
|
||||
result /= cephes::lanczos_sum_expg_scaled(w);
|
||||
wgh = w + cephes::lanczos_g - 0.5;
|
||||
w_prime = w;
|
||||
} else {
|
||||
result *= cephes::lanczos_sum_expg_scaled(1 - w) * std::sin(M_PI * w) * M_1_PI;
|
||||
wgh = 0.5 - w + cephes::lanczos_g;
|
||||
w_prime = 1 - w;
|
||||
}
|
||||
|
||||
if (x >= 0.5) {
|
||||
result /= cephes::lanczos_sum_expg_scaled(x);
|
||||
xgh = x + cephes::lanczos_g - 0.5;
|
||||
x_prime = x;
|
||||
} else {
|
||||
result *= cephes::lanczos_sum_expg_scaled(1 - x) * std::sin(M_PI * x) * M_1_PI;
|
||||
xgh = 0.5 - x + cephes::lanczos_g;
|
||||
x_prime = 1 - x;
|
||||
}
|
||||
|
||||
if (std::abs(u) >= std::abs(w)) {
|
||||
// u has greatest absolute value. Absorb ugh into the others.
|
||||
if (std::abs((v_prime - u_prime) * (v - 0.5)) < 100 * ugh and v > 100) {
|
||||
/* Special case where base is close to 1. Condition taken from
|
||||
* Boost's beta function implementation. */
|
||||
result *= std::exp((v - 0.5) * std::log1p((v_prime - u_prime) / ugh));
|
||||
} else {
|
||||
result *= std::pow(vgh / ugh, v - 0.5);
|
||||
}
|
||||
|
||||
if (std::abs((u_prime - w_prime) * (w - 0.5)) < 100 * wgh and u > 100) {
|
||||
result *= std::exp((w - 0.5) * std::log1p((u_prime - w_prime) / wgh));
|
||||
} else {
|
||||
result *= std::pow(ugh / wgh, w - 0.5);
|
||||
}
|
||||
|
||||
if (std::abs((u_prime - x_prime) * (x - 0.5)) < 100 * xgh and u > 100) {
|
||||
result *= std::exp((x - 0.5) * std::log1p((u_prime - x_prime) / xgh));
|
||||
} else {
|
||||
result *= std::pow(ugh / xgh, x - 0.5);
|
||||
}
|
||||
} else {
|
||||
// w has greatest absolute value. Absorb wgh into the others.
|
||||
if (std::abs((u_prime - w_prime) * (u - 0.5)) < 100 * wgh and u > 100) {
|
||||
result *= std::exp((u - 0.5) * std::log1p((u_prime - w_prime) / wgh));
|
||||
} else {
|
||||
result *= pow(ugh / wgh, u - 0.5);
|
||||
}
|
||||
if (std::abs((v_prime - w_prime) * (v - 0.5)) < 100 * wgh and v > 100) {
|
||||
result *= std::exp((v - 0.5) * std::log1p((v_prime - w_prime) / wgh));
|
||||
} else {
|
||||
result *= std::pow(vgh / wgh, v - 0.5);
|
||||
}
|
||||
if (std::abs((w_prime - x_prime) * (x - 0.5)) < 100 * xgh and x > 100) {
|
||||
result *= std::exp((x - 0.5) * std::log1p((w_prime - x_prime) / xgh));
|
||||
} else {
|
||||
result *= std::pow(wgh / xgh, x - 0.5);
|
||||
}
|
||||
}
|
||||
// This exhausts all cases because we assume |u| >= |v| and |w| >= |x|.
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double four_gammas(double u, double v, double w, double x) {
|
||||
double result;
|
||||
|
||||
// Without loss of generality, assume |u| >= |v| and |w| >= |x|.
|
||||
if (std::abs(u) > std::abs(v)) {
|
||||
std::swap(u, v);
|
||||
}
|
||||
if (std::abs(x) > std::abs(w)) {
|
||||
std::swap(x, w);
|
||||
}
|
||||
/* Direct ratio tends to be more accurate for arguments in this range. Range
|
||||
* chosen empirically based on the relevant benchmarks in
|
||||
* scipy/special/_precompute/hyp2f1_data.py */
|
||||
if (std::abs(u) <= 100 && std::abs(v) <= 100 && std::abs(w) <= 100 && std::abs(x) <= 100) {
|
||||
result = cephes::Gamma(u) * cephes::Gamma(v) / (cephes::Gamma(w) * cephes::Gamma(x));
|
||||
if (std::isfinite(result) && result != 0.0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = four_gammas_lanczos(u, v, w, x);
|
||||
if (std::isfinite(result) && result != 0.0) {
|
||||
return result;
|
||||
}
|
||||
// If overflow or underflow, try again with logs.
|
||||
result = std::exp(cephes::lgam(v) - cephes::lgam(x) + cephes::lgam(u) - cephes::lgam(w));
|
||||
result *= cephes::gammasgn(u) * cephes::gammasgn(w) * cephes::gammasgn(v) * cephes::gammasgn(x);
|
||||
return result;
|
||||
}
|
||||
|
||||
class HypergeometricSeriesGenerator {
|
||||
/* Maclaurin series for hyp2f1.
|
||||
*
|
||||
* Series is convergent for |z| < 1 but is only practical for numerical
|
||||
* computation when |z| < 0.9.
|
||||
*/
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE HypergeometricSeriesGenerator(double a, double b, double c, std::complex<double> z)
|
||||
: a_(a), b_(b), c_(c), z_(z), term_(1.0), k_(0) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
std::complex<double> output = term_;
|
||||
term_ = term_ * (a_ + k_) * (b_ + k_) / ((k_ + 1) * (c_ + k_)) * z_;
|
||||
++k_;
|
||||
return output;
|
||||
}
|
||||
|
||||
private:
|
||||
double a_, b_, c_;
|
||||
std::complex<double> z_, term_;
|
||||
std::uint64_t k_;
|
||||
};
|
||||
|
||||
class Hyp2f1Transform1Generator {
|
||||
/* 1 -z transformation of standard series.*/
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE Hyp2f1Transform1Generator(double a, double b, double c, std::complex<double> z)
|
||||
: factor1_(four_gammas(c, c - a - b, c - a, c - b)),
|
||||
factor2_(four_gammas(c, a + b - c, a, b) * std::pow(1.0 - z, c - a - b)),
|
||||
generator1_(HypergeometricSeriesGenerator(a, b, a + b - c + 1, 1.0 - z)),
|
||||
generator2_(HypergeometricSeriesGenerator(c - a, c - b, c - a - b + 1, 1.0 - z)) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
return factor1_ * generator1_() + factor2_ * generator2_();
|
||||
}
|
||||
|
||||
private:
|
||||
std::complex<double> factor1_, factor2_;
|
||||
HypergeometricSeriesGenerator generator1_, generator2_;
|
||||
};
|
||||
|
||||
class Hyp2f1Transform1LimitSeriesGenerator {
|
||||
/* 1 - z transform in limit as c - a - b approaches an integer m. */
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE Hyp2f1Transform1LimitSeriesGenerator(double a, double b, double m, std::complex<double> z)
|
||||
: d1_(special::digamma(a)), d2_(special::digamma(b)), d3_(special::digamma(1 + m)),
|
||||
d4_(special::digamma(1.0)), a_(a), b_(b), m_(m), z_(z), log_1_z_(std::log(1.0 - z)),
|
||||
factor_(1.0 / cephes::Gamma(m + 1)), k_(0) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
std::complex<double> term_ = (d1_ + d2_ - d3_ - d4_ + log_1_z_) * factor_;
|
||||
// Use digamma(x + 1) = digamma(x) + 1/x
|
||||
d1_ += 1 / (a_ + k_); // d1 = digamma(a + k)
|
||||
d2_ += 1 / (b_ + k_); // d2 = digamma(b + k)
|
||||
d3_ += 1 / (1.0 + m_ + k_); // d3 = digamma(1 + m + k)
|
||||
d4_ += 1 / (1.0 + k_); // d4 = digamma(1 + k)
|
||||
factor_ *= (a_ + k_) * (b_ + k_) / ((k_ + 1.0) * (m_ + k_ + 1)) * (1.0 - z_);
|
||||
++k_;
|
||||
return term_;
|
||||
}
|
||||
|
||||
private:
|
||||
double d1_, d2_, d3_, d4_, a_, b_, m_;
|
||||
std::complex<double> z_, log_1_z_, factor_;
|
||||
int k_;
|
||||
};
|
||||
|
||||
class Hyp2f1Transform2Generator {
|
||||
/* 1/z transformation of standard series.*/
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE Hyp2f1Transform2Generator(double a, double b, double c, std::complex<double> z)
|
||||
: factor1_(four_gammas(c, b - a, b, c - a) * std::pow(-z, -a)),
|
||||
factor2_(four_gammas(c, a - b, a, c - b) * std::pow(-z, -b)),
|
||||
generator1_(HypergeometricSeriesGenerator(a, a - c + 1, a - b + 1, 1.0 / z)),
|
||||
generator2_(HypergeometricSeriesGenerator(b, b - c + 1, b - a + 1, 1.0 / z)) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
return factor1_ * generator1_() + factor2_ * generator2_();
|
||||
}
|
||||
|
||||
private:
|
||||
std::complex<double> factor1_, factor2_;
|
||||
HypergeometricSeriesGenerator generator1_, generator2_;
|
||||
};
|
||||
|
||||
class Hyp2f1Transform2LimitSeriesGenerator {
|
||||
/* 1/z transform in limit as a - b approaches a non-negative integer m. (Can swap a and b to
|
||||
* handle the m a negative integer case. */
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE Hyp2f1Transform2LimitSeriesGenerator(double a, double b, double c, double m,
|
||||
std::complex<double> z)
|
||||
: d1_(special::digamma(1.0)), d2_(special::digamma(1 + m)), d3_(special::digamma(a)),
|
||||
d4_(special::digamma(c - a)), a_(a), b_(b), c_(c), m_(m), z_(z), log_neg_z_(std::log(-z)),
|
||||
factor_(special::cephes::poch(b, m) * special::cephes::poch(1 - c + b, m) /
|
||||
special::cephes::Gamma(m + 1)),
|
||||
k_(0) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
std::complex<double> term = (d1_ + d2_ - d3_ - d4_ + log_neg_z_) * factor_;
|
||||
// Use digamma(x + 1) = digamma(x) + 1/x
|
||||
d1_ += 1 / (1.0 + k_); // d1 = digamma(1 + k)
|
||||
d2_ += 1 / (1.0 + m_ + k_); // d2 = digamma(1 + m + k)
|
||||
d3_ += 1 / (a_ + k_); // d3 = digamma(a + k)
|
||||
d4_ -= 1 / (c_ - a_ - k_ - 1); // d4 = digamma(c - a - k)
|
||||
factor_ *= (b_ + m_ + k_) * (1 - c_ + b_ + m_ + k_) / ((k_ + 1) * (m_ + k_ + 1)) / z_;
|
||||
++k_;
|
||||
return term;
|
||||
}
|
||||
|
||||
private:
|
||||
double d1_, d2_, d3_, d4_, a_, b_, c_, m_;
|
||||
std::complex<double> z_, log_neg_z_, factor_;
|
||||
std::uint64_t k_;
|
||||
};
|
||||
|
||||
class Hyp2f1Transform2LimitSeriesCminusAIntGenerator {
|
||||
/* 1/z transform in limit as a - b approaches a non-negative integer m, and c - a approaches
|
||||
* a positive integer n. */
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE Hyp2f1Transform2LimitSeriesCminusAIntGenerator(double a, double b, double c, double m,
|
||||
double n, std::complex<double> z)
|
||||
: d1_(special::digamma(1.0)), d2_(special::digamma(1 + m)), d3_(special::digamma(a)),
|
||||
d4_(special::digamma(n)), a_(a), b_(b), c_(c), m_(m), n_(n), z_(z), log_neg_z_(std::log(-z)),
|
||||
factor_(special::cephes::poch(b, m) * special::cephes::poch(1 - c + b, m) /
|
||||
special::cephes::Gamma(m + 1)),
|
||||
k_(0) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
std::complex<double> term;
|
||||
if (k_ < n_) {
|
||||
term = (d1_ + d2_ - d3_ - d4_ + log_neg_z_) * factor_;
|
||||
// Use digamma(x + 1) = digamma(x) + 1/x
|
||||
d1_ += 1 / (1.0 + k_); // d1 = digamma(1 + k)
|
||||
d2_ += 1 / (1 + m_ + k_); // d2 = digamma(1 + m + k)
|
||||
d3_ += 1 / (a_ + k_); // d3 = digamma(a + k)
|
||||
d4_ -= 1 / (n_ - k_ - 1); // d4 = digamma(c - a - k)
|
||||
factor_ *= (b_ + m_ + k_) * (1 - c_ + b_ + m_ + k_) / ((k_ + 1) * (m_ + k_ + 1)) / z_;
|
||||
++k_;
|
||||
return term;
|
||||
}
|
||||
if (k_ == n_) {
|
||||
/* When c - a approaches a positive integer and k_ >= c - a = n then
|
||||
* poch(1 - c + b + m + k) = poch(1 - c + a + k) = approaches zero and
|
||||
* digamma(c - a - k) approaches a pole. However we can use the limit
|
||||
* digamma(-n + epsilon) / gamma(-n + epsilon) -> (-1)**(n + 1) * (n+1)! as epsilon -> 0
|
||||
* to continue the series.
|
||||
*
|
||||
* poch(1 - c + b, m + k) = gamma(1 - c + b + m + k)/gamma(1 - c + b)
|
||||
*
|
||||
* If a - b is an integer and c - a is an integer, then a and b must both be integers, so assume
|
||||
* a and b are integers and take the limit as c approaches an integer.
|
||||
*
|
||||
* gamma(1 - c + epsilon + a + k)/gamma(1 - c - epsilon + b) =
|
||||
* (gamma(c + epsilon - b) / gamma(c + epsilon - a - k)) *
|
||||
* (sin(pi * (c + epsilon - b)) / sin(pi * (c + epsilon - a - k))) (reflection principle)
|
||||
*
|
||||
* In the limit as epsilon goes to zero, the ratio of sines will approach
|
||||
* (-1)**(a - b + k) = (-1)**(m + k)
|
||||
*
|
||||
* We may then replace
|
||||
*
|
||||
* poch(1 - c - epsilon + b, m + k)*digamma(c + epsilon - a - k)
|
||||
*
|
||||
* with
|
||||
*
|
||||
* (-1)**(a - b + k)*gamma(c + epsilon - b) * digamma(c + epsilon - a - k) / gamma(c + epsilon - a - k)
|
||||
*
|
||||
* and taking the limit epsilon -> 0 gives
|
||||
*
|
||||
* (-1)**(a - b + k) * gamma(c - b) * (-1)**(k + a - c + 1)(k + a - c)!
|
||||
* = (-1)**(c - b - 1)*Gamma(k + a - c + 1)
|
||||
*/
|
||||
factor_ = std::pow(-1, m_ + n_) * special::binom(c_ - 1, b_ - 1) *
|
||||
special::cephes::poch(c_ - a_ + 1, m_ - 1) / std::pow(z_, static_cast<double>(k_));
|
||||
}
|
||||
term = factor_;
|
||||
factor_ *= (b_ + m_ + k_) * (k_ + a_ - c_ + 1) / ((k_ + 1) * (m_ + k_ + 1)) / z_;
|
||||
++k_;
|
||||
return term;
|
||||
}
|
||||
|
||||
private:
|
||||
double d1_, d2_, d3_, d4_, a_, b_, c_, m_, n_;
|
||||
std::complex<double> z_, log_neg_z_, factor_;
|
||||
std::uint64_t k_;
|
||||
};
|
||||
|
||||
class Hyp2f1Transform2LimitFinitePartGenerator {
|
||||
/* Initial finite sum in limit as a - b approaches a non-negative integer m. The limiting series
|
||||
* for the 1 - z transform also has an initial finite sum, but it is a standard hypergeometric
|
||||
* series. */
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE Hyp2f1Transform2LimitFinitePartGenerator(double b, double c, double m,
|
||||
std::complex<double> z)
|
||||
: b_(b), c_(c), m_(m), z_(z), term_(cephes::Gamma(m) / cephes::Gamma(c - b)), k_(0) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
std::complex<double> output = term_;
|
||||
term_ = term_ * (b_ + k_) * (c_ - b_ - k_ - 1) / ((k_ + 1) * (m_ - k_ - 1)) / z_;
|
||||
++k_;
|
||||
return output;
|
||||
}
|
||||
|
||||
private:
|
||||
double b_, c_, m_;
|
||||
std::complex<double> z_, term_;
|
||||
std::uint64_t k_;
|
||||
};
|
||||
|
||||
class LopezTemmeSeriesGenerator {
|
||||
/* Lopez-Temme Series for Gaussian hypergeometric function [4].
|
||||
*
|
||||
* Converges for all z with real(z) < 1, including in the regions surrounding
|
||||
* the points exp(+- i*pi/3) that are not covered by any of the standard
|
||||
* transformations.
|
||||
*/
|
||||
public:
|
||||
SPECFUN_HOST_DEVICE LopezTemmeSeriesGenerator(double a, double b, double c, std::complex<double> z)
|
||||
: n_(0), a_(a), b_(b), c_(c), phi_previous_(1.0), phi_(1 - 2 * b / c), z_(z), Z_(a * z / (z - 2.0)) {}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> operator()() {
|
||||
if (n_ == 0) {
|
||||
++n_;
|
||||
return 1.0;
|
||||
}
|
||||
if (n_ > 1) { // Update phi and Z for n>=2
|
||||
double new_phi = ((n_ - 1) * phi_previous_ - (2.0 * b_ - c_) * phi_) / (c_ + (n_ - 1));
|
||||
phi_previous_ = phi_;
|
||||
phi_ = new_phi;
|
||||
Z_ = Z_ * z_ / (z_ - 2.0) * ((a_ + (n_ - 1)) / n_);
|
||||
}
|
||||
++n_;
|
||||
return Z_ * phi_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint64_t n_;
|
||||
double a_, b_, c_, phi_previous_, phi_;
|
||||
std::complex<double> z_, Z_;
|
||||
};
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> hyp2f1_transform1_limiting_case(double a, double b, double c, double m,
|
||||
std::complex<double> z) {
|
||||
/* 1 - z transform in limiting case where c - a - b approaches an integer m. */
|
||||
std::complex<double> result = 0.0;
|
||||
if (m >= 0) {
|
||||
if (m != 0) {
|
||||
auto series_generator = HypergeometricSeriesGenerator(a, b, 1 - m, 1.0 - z);
|
||||
result += four_gammas(m, c, a + m, b + m) * series_eval_fixed_length(series_generator,
|
||||
std::complex<double>{0.0, 0.0},
|
||||
static_cast<std::uint64_t>(m));
|
||||
}
|
||||
std::complex<double> prefactor = std::pow(-1.0, m + 1) * special::cephes::Gamma(c) /
|
||||
(special::cephes::Gamma(a) * special::cephes::Gamma(b)) *
|
||||
std::pow(1.0 - z, m);
|
||||
auto series_generator = Hyp2f1Transform1LimitSeriesGenerator(a + m, b + m, m, z);
|
||||
result += prefactor * series_eval(series_generator, std::complex<double>{0.0, 0.0}, hyp2f1_EPS,
|
||||
hyp2f1_MAXITER, "hyp2f1");
|
||||
return result;
|
||||
} else {
|
||||
result = four_gammas(-m, c, a, b) * std::pow(1.0 - z, m);
|
||||
auto series_generator1 = HypergeometricSeriesGenerator(a + m, b + m, 1 + m, 1.0 - z);
|
||||
result *= series_eval_fixed_length(series_generator1, std::complex<double>{0.0, 0.0},
|
||||
static_cast<std::uint64_t>(-m));
|
||||
double prefactor = std::pow(-1.0, m + 1) * special::cephes::Gamma(c) /
|
||||
(special::cephes::Gamma(a + m) * special::cephes::Gamma(b + m));
|
||||
auto series_generator2 = Hyp2f1Transform1LimitSeriesGenerator(a, b, -m, z);
|
||||
result += prefactor * series_eval(series_generator2, std::complex<double>{0.0, 0.0}, hyp2f1_EPS,
|
||||
hyp2f1_MAXITER, "hyp2f1");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> hyp2f1_transform2_limiting_case(double a, double b, double c, double m,
|
||||
std::complex<double> z) {
|
||||
/* 1 / z transform in limiting case where a - b approaches a non-negative integer m. Negative integer case
|
||||
* can be handled by swapping a and b. */
|
||||
auto series_generator1 = Hyp2f1Transform2LimitFinitePartGenerator(b, c, m, z);
|
||||
std::complex<double> result = cephes::Gamma(c) / cephes::Gamma(a) * std::pow(-z, -b);
|
||||
result *=
|
||||
series_eval_fixed_length(series_generator1, std::complex<double>{0.0, 0.0}, static_cast<std::uint64_t>(m));
|
||||
std::complex<double> prefactor = cephes::Gamma(c) / (cephes::Gamma(a) * cephes::Gamma(c - b) * std::pow(-z, a));
|
||||
double n = c - a;
|
||||
if (abs(n - std::round(n)) < hyp2f1_EPS) {
|
||||
auto series_generator2 = Hyp2f1Transform2LimitSeriesCminusAIntGenerator(a, b, c, m, n, z);
|
||||
result += prefactor * series_eval(series_generator2, std::complex<double>{0.0, 0.0}, hyp2f1_EPS,
|
||||
hyp2f1_MAXITER, "hyp2f1");
|
||||
return result;
|
||||
}
|
||||
auto series_generator2 = Hyp2f1Transform2LimitSeriesGenerator(a, b, c, m, z);
|
||||
result += prefactor *
|
||||
series_eval(series_generator2, std::complex<double>{0.0, 0.0}, hyp2f1_EPS, hyp2f1_MAXITER, "hyp2f1");
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> hyp2f1(double a, double b, double c, std::complex<double> z) {
|
||||
/* Special Cases
|
||||
* -----------------------------------------------------------------------
|
||||
* Takes constant value 1 when a = 0 or b = 0, even if c is a non-positive
|
||||
* integer. This follows mpmath. */
|
||||
if (a == 0 || b == 0) {
|
||||
return 1.0;
|
||||
}
|
||||
double z_abs = std::abs(z);
|
||||
// Equals 1 when z i 0, unless c is 0.
|
||||
if (z_abs == 0) {
|
||||
if (c != 0) {
|
||||
return 1.0;
|
||||
} else {
|
||||
// Returning real part NAN and imaginary part 0 follows mpmath.
|
||||
return std::complex<double>{std::numeric_limits<double>::quiet_NaN(), 0};
|
||||
}
|
||||
}
|
||||
bool a_neg_int = a == std::trunc(a) && a < 0;
|
||||
bool b_neg_int = b == std::trunc(b) && b < 0;
|
||||
bool c_non_pos_int = c == std::trunc(c) and c <= 0;
|
||||
/* Diverges when c is a non-positive integer unless a is an integer with
|
||||
* c <= a <= 0 or b is an integer with c <= b <= 0, (or z equals 0 with
|
||||
* c != 0) Cases z = 0, a = 0, or b = 0 have already been handled. We follow
|
||||
* mpmath in handling the degenerate cases where any of a, b, c are
|
||||
* non-positive integers. See [3] for a treatment of degenerate cases. */
|
||||
if (c_non_pos_int && !((a_neg_int && c <= a && a < 0) || (b_neg_int && c <= b && b < 0))) {
|
||||
return std::complex<double>{std::numeric_limits<double>::infinity(), 0};
|
||||
}
|
||||
/* Reduces to a polynomial when a or b is a negative integer.
|
||||
* If a and b are both negative integers, we take care to terminate
|
||||
* the series at a or b of smaller magnitude. This is to ensure proper
|
||||
* handling of situations like a < c < b <= 0, a, b, c all non-positive
|
||||
* integers, where terminating at a would lead to a term of the form 0 / 0. */
|
||||
std::uint64_t max_degree;
|
||||
if (a_neg_int || b_neg_int) {
|
||||
if (a_neg_int && b_neg_int) {
|
||||
max_degree = a > b ? std::abs(a) : std::abs(b);
|
||||
} else if (a_neg_int) {
|
||||
max_degree = std::abs(a);
|
||||
} else {
|
||||
max_degree = std::abs(b);
|
||||
}
|
||||
if (max_degree <= UINT64_MAX) {
|
||||
auto series_generator = detail::HypergeometricSeriesGenerator(a, b, c, z);
|
||||
return detail::series_eval_fixed_length(series_generator, std::complex<double>{0.0, 0.0}, max_degree + 1);
|
||||
} else {
|
||||
set_error("hyp2f1", SF_ERROR_NO_RESULT, NULL);
|
||||
return std::complex<double>{std::numeric_limits<double>::quiet_NaN(),
|
||||
std::numeric_limits<double>::quiet_NaN()};
|
||||
}
|
||||
}
|
||||
// Kummer's Theorem for z = -1; c = 1 + a - b (DLMF 15.4.26)
|
||||
if (std::abs(z + 1.0) < detail::hyp2f1_EPS && std::abs(1 + a - b - c) < detail::hyp2f1_EPS && !c_non_pos_int) {
|
||||
return detail::four_gammas(a - b + 1, 0.5 * a + 1, a + 1, 0.5 * a - b + 1);
|
||||
}
|
||||
std::complex<double> result;
|
||||
bool c_minus_a_neg_int = c - a == std::trunc(c - a) && c - a < 0;
|
||||
bool c_minus_b_neg_int = c - b == std::trunc(c - b) && c - b < 0;
|
||||
/* If one of c - a or c - b is a negative integer, reduces to evaluating
|
||||
* a polynomial through an Euler hypergeometric transformation.
|
||||
* (DLMF 15.8.1) */
|
||||
if (c_minus_a_neg_int || c_minus_b_neg_int) {
|
||||
max_degree = c_minus_b_neg_int ? std::abs(c - b) : std::abs(c - a);
|
||||
if (max_degree <= UINT64_MAX) {
|
||||
result = std::pow(1.0 - z, c - a - b);
|
||||
auto series_generator = detail::HypergeometricSeriesGenerator(c - a, c - b, c, z);
|
||||
result *=
|
||||
detail::series_eval_fixed_length(series_generator, std::complex<double>{0.0, 0.0}, max_degree + 2);
|
||||
return result;
|
||||
} else {
|
||||
set_error("hyp2f1", SF_ERROR_NO_RESULT, NULL);
|
||||
return std::complex<double>{std::numeric_limits<double>::quiet_NaN(),
|
||||
std::numeric_limits<double>::quiet_NaN()};
|
||||
}
|
||||
}
|
||||
/* Diverges as real(z) -> 1 when c <= a + b.
|
||||
* Todo: Actually check for overflow instead of using a fixed tolerance for
|
||||
* all parameter combinations like in the Fortran original. */
|
||||
if (std::abs(1 - z.real()) < detail::hyp2f1_EPS && z.imag() == 0 && c - a - b <= 0 && !c_non_pos_int) {
|
||||
return std::complex<double>{std::numeric_limits<double>::infinity(), 0};
|
||||
}
|
||||
// Gauss's Summation Theorem for z = 1; c - a - b > 0 (DLMF 15.4.20).
|
||||
if (z == 1.0 && c - a - b > 0 && !c_non_pos_int) {
|
||||
return detail::four_gammas(c, c - a - b, c - a, c - b);
|
||||
}
|
||||
/* |z| < 0, z.real() >= 0. Use the Maclaurin Series.
|
||||
* -----------------------------------------------------------------------
|
||||
* Apply Euler Hypergeometric Transformation (DLMF 15.8.1) to reduce
|
||||
* size of a and b if possible. We follow Zhang and Jin's
|
||||
* implementation [1] although there is very likely a better heuristic
|
||||
* to determine when this transformation should be applied. As it
|
||||
* stands, this hurts precision in some cases. */
|
||||
if (z_abs < 0.9 && z.real() >= 0) {
|
||||
if (c - a < a && c - b < b) {
|
||||
result = std::pow(1.0 - z, c - a - b);
|
||||
auto series_generator = detail::HypergeometricSeriesGenerator(c - a, c - b, c, z);
|
||||
result *= detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS,
|
||||
detail::hyp2f1_MAXITER, "hyp2f1");
|
||||
return result;
|
||||
}
|
||||
auto series_generator = detail::HypergeometricSeriesGenerator(a, b, c, z);
|
||||
return detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS,
|
||||
detail::hyp2f1_MAXITER, "hyp2f1");
|
||||
}
|
||||
/* Points near exp(iπ/3), exp(-iπ/3) not handled by any of the standard
|
||||
* transformations. Use series of López and Temme [5]. These regions
|
||||
* were not correctly handled by Zhang and Jin's implementation.
|
||||
* -------------------------------------------------------------------------*/
|
||||
if (0.9 <= z_abs && z_abs < 1.1 && std::abs(1.0 - z) >= 0.9 && z.real() >= 0) {
|
||||
/* This condition for applying Euler Transformation (DLMF 15.8.1)
|
||||
* was determined empirically to work better for this case than that
|
||||
* used in Zhang and Jin's implementation for |z| < 0.9,
|
||||
* real(z) >= 0. */
|
||||
if ((c - a <= a && c - b < b) || (c - a < a && c - b <= b)) {
|
||||
auto series_generator = detail::LopezTemmeSeriesGenerator(c - a, c - b, c, z);
|
||||
result = std::pow(1.0 - 0.5 * z, a - c); // Lopez-Temme prefactor
|
||||
result *= detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS,
|
||||
detail::hyp2f1_MAXITER, "hyp2f1");
|
||||
return std::pow(1.0 - z, c - a - b) * result; // Euler transform prefactor.
|
||||
}
|
||||
auto series_generator = detail::LopezTemmeSeriesGenerator(a, b, c, z);
|
||||
result = detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS,
|
||||
detail::hyp2f1_MAXITER, "hyp2f1");
|
||||
return std::pow(1.0 - 0.5 * z, -a) * result; // Lopez-Temme prefactor.
|
||||
}
|
||||
/* z/(z - 1) transformation (DLMF 15.8.1). Avoids cancellation issues that
|
||||
* occur with Maclaurin series for real(z) < 0.
|
||||
* -------------------------------------------------------------------------*/
|
||||
if (z_abs < 1.1 && z.real() < 0) {
|
||||
if (0 < b && b < a && a < c) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
auto series_generator = detail::HypergeometricSeriesGenerator(a, c - b, c, z / (z - 1.0));
|
||||
return std::pow(1.0 - z, -a) * detail::series_eval(series_generator, std::complex<double>{0.0, 0.0},
|
||||
detail::hyp2f1_EPS, detail::hyp2f1_MAXITER, "hyp2f1");
|
||||
}
|
||||
/* 1 - z transformation (DLMF 15.8.4). */
|
||||
if (0.9 <= z_abs && z_abs < 1.1) {
|
||||
if (std::abs(c - a - b - std::round(c - a - b)) < detail::hyp2f1_EPS) {
|
||||
// Removable singularity when c - a - b is an integer. Need to use limiting formula.
|
||||
double m = std::round(c - a - b);
|
||||
return detail::hyp2f1_transform1_limiting_case(a, b, c, m, z);
|
||||
}
|
||||
auto series_generator = detail::Hyp2f1Transform1Generator(a, b, c, z);
|
||||
return detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS,
|
||||
detail::hyp2f1_MAXITER, "hyp2f1");
|
||||
}
|
||||
/* 1/z transformation (DLMF 15.8.2). */
|
||||
if (std::abs(a - b - std::round(a - b)) < detail::hyp2f1_EPS) {
|
||||
if (b > a) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
double m = std::round(a - b);
|
||||
return detail::hyp2f1_transform2_limiting_case(a, b, c, m, z);
|
||||
}
|
||||
auto series_generator = detail::Hyp2f1Transform2Generator(a, b, c, z);
|
||||
return detail::series_eval(series_generator, std::complex<double>{0.0, 0.0}, detail::hyp2f1_EPS,
|
||||
detail::hyp2f1_MAXITER, "hyp2f1");
|
||||
}
|
||||
|
||||
inline std::complex<float> hyp2f1(float a, float b, float c, std::complex<float> x) {
|
||||
return static_cast<std::complex<float>>(hyp2f1(static_cast<double>(a), static_cast<double>(b),
|
||||
static_cast<double>(c), static_cast<std::complex<double>>(x)));
|
||||
}
|
||||
|
||||
inline double hyp2f1(double a, double b, double c, double x) { return cephes::hyp2f1(a, b, c, x); }
|
||||
|
||||
inline float hyp2f1(float a, float b, float c, float x) {
|
||||
return hyp2f1(static_cast<double>(a), static_cast<double>(b), static_cast<double>(c), static_cast<double>(x));
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
@ -0,0 +1,150 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2023.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* Implementation of the Lambert W function [1]. Based on MPMath
|
||||
* Implementation [2], and documentation [3].
|
||||
*
|
||||
* Copyright: Yosef Meller, 2009
|
||||
* Author email: mellerf@netvision.net.il
|
||||
*
|
||||
* Distributed under the same license as SciPy
|
||||
*
|
||||
*
|
||||
* References:
|
||||
* [1] On the Lambert W function, Adv. Comp. Math. 5 (1996) 329-359,
|
||||
* available online: https://web.archive.org/web/20230123211413/https://cs.uwaterloo.ca/research/tr/1993/03/W.pdf
|
||||
* [2] mpmath source code,
|
||||
https://github.com/mpmath/mpmath/blob/c5939823669e1bcce151d89261b802fe0d8978b4/mpmath/functions/functions.py#L435-L461
|
||||
* [3]
|
||||
https://web.archive.org/web/20230504171447/https://mpmath.org/doc/current/functions/powers.html#lambert-w-function
|
||||
*
|
||||
|
||||
* TODO: use a series expansion when extremely close to the branch point
|
||||
* at `-1/e` and make sure that the proper branch is chosen there.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "evalpoly.h"
|
||||
|
||||
namespace special {
|
||||
constexpr double EXPN1 = 0.36787944117144232159553; // exp(-1)
|
||||
constexpr double OMEGA = 0.56714329040978387299997; // W(1, 0)
|
||||
|
||||
namespace detail {
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> lambertw_branchpt(std::complex<double> z) {
|
||||
// Series for W(z, 0) around the branch point; see 4.22 in [1].
|
||||
double coeffs[] = {-1.0 / 3.0, 1.0, -1.0};
|
||||
std::complex<double> p = std::sqrt(2.0 * (M_E * z + 1.0));
|
||||
|
||||
return cevalpoly(coeffs, 2, p);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> lambertw_pade0(std::complex<double> z) {
|
||||
// (3, 2) Pade approximation for W(z, 0) around 0.
|
||||
double num[] = {12.85106382978723404255, 12.34042553191489361902, 1.0};
|
||||
double denom[] = {32.53191489361702127660, 14.34042553191489361702, 1.0};
|
||||
|
||||
/* This only gets evaluated close to 0, so we don't need a more
|
||||
* careful algorithm that avoids overflow in the numerator for
|
||||
* large z. */
|
||||
return z * cevalpoly(num, 2, z) / cevalpoly(denom, 2, z);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> lambertw_asy(std::complex<double> z, long k) {
|
||||
/* Compute the W function using the first two terms of the
|
||||
* asymptotic series. See 4.20 in [1].
|
||||
*/
|
||||
std::complex<double> w = std::log(z) + 2.0 * M_PI * k * std::complex<double>(0, 1);
|
||||
return w - std::log(w);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> lambertw(std::complex<double> z, long k, double tol) {
|
||||
double absz;
|
||||
std::complex<double> w;
|
||||
std::complex<double> ew, wew, wewz, wn;
|
||||
|
||||
if (std::isnan(z.real()) || std::isnan(z.imag())) {
|
||||
return z;
|
||||
}
|
||||
if (z.real() == std::numeric_limits<double>::infinity()) {
|
||||
return z + 2.0 * M_PI * k * std::complex<double>(0, 1);
|
||||
}
|
||||
if (z.real() == -std::numeric_limits<double>::infinity()) {
|
||||
return -z + (2.0 * M_PI * k + M_PI) * std::complex<double>(0, 1);
|
||||
}
|
||||
if (z == 0.0) {
|
||||
if (k == 0) {
|
||||
return z;
|
||||
}
|
||||
set_error("lambertw", SF_ERROR_SINGULAR, NULL);
|
||||
return -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
if (z == 1.0 && k == 0) {
|
||||
// Split out this case because the asymptotic series blows up
|
||||
return OMEGA;
|
||||
}
|
||||
|
||||
absz = std::abs(z);
|
||||
// Get an initial guess for Halley's method
|
||||
if (k == 0) {
|
||||
if (std::abs(z + EXPN1) < 0.3) {
|
||||
w = detail::lambertw_branchpt(z);
|
||||
} else if (-1.0 < z.real() && z.real() < 1.5 && std::abs(z.imag()) < 1.0 &&
|
||||
-2.5 * std::abs(z.imag()) - 0.2 < z.real()) {
|
||||
/* Empirically determined decision boundary where the Pade
|
||||
* approximation is more accurate. */
|
||||
w = detail::lambertw_pade0(z);
|
||||
} else {
|
||||
w = detail::lambertw_asy(z, k);
|
||||
}
|
||||
} else if (k == -1) {
|
||||
if (absz <= EXPN1 && z.imag() == 0.0 && z.real() < 0.0) {
|
||||
w = std::log(-z.real());
|
||||
} else {
|
||||
w = detail::lambertw_asy(z, k);
|
||||
}
|
||||
} else {
|
||||
w = detail::lambertw_asy(z, k);
|
||||
}
|
||||
|
||||
// Halley's method; see 5.9 in [1]
|
||||
if (w.real() >= 0) {
|
||||
// Rearrange the formula to avoid overflow in exp
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ew = std::exp(-w);
|
||||
wewz = w - z * ew;
|
||||
wn = w - wewz / (w + 1.0 - (w + 2.0) * wewz / (2.0 * w + 2.0));
|
||||
if (std::abs(wn - w) <= tol * std::abs(wn)) {
|
||||
return wn;
|
||||
}
|
||||
w = wn;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ew = std::exp(w);
|
||||
wew = w * ew;
|
||||
wewz = wew - z;
|
||||
wn = w - wewz / (wew + ew - (w + 2.0) * wewz / (2.0 * w + 2.0));
|
||||
if (std::abs(wn - w) <= tol * std::abs(wn)) {
|
||||
return wn;
|
||||
}
|
||||
w = wn;
|
||||
}
|
||||
}
|
||||
|
||||
set_error("lambertw", SF_ERROR_SLOW, "iteration failed to converge: %g + %gj", z.real(), z.imag());
|
||||
return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()};
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<float> lambertw(std::complex<float> z, long k, float tol) {
|
||||
return static_cast<std::complex<float>>(
|
||||
lambertw(static_cast<std::complex<double>>(z), k, static_cast<double>(tol)));
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
@ -0,0 +1,163 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2024.
|
||||
* Original header comment appears below.
|
||||
*/
|
||||
|
||||
/* An implementation of the principal branch of the logarithm of
|
||||
* Gamma. Also contains implementations of Gamma and 1/Gamma which are
|
||||
* easily computed from log-Gamma.
|
||||
*
|
||||
* Author: Josh Wilson
|
||||
*
|
||||
* Distributed under the same license as Scipy.
|
||||
*
|
||||
* References
|
||||
* ----------
|
||||
* [1] Hare, "Computing the Principal Branch of log-Gamma",
|
||||
* Journal of Algorithms, 1997.
|
||||
*
|
||||
* [2] Julia,
|
||||
* https://github.com/JuliaLang/julia/blob/master/base/special/gamma.jl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cephes/gamma.h"
|
||||
#include "cephes/rgamma.h"
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "evalpoly.h"
|
||||
#include "trig.h"
|
||||
#include "zlog1.h"
|
||||
|
||||
namespace special {
|
||||
|
||||
namespace detail {
|
||||
constexpr double loggamma_SMALLX = 7;
|
||||
constexpr double loggamma_SMALLY = 7;
|
||||
constexpr double loggamma_HLOG2PI = 0.918938533204672742; // log(2*pi)/2
|
||||
constexpr double loggamma_LOGPI = 1.1447298858494001741434262; // log(pi)
|
||||
constexpr double loggamma_TAYLOR_RADIUS = 0.2;
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> loggamma_stirling(std::complex<double> z) {
|
||||
/* Stirling series for log-Gamma
|
||||
*
|
||||
* The coefficients are B[2*n]/(2*n*(2*n - 1)) where B[2*n] is the
|
||||
* (2*n)th Bernoulli number. See (1.1) in [1].
|
||||
*/
|
||||
double coeffs[] = {-2.955065359477124183E-2, 6.4102564102564102564E-3, -1.9175269175269175269E-3,
|
||||
8.4175084175084175084E-4, -5.952380952380952381E-4, 7.9365079365079365079E-4,
|
||||
-2.7777777777777777778E-3, 8.3333333333333333333E-2};
|
||||
std::complex<double> rz = 1.0 / z;
|
||||
std::complex<double> rzz = rz / z;
|
||||
|
||||
return (z - 0.5) * std::log(z) - z + loggamma_HLOG2PI + rz * cevalpoly(coeffs, 7, rzz);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> loggamma_recurrence(std::complex<double> z) {
|
||||
/* Backward recurrence relation.
|
||||
*
|
||||
* See Proposition 2.2 in [1] and the Julia implementation [2].
|
||||
*
|
||||
*/
|
||||
int signflips = 0;
|
||||
int sb = 0;
|
||||
std::complex<double> shiftprod = z;
|
||||
|
||||
z += 1.0;
|
||||
int nsb;
|
||||
while (z.real() <= loggamma_SMALLX) {
|
||||
shiftprod *= z;
|
||||
nsb = std::signbit(shiftprod.imag());
|
||||
signflips += nsb != 0 && sb == 0 ? 1 : 0;
|
||||
sb = nsb;
|
||||
z += 1.0;
|
||||
}
|
||||
return loggamma_stirling(z) - std::log(shiftprod) - signflips * 2 * M_PI * std::complex<double>(0, 1);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE std::complex<double> loggamma_taylor(std::complex<double> z) {
|
||||
/* Taylor series for log-Gamma around z = 1.
|
||||
*
|
||||
* It is
|
||||
*
|
||||
* loggamma(z + 1) = -gamma*z + zeta(2)*z**2/2 - zeta(3)*z**3/3 ...
|
||||
*
|
||||
* where gamma is the Euler-Mascheroni constant.
|
||||
*/
|
||||
|
||||
double coeffs[] = {
|
||||
-4.3478266053040259361E-2, 4.5454556293204669442E-2, -4.7619070330142227991E-2, 5.000004769810169364E-2,
|
||||
-5.2631679379616660734E-2, 5.5555767627403611102E-2, -5.8823978658684582339E-2, 6.2500955141213040742E-2,
|
||||
-6.6668705882420468033E-2, 7.1432946295361336059E-2, -7.6932516411352191473E-2, 8.3353840546109004025E-2,
|
||||
-9.0954017145829042233E-2, 1.0009945751278180853E-1, -1.1133426586956469049E-1, 1.2550966952474304242E-1,
|
||||
-1.4404989676884611812E-1, 1.6955717699740818995E-1, -2.0738555102867398527E-1, 2.7058080842778454788E-1,
|
||||
-4.0068563438653142847E-1, 8.2246703342411321824E-1, -5.7721566490153286061E-1};
|
||||
|
||||
z -= 1.0;
|
||||
return z * cevalpoly(coeffs, 22, z);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double loggamma(double x) {
|
||||
if (x < 0.0) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
return cephes::lgam(x);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline float loggamma(float x) { return loggamma(static_cast<double>(x)); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> loggamma(std::complex<double> z) {
|
||||
// Compute the principal branch of log-Gamma
|
||||
|
||||
if (std::isnan(z.real()) || std::isnan(z.imag())) {
|
||||
return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()};
|
||||
}
|
||||
if (z.real() <= 0 and z == std::floor(z.real())) {
|
||||
set_error("loggamma", SF_ERROR_SINGULAR, NULL);
|
||||
return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()};
|
||||
}
|
||||
if (z.real() > detail::loggamma_SMALLX || std::abs(z.imag()) > detail::loggamma_SMALLY) {
|
||||
return detail::loggamma_stirling(z);
|
||||
}
|
||||
if (std::abs(z - 1.0) < detail::loggamma_TAYLOR_RADIUS) {
|
||||
return detail::loggamma_taylor(z);
|
||||
}
|
||||
if (std::abs(z - 2.0) < detail::loggamma_TAYLOR_RADIUS) {
|
||||
// Recurrence relation and the Taylor series around 1.
|
||||
return detail::zlog1(z - 1.0) + detail::loggamma_taylor(z - 1.0);
|
||||
}
|
||||
if (z.real() < 0.1) {
|
||||
// Reflection formula; see Proposition 3.1 in [1]
|
||||
double tmp = std::copysign(2 * M_PI, z.imag()) * std::floor(0.5 * z.real() + 0.25);
|
||||
return std::complex<double>(detail::loggamma_LOGPI, tmp) - std::log(sinpi(z)) - loggamma(1.0 - z);
|
||||
}
|
||||
if (std::signbit(z.imag()) == 0) {
|
||||
// z.imag() >= 0 but is not -0.0
|
||||
return detail::loggamma_recurrence(z);
|
||||
}
|
||||
return std::conj(detail::loggamma_recurrence(std::conj(z)));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<float> loggamma(std::complex<float> z) {
|
||||
return static_cast<std::complex<float>>(loggamma(static_cast<std::complex<double>>(z)));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double rgamma(double z) { return cephes::rgamma(z); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline float rgamma(float z) { return rgamma(static_cast<double>(z)); }
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> rgamma(std::complex<double> z) {
|
||||
// Compute 1/Gamma(z) using loggamma.
|
||||
if (z.real() <= 0 && z == std::floor(z.real())) {
|
||||
// Zeros at 0, -1, -2, ...
|
||||
return 0.0;
|
||||
}
|
||||
return std::exp(-loggamma(z));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<float> rgamma(std::complex<float> z) {
|
||||
return static_cast<std::complex<float>>(rgamma(static_cast<std::complex<double>>(z)));
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
269
venv/lib/python3.12/site-packages/scipy/special/special/tools.h
Normal file
269
venv/lib/python3.12/site-packages/scipy/special/special/tools.h
Normal file
@ -0,0 +1,269 @@
|
||||
/* Building blocks for implementing special functions */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
|
||||
namespace special {
|
||||
namespace detail {
|
||||
|
||||
/* Result type of a "generator", a callable object that produces a value
|
||||
* each time it is called.
|
||||
*/
|
||||
template <typename Generator>
|
||||
using generator_result_t = std::decay_t<std::invoke_result_t<Generator>>;
|
||||
|
||||
/* Used to deduce the type of the numerator/denominator of a fraction. */
|
||||
template <typename Pair>
|
||||
struct pair_traits;
|
||||
|
||||
template <typename T>
|
||||
struct pair_traits<std::pair<T, T>> {
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <typename Pair>
|
||||
using pair_value_t = typename pair_traits<Pair>::value_type;
|
||||
|
||||
/* Used to extract the "value type" of a complex type. */
|
||||
template <typename T>
|
||||
struct real_type {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct real_type<std::complex<T>> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using real_type_t = typename real_type<T>::type;
|
||||
|
||||
// Return NaN, handling both real and complex types.
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE inline std::enable_if_t<std::is_floating_point_v<T>, T> maybe_complex_NaN() {
|
||||
return std::numeric_limits<T>::quiet_NaN();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE inline std::enable_if_t<!std::is_floating_point_v<T>, T> maybe_complex_NaN() {
|
||||
using V = typename T::value_type;
|
||||
return {std::numeric_limits<V>::quiet_NaN(), std::numeric_limits<V>::quiet_NaN()};
|
||||
}
|
||||
|
||||
// Series evaluators.
|
||||
template <typename Generator, typename T = generator_result_t<Generator>>
|
||||
SPECFUN_HOST_DEVICE T series_eval(Generator &g, T init_val, real_type_t<T> tol, std::uint64_t max_terms,
|
||||
const char *func_name) {
|
||||
/* Sum an infinite series to a given precision.
|
||||
*
|
||||
* g : a generator of terms for the series.
|
||||
*
|
||||
* init_val : A starting value that terms are added to. This argument determines the
|
||||
* type of the result.
|
||||
*
|
||||
* tol : relative tolerance for stopping criterion.
|
||||
*
|
||||
* max_terms : The maximum number of terms to add before giving up and declaring
|
||||
* non-convergence.
|
||||
*
|
||||
* func_name : The name of the function within SciPy where this call to series_eval
|
||||
* will ultimately be used. This is needed to pass to set_error in case
|
||||
* of non-convergence.
|
||||
*/
|
||||
T result = init_val;
|
||||
T term;
|
||||
for (std::uint64_t i = 0; i < max_terms; ++i) {
|
||||
term = g();
|
||||
result += term;
|
||||
if (std::abs(term) < std::abs(result) * tol) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Exceeded max terms without converging. Return NaN.
|
||||
set_error(func_name, SF_ERROR_NO_RESULT, NULL);
|
||||
return maybe_complex_NaN<T>();
|
||||
}
|
||||
|
||||
template <typename Generator, typename T = generator_result_t<Generator>>
|
||||
SPECFUN_HOST_DEVICE T series_eval_fixed_length(Generator &g, T init_val, std::uint64_t num_terms) {
|
||||
/* Sum a fixed number of terms from a series.
|
||||
*
|
||||
* g : a generator of terms for the series.
|
||||
*
|
||||
* init_val : A starting value that terms are added to. This argument determines the
|
||||
* type of the result.
|
||||
*
|
||||
* max_terms : The number of terms from the series to sum.
|
||||
*
|
||||
*/
|
||||
T result = init_val;
|
||||
for (std::uint64_t i = 0; i < num_terms; ++i) {
|
||||
result += g();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Performs one step of Kahan summation. */
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE void kahan_step(T& sum, T& comp, T x) {
|
||||
T y = x - comp;
|
||||
T t = sum + y;
|
||||
comp = (t - sum) - y;
|
||||
sum = t;
|
||||
}
|
||||
|
||||
/* Evaluates an infinite series using Kahan summation.
|
||||
*
|
||||
* Denote the series by
|
||||
*
|
||||
* S = a[0] + a[1] + a[2] + ...
|
||||
*
|
||||
* And for n = 0, 1, 2, ..., denote its n-th partial sum by
|
||||
*
|
||||
* S[n] = a[0] + a[1] + ... + a[n]
|
||||
*
|
||||
* This function computes S[0], S[1], ... until a[n] is sufficiently
|
||||
* small or if the maximum number of terms have been evaluated.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* g
|
||||
* Reference to generator that yields the sequence of values a[1],
|
||||
* a[2], a[3], ...
|
||||
*
|
||||
* tol
|
||||
* Relative tolerance for convergence. Specifically, stop iteration
|
||||
* as soon as `abs(a[n]) <= tol * abs(S[n])` for some n >= 1.
|
||||
*
|
||||
* max_terms
|
||||
* Maximum number of terms after a[0] to evaluate. It should be set
|
||||
* large enough such that the convergence criterion is guaranteed
|
||||
* to have been satisfied within that many terms if there is no
|
||||
* rounding error.
|
||||
*
|
||||
* init_val
|
||||
* a[0]. Default is zero. The type of this parameter (T) is used
|
||||
* for intermediary computations as well as the result.
|
||||
*
|
||||
* Return Value
|
||||
* ------------
|
||||
* If the convergence criterion is satisfied by some `n <= max_terms`,
|
||||
* returns `(S[n], n)`. Otherwise, returns `(S[max_terms], 0)`.
|
||||
*/
|
||||
template <typename Generator, typename T = generator_result_t<Generator>>
|
||||
SPECFUN_HOST_DEVICE std::pair<T, std::uint64_t> series_eval_kahan(
|
||||
Generator &&g, real_type_t<T> tol, std::uint64_t max_terms, T init_val = T(0)) {
|
||||
|
||||
T sum = init_val;
|
||||
T comp = 0;
|
||||
for (std::uint64_t i = 0; i < max_terms; ++i) {
|
||||
T term = g();
|
||||
kahan_step(sum, comp, term);
|
||||
if (std::abs(term) <= tol * std::abs(sum)) {
|
||||
return {sum, i + 1};
|
||||
}
|
||||
}
|
||||
return {sum, 0};
|
||||
}
|
||||
|
||||
/* Generator that yields the difference of successive convergents of a
|
||||
* continued fraction.
|
||||
*
|
||||
* Let f[n] denote the n-th convergent of a continued fraction:
|
||||
*
|
||||
* a[1] a[2] a[n]
|
||||
* f[n] = b[0] + ------ ------ ... ----
|
||||
* b[1] + b[2] + b[n]
|
||||
*
|
||||
* with f[0] = b[0]. This generator yields the sequence of values
|
||||
* f[1]-f[0], f[2]-f[1], f[3]-f[2], ...
|
||||
*
|
||||
* Constructor Arguments
|
||||
* ---------------------
|
||||
* cf
|
||||
* Reference to generator that yields the terms of the continued
|
||||
* fraction as (numerator, denominator) pairs, starting from
|
||||
* (a[1], b[1]).
|
||||
*
|
||||
* `cf` must outlive the ContinuedFractionSeriesGenerator object.
|
||||
*
|
||||
* The constructed object always eagerly retrieves the next term
|
||||
* of the continued fraction. Specifically, (a[1], b[1]) is
|
||||
* retrieved upon construction, and (a[n], b[n]) is retrieved after
|
||||
* (n-1) calls of `()`.
|
||||
*
|
||||
* Type Arguments
|
||||
* --------------
|
||||
* T
|
||||
* Type in which computations are performed and results are turned.
|
||||
*
|
||||
* Remarks
|
||||
* -------
|
||||
* The series is computed using the recurrence relation described in [1].
|
||||
*
|
||||
* No error checking is performed. The caller must ensure that all terms
|
||||
* are finite and that intermediary computations do not trigger floating
|
||||
* point exceptions such as overflow.
|
||||
*
|
||||
* The numerical stability of this method depends on the characteristics
|
||||
* of the continued fraction being evaluated.
|
||||
*
|
||||
* Reference
|
||||
* ---------
|
||||
* [1] Gautschi, W. (1967). “Computational Aspects of Three-Term
|
||||
* Recurrence Relations.” SIAM Review, 9(1):24-82.
|
||||
*/
|
||||
template <typename Generator, typename T = pair_value_t<generator_result_t<Generator>>>
|
||||
class ContinuedFractionSeriesGenerator {
|
||||
|
||||
public:
|
||||
explicit ContinuedFractionSeriesGenerator(Generator &cf) : cf_(cf) {
|
||||
init();
|
||||
}
|
||||
|
||||
double operator()() {
|
||||
double v = v_;
|
||||
advance();
|
||||
return v;
|
||||
}
|
||||
|
||||
private:
|
||||
void init() {
|
||||
auto [num, denom] = cf_();
|
||||
T a = num;
|
||||
T b = denom;
|
||||
u_ = T(1);
|
||||
v_ = a / b;
|
||||
b_ = b;
|
||||
}
|
||||
|
||||
void advance() {
|
||||
auto [num, denom] = cf_();
|
||||
T a = num;
|
||||
T b = denom;
|
||||
u_ = T(1) / (T(1) + (a * u_) / (b * b_));
|
||||
v_ *= (u_ - T(1));
|
||||
b_ = b;
|
||||
}
|
||||
|
||||
Generator& cf_; // reference to continued fraction generator
|
||||
T v_; // v[n] == f[n] - f[n-1], n >= 1
|
||||
T u_; // u[1] = 1, u[n] = v[n]/v[n-1], n >= 2
|
||||
T b_; // last denominator, i.e. b[n-1]
|
||||
};
|
||||
|
||||
/* Converts a continued fraction into a series whose terms are the
|
||||
* difference of its successive convergents.
|
||||
*
|
||||
* See ContinuedFractionSeriesGenerator for details.
|
||||
*/
|
||||
template <typename Generator, typename T = pair_value_t<generator_result_t<Generator>>>
|
||||
SPECFUN_HOST_DEVICE ContinuedFractionSeriesGenerator<Generator, T>
|
||||
continued_fraction_series(Generator &cf) {
|
||||
return ContinuedFractionSeriesGenerator<Generator, T>(cf);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace special
|
||||
111
venv/lib/python3.12/site-packages/scipy/special/special/trig.h
Normal file
111
venv/lib/python3.12/site-packages/scipy/special/special/trig.h
Normal file
@ -0,0 +1,111 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2023.
|
||||
*
|
||||
* Original author: Josh Wilson, 2016.
|
||||
*/
|
||||
|
||||
/* Implement sin(pi*z) and cos(pi*z) for complex z. Since the periods
|
||||
* of these functions are integral (and thus better representable in
|
||||
* floating point), it's possible to compute them with greater accuracy
|
||||
* than sin(z), cos(z).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cephes/trig.h"
|
||||
#include "config.h"
|
||||
#include "evalpoly.h"
|
||||
|
||||
namespace special {
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE T sinpi(T x) {
|
||||
return cephes::sinpi(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE std::complex<T> sinpi(std::complex<T> z) {
|
||||
T x = z.real();
|
||||
T piy = M_PI * z.imag();
|
||||
T abspiy = std::abs(piy);
|
||||
T sinpix = cephes::sinpi(x);
|
||||
T cospix = cephes::cospi(x);
|
||||
|
||||
if (abspiy < 700) {
|
||||
return {sinpix * std::cosh(piy), cospix * std::sinh(piy)};
|
||||
}
|
||||
|
||||
/* Have to be careful--sinh/cosh could overflow while cos/sin are small.
|
||||
* At this large of values
|
||||
*
|
||||
* cosh(y) ~ exp(y)/2
|
||||
* sinh(y) ~ sgn(y)*exp(y)/2
|
||||
*
|
||||
* so we can compute exp(y/2), scale by the right factor of sin/cos
|
||||
* and then multiply by exp(y/2) to avoid overflow. */
|
||||
T exphpiy = std::exp(abspiy / 2);
|
||||
T coshfac;
|
||||
T sinhfac;
|
||||
if (exphpiy == std::numeric_limits<T>::infinity()) {
|
||||
if (sinpix == 0.0) {
|
||||
// Preserve the sign of zero.
|
||||
coshfac = std::copysign(0.0, sinpix);
|
||||
} else {
|
||||
coshfac = std::copysign(std::numeric_limits<T>::infinity(), sinpix);
|
||||
}
|
||||
if (cospix == 0.0) {
|
||||
// Preserve the sign of zero.
|
||||
sinhfac = std::copysign(0.0, cospix);
|
||||
} else {
|
||||
sinhfac = std::copysign(std::numeric_limits<T>::infinity(), cospix);
|
||||
}
|
||||
return {coshfac, sinhfac};
|
||||
}
|
||||
|
||||
coshfac = 0.5 * sinpix * exphpiy;
|
||||
sinhfac = 0.5 * cospix * exphpiy;
|
||||
return {coshfac * exphpiy, sinhfac * exphpiy};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE T cospi(T x) {
|
||||
return cephes::cospi(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SPECFUN_HOST_DEVICE std::complex<T> cospi(std::complex<T> z) {
|
||||
T x = z.real();
|
||||
T piy = M_PI * z.imag();
|
||||
T abspiy = std::abs(piy);
|
||||
T sinpix = cephes::sinpi(x);
|
||||
T cospix = cephes::cospi(x);
|
||||
|
||||
if (abspiy < 700) {
|
||||
return {cospix * std::cosh(piy), -sinpix * std::sinh(piy)};
|
||||
}
|
||||
|
||||
// See csinpi(z) for an idea of what's going on here.
|
||||
T exphpiy = std::exp(abspiy / 2);
|
||||
T coshfac;
|
||||
T sinhfac;
|
||||
if (exphpiy == std::numeric_limits<T>::infinity()) {
|
||||
if (sinpix == 0.0) {
|
||||
// Preserve the sign of zero.
|
||||
coshfac = std::copysign(0.0, cospix);
|
||||
} else {
|
||||
coshfac = std::copysign(std::numeric_limits<T>::infinity(), cospix);
|
||||
}
|
||||
if (cospix == 0.0) {
|
||||
// Preserve the sign of zero.
|
||||
sinhfac = std::copysign(0.0, sinpix);
|
||||
} else {
|
||||
sinhfac = std::copysign(std::numeric_limits<T>::infinity(), sinpix);
|
||||
}
|
||||
return {coshfac, sinhfac};
|
||||
}
|
||||
|
||||
coshfac = 0.5 * cospix * exphpiy;
|
||||
sinhfac = 0.5 * sinpix * exphpiy;
|
||||
return {coshfac * exphpiy, sinhfac * exphpiy};
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
@ -0,0 +1,841 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2023.
|
||||
* Original header with Copyright information appears below.
|
||||
*/
|
||||
|
||||
/* Implementation of Wright's generalized Bessel function Phi, see
|
||||
* https://dlmf.nist.gov/10.46.E1
|
||||
*
|
||||
* Copyright: Christian Lorentzen
|
||||
*
|
||||
* Distributed under the same license as SciPy
|
||||
*
|
||||
*
|
||||
* Implementation Overview:
|
||||
*
|
||||
* First, different functions are implemented valid for certain domains of the
|
||||
* three arguments.
|
||||
* Finally they are put together in wright_bessel. See the docstring of
|
||||
* that function for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cephes/lanczos.h"
|
||||
#include "cephes/polevl.h"
|
||||
#include "cephes/rgamma.h"
|
||||
#include "config.h"
|
||||
#include "digamma.h"
|
||||
#include "error.h"
|
||||
|
||||
namespace special {
|
||||
|
||||
namespace detail {
|
||||
// rgamma_zero: smallest value x for which rgamma(x) == 0 as x gets large
|
||||
constexpr double rgamma_zero = 178.47241115886637;
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double exp_rgamma(double x, double y) {
|
||||
/* Compute exp(x) / gamma(y) = exp(x) * rgamma(y).
|
||||
*
|
||||
* This helper function avoids overflow by using the lanczos
|
||||
* approximation of the gamma function.
|
||||
*/
|
||||
return std::exp(x + (1 - std::log(y + cephes::lanczos_g - 0.5)) * (y - 0.5)) /
|
||||
cephes::lanczos_sum_expg_scaled(y);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double wb_series(double a, double b, double x, unsigned int nstart, unsigned int nstop) {
|
||||
/* 1. Taylor series expansion in x=0 for x <= 1.
|
||||
*
|
||||
* Phi(a, b, x) = sum_k x^k / k! / Gamma(a*k + b)
|
||||
*
|
||||
* Note that every term, and therefore also Phi(a, b, x) is
|
||||
* monotone decreasing with increasing a or b.
|
||||
*/
|
||||
double xk_k = std::pow(x, nstart) * cephes::rgamma(nstart + 1); // x^k/k!
|
||||
double res = xk_k * cephes::rgamma(nstart * a + b);
|
||||
// term k=nstart+1, +2, +3, ...
|
||||
if (nstop > nstart) {
|
||||
// series expansion until term k such that a*k+b <= rgamma_zero
|
||||
unsigned int k_max = std::floor((rgamma_zero - b) / a);
|
||||
if (nstop > k_max) {
|
||||
nstop = k_max;
|
||||
}
|
||||
for (unsigned int k = nstart + 1; k < nstop; k++) {
|
||||
xk_k *= x / k;
|
||||
res += xk_k * cephes::rgamma(a * k + b);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<bool log_wb>
|
||||
SPECFUN_HOST_DEVICE inline double wb_large_a(double a, double b, double x, int n) {
|
||||
/* 2. Taylor series expansion in x=0, for large a.
|
||||
*
|
||||
* Phi(a, b, x) = sum_k x^k / k! / Gamma(a*k + b)
|
||||
*
|
||||
* Use Stirling's formula to find k=k_max, the maximum term.
|
||||
* Then use n terms of Taylor series around k_max.
|
||||
*/
|
||||
int k_max = static_cast<int>(std::pow(std::pow(a, -a) * x, 1.0 / (1 + a)));
|
||||
|
||||
int nstart = k_max - n / 2;
|
||||
if (nstart < 0) {
|
||||
nstart = 0;
|
||||
}
|
||||
|
||||
double res = 0;
|
||||
double lnx = std::log(x);
|
||||
// For numerical stability, we factor out the maximum term exp(..) with k=k_max
|
||||
// but only if it is larger than 0.
|
||||
double max_exponent = std::fmax(0, k_max * lnx - cephes::lgam(k_max + 1) - cephes::lgam(a * k_max + b));
|
||||
for (int k = nstart; k < nstart + n; k++) {
|
||||
res += std::exp(k * lnx - cephes::lgam(k + 1) - cephes::lgam(a * k + b) - max_exponent);
|
||||
}
|
||||
|
||||
if (!log_wb) {
|
||||
res *= std::exp(max_exponent);
|
||||
} else {
|
||||
// logarithm of Wright's function
|
||||
res = max_exponent + std::log(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<bool log_wb>
|
||||
SPECFUN_HOST_DEVICE inline double wb_small_a(double a, double b, double x, int order) {
|
||||
/* 3. Taylor series in a=0 up to order 5, for tiny a and not too large x
|
||||
*
|
||||
* Phi(a, b, x) = exp(x)/Gamma(b)
|
||||
* (1 - a*x * Psi(b) + a^2/2*x*(1+x) * (Psi(b)^2 - Psi'(b)
|
||||
+ ... )
|
||||
+ O(a^6))
|
||||
*
|
||||
* where Psi is the digamma function.
|
||||
*
|
||||
* Parameter order takes effect only when b > 1e-3 and 2 <= order <= 5,
|
||||
* otherwise it defaults to 2, or if b <= 1e-3, to 5. The lower order is,
|
||||
* the fewer polygamma functions have to be computed.
|
||||
*
|
||||
* Call: python _precompute/wright_bessel.py 1
|
||||
*
|
||||
* For small b, i.e. b <= 1e-3, cancellation of poles of digamma(b)/Gamma(b)
|
||||
* and polygamma needs to be carried out => series expansion in a=0 to order 5
|
||||
* and in b=0 to order 4.
|
||||
* Call: python _precompute/wright_bessel.py 2
|
||||
*/
|
||||
double A[6]; // coefficients of a^k (1, -x * Psi(b), ...)
|
||||
double B[6]; // powers of b^k/k! or terms in polygamma functions
|
||||
constexpr double C[5] = { // coefficients of a^k1 * b^k2
|
||||
1.0000000000000000, // C[0]
|
||||
1.1544313298030657, // C[1]
|
||||
-3.9352684291215233, // C[2]
|
||||
-1.0080632408182857, // C[3]
|
||||
19.984633365874979, // C[4]
|
||||
};
|
||||
double X[6] = { // polynomials in x;
|
||||
1, // X[0]
|
||||
x, // X[1]
|
||||
x * (x + 1), // X[2]
|
||||
x * (x * (x + 3) + 1), // X[3]
|
||||
x * (x * (x * (x + 6) + 7) + 1), // X[4]
|
||||
x * (x * (x * (x * (x + 10) + 25) + 15) + 1), // X[5]
|
||||
};
|
||||
double res;
|
||||
|
||||
if (b <= 1E-3) {
|
||||
/* Series expansion of both a and b up to order 5:
|
||||
* M_PI = pi
|
||||
* M_EG = Euler Gamma aka Euler Mascheroni constant
|
||||
* M_Z3 = zeta(3)
|
||||
* C[0] = 1
|
||||
* C[1] = 2*M_EG
|
||||
* C[2] = 3*M_EG^2 - M_PI^2/2
|
||||
* C[3] = 4*M_EG^3 - 2*M_EG*M_PI^2 + 8*M_Z3
|
||||
* C[4] = 5*M_EG^4 - 5*M_EG^2*M_PI^2 + 40*M_EG*M_Z3 + M_PI^4/12
|
||||
*/
|
||||
B[0] = 1.;
|
||||
for (int k = 1; k < 5; k++) {
|
||||
B[k] = b / k * B[k - 1];
|
||||
}
|
||||
// Note that polevl assumes inverse ordering => A[5] = 0th term
|
||||
A[5] = cephes::rgamma(b);
|
||||
A[4] = X[1] * (C[0] + C[1] * b + C[2] * B[2] + C[3] * B[3] + C[4] * B[4]);
|
||||
A[3] = X[2] / 2. * (C[1] + C[2] * b + C[3] * B[2] + C[4] * B[3]);
|
||||
A[2] = X[3] / 6. * (C[2] + C[3] * b + C[4] * B[2]);
|
||||
A[1] = X[4] / 24. * (C[3] + C[4] * b);
|
||||
A[0] = X[5] / 120. * C[4];
|
||||
// res = exp(x) * (A[5] + A[4] * a + A[3] * a^2 + A[2] * a^3 + ...)
|
||||
if (!log_wb) {
|
||||
res = exp(x) * cephes::polevl(a, A, 5);
|
||||
} else {
|
||||
// logarithm of Wright's function
|
||||
res = x + std::log(cephes::polevl(a, A, 5));
|
||||
}
|
||||
} else {
|
||||
/* Phi(a, b, x) = exp(x)/gamma(b) * sum(A[i] * X[i] * B[i], i=0..5)
|
||||
* A[n] = a^n/n!
|
||||
* But here, we repurpose A[n] = X[n] * B[n] / n!
|
||||
* Note that polevl assumes inverse ordering => A[order] = 0th term */
|
||||
double dg = digamma(b);
|
||||
// pg1 = polygamma(1, b)
|
||||
double pg1 = cephes::zeta(2, b);
|
||||
if (order <= 2) {
|
||||
res = 1 + a * x * (-dg + 0.5 * a * (1 + x) * (dg * dg - pg1));
|
||||
} else {
|
||||
if (order > 5) {
|
||||
order = 5;
|
||||
}
|
||||
// pg2 = polygamma(2, b)
|
||||
double pg2 = -2 * cephes::zeta(3, b);
|
||||
B[0] = 1;
|
||||
B[1] = -dg;
|
||||
B[2] = dg * dg - pg1;
|
||||
B[3] = (-dg * dg + 3 * pg1) * dg - pg2;
|
||||
A[order] = 1;
|
||||
A[order - 1] = X[1] * B[1];
|
||||
A[order - 2] = X[2] * B[2] / 2.;
|
||||
A[order - 3] = X[3] * B[3] / 6.;
|
||||
if (order >= 4) {
|
||||
// double pg3 = polygamma(3, b)
|
||||
double pg3 = 6 * cephes::zeta(4, b);
|
||||
B[4] = ((dg * dg - 6 * pg1) * dg + 4 * pg2) * dg + 3 * pg1 * pg1 - pg3;
|
||||
A[order - 4] = X[4] * B[4] / 24.;
|
||||
if (order >= 5) {
|
||||
// pg4 = polygamma(4, b)
|
||||
double pg4 = -24 * cephes::zeta(5, b);
|
||||
B[5] =
|
||||
((((-dg * dg + 10 * pg1) * dg - 10 * pg2) * dg - 15 * pg1 * pg1 + 5 * pg3) * dg +
|
||||
10 * pg1 * pg2 - pg4);
|
||||
A[order - 5] = X[5] * B[5] / 120.;
|
||||
}
|
||||
}
|
||||
res = cephes::polevl(a, A, order);
|
||||
}
|
||||
// res *= exp(x) * rgamma(b)
|
||||
if (!log_wb) {
|
||||
res *= exp_rgamma(x, b);
|
||||
} else {
|
||||
// logarithm of Wright's function
|
||||
res = x - cephes::lgam(b) + std::log(res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<bool log_wb>
|
||||
SPECFUN_HOST_DEVICE inline double wb_asymptotic(double a, double b, double x) {
|
||||
/* 4. Asymptotic expansion for large x up to order 8
|
||||
*
|
||||
* Phi(a, b, x) ~ Z^(1/2-b) * exp((1+a)/a * Z) * sum_k (-1)^k * C_k / Z^k
|
||||
*
|
||||
* with Z = (a*x)^(1/(1+a)).
|
||||
* Call: python _precompute/wright_bessel.py 3
|
||||
*/
|
||||
double A[15]; // powers of a
|
||||
double B[17]; // powers of b
|
||||
double Ap1[9]; // powers of (1+a)
|
||||
double C[9]; // coefficients of asymptotic series a_k
|
||||
|
||||
A[0] = 1.;
|
||||
B[0] = 1.;
|
||||
Ap1[0] = 1.;
|
||||
for (int k = 1; k < 15; k++) {
|
||||
A[k] = A[k - 1] * a;
|
||||
}
|
||||
for (int k = 1; k < 17; k++) {
|
||||
B[k] = B[k - 1] * b;
|
||||
}
|
||||
for (int k = 1; k < 9; k++) {
|
||||
Ap1[k] = Ap1[k - 1] * (1 + a);
|
||||
}
|
||||
|
||||
C[0] = 1. / std::sqrt(2. * M_PI * Ap1[1]);
|
||||
|
||||
C[1] = C[0] / (24 * Ap1[1]);
|
||||
C[1] *= (2 * a + 1) * (2 + a) - 12 * b * (1 + a - b);
|
||||
|
||||
C[2] = C[0] / (1152 * Ap1[2]);
|
||||
C[2] *=
|
||||
(144 * B[4] - 96 * B[3] * (5 * a + 1) + 24 * B[2] * (20 * A[2] + 5 * a - 4) -
|
||||
24 * b * Ap1[1] * (6 * A[2] - 7 * a - 2) + (a + 2) * (2 * a + 1) * (2 * A[2] - 19 * a + 2));
|
||||
|
||||
C[3] = C[0] / (414720 * Ap1[3]);
|
||||
C[3] *=
|
||||
(8640 * B[6] - 8640 * B[5] * (7 * a - 1) + 10800 * B[4] * (14 * A[2] - 7 * a - 2) -
|
||||
1440 * B[3] * (112 * A[3] - 147 * A[2] - 63 * a + 8) +
|
||||
180 * B[2] * (364 * A[4] - 1288 * A[3] - 567 * A[2] + 392 * a + 76) -
|
||||
180 * b * Ap1[1] * (20 * A[4] - 516 * A[3] + 417 * A[2] + 172 * a - 12) -
|
||||
(a + 2) * (2 * a + 1) * (556 * A[4] + 1628 * A[3] - 9093 * A[2] + 1628 * a + 556));
|
||||
|
||||
C[4] = C[0] / (39813120 * Ap1[4]);
|
||||
C[4] *=
|
||||
(103680 * B[8] - 414720 * B[7] * (3 * a - 1) + 725760 * B[6] * a * (8 * a - 7) -
|
||||
48384 * B[5] * (274 * A[3] - 489 * A[2] + 39 * a + 26) +
|
||||
30240 * B[4] * (500 * A[4] - 1740 * A[3] + 495 * A[2] + 340 * a - 12) -
|
||||
2880 * B[3] * (2588 * A[5] - 19780 * A[4] + 14453 * A[3] + 9697 * A[2] - 1892 * a - 404) +
|
||||
48 * B[2] *
|
||||
(11488 * A[6] - 547836 * A[5] + 1007484 * A[4] + 593353 * A[3] - 411276 * A[2] - 114396 * a + 4288) +
|
||||
48 * b * Ap1[1] *
|
||||
(7784 * A[6] + 48180 * A[5] - 491202 * A[4] + 336347 * A[3] + 163734 * A[2] - 28908 * a - 5560) -
|
||||
(a + 2) * (2 * a + 1) *
|
||||
(4568 * A[6] - 226668 * A[5] - 465702 * A[4] + 2013479 * A[3] - 465702 * A[2] - 226668 * a + 4568));
|
||||
|
||||
C[5] = C[0] / (6688604160. * Ap1[5]);
|
||||
C[5] *=
|
||||
(1741824 * B[10] - 2903040 * B[9] * (11 * a - 5) + 2177280 * B[8] * (110 * A[2] - 121 * a + 14) -
|
||||
580608 * B[7] * (1628 * A[3] - 3333 * A[2] + 1023 * a + 52) +
|
||||
169344 * B[6] * (12364 * A[4] - 43648 * A[3] + 26763 * A[2] + 1232 * a - 788) -
|
||||
24192 * B[5] * (104852 * A[5] - 646624 * A[4] + 721391 * A[3] - 16841 * A[2] - 74096 * a + 148) +
|
||||
2016 * B[4] *
|
||||
(710248 * A[6] - 8878716 * A[5] + 17928834 * A[4] - 3333407 * A[3] - 4339566 * A[2] + 287364 * a +
|
||||
89128) -
|
||||
1344 * B[3] *
|
||||
(87824 * A[7] - 7150220 * A[6] + 29202756 * A[5] - 15113527 * A[4] - 14223011 * A[3] + 3462492 * A[2] +
|
||||
1137092 * a - 18896) -
|
||||
84 * B[2] *
|
||||
(1690480 * A[8] + 14139136 * A[7] - 232575464 * A[6] + 296712592 * A[5] + 215856619 * A[4] -
|
||||
152181392 * A[3] - 47718440 * A[2] + 5813632 * a + 943216) +
|
||||
84 * b * Ap1[1] *
|
||||
(82224 * A[8] - 5628896 * A[7] - 26466520 * A[6] + 168779208 * A[5] - 104808005 * A[4] -
|
||||
56259736 * A[3] + 15879912 * A[2] + 4020640 * a - 63952) +
|
||||
(a + 2) * (2 * a + 1) *
|
||||
(2622064 * A[8] + 12598624 * A[7] - 167685080 * A[6] - 302008904 * A[5] + 1115235367. * A[4] -
|
||||
302008904 * A[3] - 167685080 * A[2] + 12598624 * a + 2622064));
|
||||
|
||||
C[6] = C[0] / (4815794995200. * Ap1[6]);
|
||||
C[6] *=
|
||||
(104509440 * B[12] - 209018880 * B[11] * (13 * a - 7) + 574801920 * B[10] * (52 * A[2] - 65 * a + 12) -
|
||||
63866880 * B[9] * (2834 * A[3] - 6279 * A[2] + 2769 * a - 134) +
|
||||
23950080 * B[8] * (27404 * A[4] - 98228 * A[3] + 78663 * A[2] - 10868 * a - 1012) -
|
||||
13685760 * B[7] * (105612 * A[5] - 599196 * A[4] + 791843 * A[3] - 224913 * A[2] - 27612 * a + 4540) +
|
||||
2661120 * B[6] *
|
||||
(693680 * A[6] - 6473532 * A[5] + 13736424 * A[4] - 7047469 * A[3] - 723840 * A[2] + 471588 * a + 7376
|
||||
) -
|
||||
2661120 * B[5] *
|
||||
(432536 * A[7] - 7850804 * A[6] + 27531114 * A[5] - 24234457 * A[4] - 703001 * A[3] + 3633474 * A[2] -
|
||||
36244 * a - 45128) +
|
||||
166320 * B[4] *
|
||||
(548912 * A[8] - 75660832 * A[7] + 502902712 * A[6] - 764807992 * A[5] + 91248287 * A[4] +
|
||||
217811464 * A[3] - 20365384 * A[2] - 9776416 * a + 37936) +
|
||||
10080 * B[3] *
|
||||
(18759728 * A[9] + 165932208 * A[8] - 4710418440. * A[7] + 13686052536. * A[6] - 5456818809. * A[5] -
|
||||
6834514245. * A[4] + 1919299512. * A[3] + 752176152 * A[2] - 45661200 * a - 8616848) -
|
||||
360 * B[2] *
|
||||
(32743360 * A[10] - 3381871792. * A[9] - 21488827776. * A[8] + 200389923864. * A[7] -
|
||||
198708005340. * A[6] - 171633799779. * A[5] + 123124874028. * A[4] + 40072774872. * A[3] -
|
||||
9137993280. * A[2] - 1895843248. * a + 18929728) -
|
||||
360 * b * Ap1[1] *
|
||||
(57685408 * A[10] + 406929456 * A[9] - 6125375760. * A[8] - 27094918920. * A[7] +
|
||||
128752249410. * A[6] - 74866710561. * A[5] - 42917416470. * A[4] + 16256951352. * A[3] +
|
||||
4375268400. * A[2] - 316500688 * a - 47197152) +
|
||||
(a + 2) * (2 * a + 1) *
|
||||
(167898208 * A[10] - 22774946512. * A[9] - 88280004528. * A[8] + 611863976472. * A[7] +
|
||||
1041430242126. * A[6] - 3446851131657. * A[5] + 1041430242126. * A[4] + 611863976472. * A[3] -
|
||||
88280004528. * A[2] - 22774946512. * a + 167898208));
|
||||
|
||||
C[7] = C[0] / (115579079884800. * Ap1[7]);
|
||||
C[7] *=
|
||||
(179159040 * B[14] - 1254113280. * B[13] * (5 * a - 3) + 1358622720. * B[12] * (70 * A[2] - 95 * a + 22) -
|
||||
905748480 * B[11] * (904 * A[3] - 2109 * A[2] + 1119 * a - 112) +
|
||||
1245404160. * B[10] * (3532 * A[4] - 12824 * A[3] + 11829 * A[2] - 2824 * a + 44) -
|
||||
59304960 * B[9] * (256820 * A[5] - 1397680 * A[4] + 2025545 * A[3] - 869495 * A[2] + 52000 * a + 8788) +
|
||||
14826240 * B[8] *
|
||||
(2274536 * A[6] - 18601572 * A[5] + 40698318 * A[4] - 28230079 * A[3] + 3916398 * A[2] + 832668 * a -
|
||||
65176) -
|
||||
59304960 * B[7] *
|
||||
(760224 * A[7] - 9849164 * A[6] + 32495784 * A[5] - 34813869 * A[4] + 9175207 * A[3] + 1898688 * A[2] -
|
||||
469788 * a - 13184) +
|
||||
25945920 * B[6] *
|
||||
(1167504 * A[8] - 28779840 * A[7] + 149752856 * A[6] - 246026112 * A[5] + 111944073 * A[4] +
|
||||
18341600 * A[3] - 12131496 * A[2] - 274368 * a + 102800) -
|
||||
157248 * B[5] *
|
||||
(12341872 * A[9] - 3122991216. * A[8] + 29900054232. * A[7] - 78024816720. * A[6] +
|
||||
58914656739. * A[5] + 4637150811. * A[4] - 11523402480. * A[3] + 236218968 * A[2] + 337923216 * a +
|
||||
1592048) -
|
||||
28080 * B[4] *
|
||||
(265154912 * A[10] + 2276098704. * A[9] - 105569461008. * A[8] + 496560666360. * A[7] -
|
||||
627891462858. * A[6] + 41935358025. * A[5] + 203913875814. * A[4] - 23984801544. * A[3] -
|
||||
13869306000. * A[2] + 372786832 * a + 103532640) +
|
||||
1440 * B[3] *
|
||||
(310292864 * A[11] - 55169117872. * A[10] - 358957020112. * A[9] + 5714152556088. * A[8] -
|
||||
13241597459352. * A[7] + 4220720097141. * A[6] + 6845418090249. * A[5] - 2129559215808. * A[4] -
|
||||
909225098472. * A[3] + 107518582576. * A[2] + 25619444368. * a - 113832704) +
|
||||
12 * B[2] *
|
||||
(135319651136. * A[12] + 1119107842176. * A[11] - 22193518174320. * A[10] - 133421793595520. * A[9] +
|
||||
860103051087996. * A[8] - 703353374803080. * A[7] - 704240127687381. * A[6] +
|
||||
513111704637960. * A[5] + 166909061348316. * A[4] - 57671564069120. * A[3] - 12453426246000. * A[2] +
|
||||
695901207936. * a + 93786157376.) -
|
||||
12 * b * Ap1[1] *
|
||||
(4365353408. * A[12] - 720248637504. * A[11] - 4222331152560. * A[10] + 29413934270560. * A[9] +
|
||||
132123980710980. * A[8] - 511247376962820. * A[7] + 283403639131779. * A[6] +
|
||||
170415792320940. * A[5] - 79274388426588. * A[4] - 21009953050400. * A[3] + 3284035340880. * A[2] +
|
||||
589294339776. * a - 3693760576.) -
|
||||
(a + 2) * (2 * a + 1) *
|
||||
(34221025984. * A[12] + 226022948160. * A[11] - 5067505612464. * A[10] - 18868361443936. * A[9] +
|
||||
86215425028308. * A[8] + 143500920544692. * A[7] - 437682618704613. * A[6] + 143500920544692. * A[5] +
|
||||
86215425028308. * A[4] - 18868361443936. * A[3] - 5067505612464. * A[2] + 226022948160. * a +
|
||||
34221025984.));
|
||||
|
||||
C[8] = C[0] / (22191183337881600. * Ap1[8]);
|
||||
C[8] *=
|
||||
(2149908480. * B[16] - 5733089280. * B[15] * (17 * a - 11) +
|
||||
7166361600. * B[14] * (272 * A[2] - 391 * a + 104) -
|
||||
3344302080. * B[13] * (6766 * A[3] - 16371 * A[2] + 9741 * a - 1306) +
|
||||
1811496960. * B[12] * (93092 * A[4] - 341564 * A[3] + 344199 * A[2] - 104924 * a + 6308) -
|
||||
517570560 * B[11] *
|
||||
(1626220 * A[5] - 8641508 * A[4] + 13274773 * A[3] - 6952303 * A[2] + 1007420 * a + 5564) +
|
||||
284663808 * B[10] *
|
||||
(9979136 * A[6] - 75766892 * A[5] + 169256148 * A[4] - 136824959 * A[3] + 35714348 * A[2] -
|
||||
463692 * a - 293664) -
|
||||
1423319040. * B[9] *
|
||||
(4466648 * A[7] - 49231116 * A[6] + 157507414 * A[5] - 187114257 * A[4] + 78372295 * A[3] -
|
||||
4470082 * A[2] - 1913996 * a + 82424) +
|
||||
266872320 * B[8] *
|
||||
(33133136 * A[8] - 564264544 * A[7] + 2618606424. * A[6] - 4491310104. * A[5] + 2853943765. * A[4] -
|
||||
374694552 * A[3] - 135365288 * A[2] + 17623968 * a + 696912) -
|
||||
2156544 * B[7] *
|
||||
(2914256144. * A[9] - 93491712432. * A[8] + 664876176984. * A[7] - 1661362937880. * A[6] +
|
||||
1563719627313. * A[5] - 382840842843. * A[4] - 115399415640. * A[3] + 34565562936. * A[2] +
|
||||
1609337232. * a - 217321904) +
|
||||
179712 * B[6] *
|
||||
(1266018560. * A[10] - 789261834512. * A[9] + 10186841596896. * A[8] - 38877799073352. * A[7] +
|
||||
54334425968952. * A[6] - 22529574889533. * A[5] - 5132942328000. * A[4] + 3438377465592. * A[3] +
|
||||
84287641248. * A[2] - 72493479440. * a - 807415936) +
|
||||
13824 * B[5] *
|
||||
(156356794976. * A[11] + 1180898077328. * A[10] - 90615270907936. * A[9] + 609258947056248. * A[8] -
|
||||
1312655191366722. * A[7] + 885900509321745. * A[6] + 112162151855265. * A[5] -
|
||||
212803071513258. * A[4] + 6805217831352. * A[3] + 10051742651296. * A[2] - 55035924848. * a -
|
||||
52946379296.) -
|
||||
576 * B[4] *
|
||||
(143943926464. * A[12] - 60115486481856. * A[11] - 376366989757200. * A[10] +
|
||||
9534223075576160. * A[9] - 35603777465262396. * A[8] + 39375990156664980. * A[7] -
|
||||
868175004137259. * A[6] - 14279180718355020. * A[5] + 1985747535239364. * A[4] +
|
||||
1264001337603680. * A[3] - 75972792514320. * A[2] - 23855850572736. * a - 4996648256.) -
|
||||
384 * B[3] *
|
||||
(2038525473856. * A[13] + 16057322146112. * A[12] - 502133360559024. * A[11] -
|
||||
2985686417468080. * A[10] + 32418922182093292. * A[9] - 63665380623022452. * A[8] +
|
||||
16481208821092575. * A[7] + 34161547357596099. * A[6] - 11490298497454932. * A[5] -
|
||||
5117272758337156. * A[4] + 933703210750480. * A[3] + 234855186762000. * A[2] - 7860524600000. * a -
|
||||
1226607567040.) +
|
||||
96 * B[2] *
|
||||
(324439754752. * A[14] - 77231415197120. * A[13] - 539102931841856. * A[12] +
|
||||
4618258299956336. * A[11] + 28588485529469792. * A[10] - 141383982651179428. * A[9] +
|
||||
98783147840417772. * A[8] + 112831723492305801. * A[7] - 83329761150975036. * A[6] -
|
||||
26553582937192900. * A[5] + 12469117738765952. * A[4] + 2587165396642160. * A[3] -
|
||||
340406368038080. * A[2] - 53659641606080. * a + 219671272960.) +
|
||||
96 * b * Ap1[1] *
|
||||
(1026630779520. * A[14] + 8781958472768. * A[13] - 210659786204384. * A[12] -
|
||||
1222283505284208. * A[11] + 5064251967491416. * A[10] + 24013052207628140. * A[9] -
|
||||
79710880160087370. * A[8] + 42596558293213227. * A[7] + 26570293386695790. * A[6] -
|
||||
14407831324576884. * A[5] - 3617322833922440. * A[4] + 950664948554384. * A[3] +
|
||||
172358006894496. * A[2] - 7430887938496. * a - 889746675584.) -
|
||||
(a + 2) * (2 * a + 1) *
|
||||
(573840801152. * A[14] - 156998277198784. * A[13] - 898376974770592. * A[12] +
|
||||
8622589006459984. * A[11] + 32874204024803560. * A[10] - 111492707520083828. * A[9] -
|
||||
184768503480287646. * A[8] + 528612016938984183. * A[7] - 184768503480287646. * A[6] -
|
||||
111492707520083828. * A[5] + 32874204024803560. * A[4] + 8622589006459984. * A[3] -
|
||||
898376974770592. * A[2] - 156998277198784. * a + 573840801152.));
|
||||
|
||||
double Z = std::pow(a * x, 1 / Ap1[1]);
|
||||
double Zp = 1.;
|
||||
double res = C[0];
|
||||
for (int k = 1; k < 9; k++) {
|
||||
Zp /= Z;
|
||||
res += (k % 2 == 0 ? 1 : -1) * C[k] * Zp;
|
||||
}
|
||||
if (!log_wb) {
|
||||
res *= std::pow(Z, 0.5 - b) * std::exp(Ap1[1] / a * Z);
|
||||
} else {
|
||||
// logarithm of Wright's function
|
||||
res = std::log(Z) * (0.5 - b) + Ap1[1] / a * Z + std::log(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double wb_Kmod(double exp_term, double eps, double a, double b, double x, double r) {
|
||||
/* Compute integrand Kmod(eps, a, b, x, r) for Gauss-Laguerre quadrature.
|
||||
*
|
||||
* K(a, b, x, r+eps) = exp(-r-eps) * Kmod(eps, a, b, x, r)
|
||||
*
|
||||
* Kmod(eps, a, b, x, r) = exp(x * (r+eps)^(-a) * cos(pi*a)) * (r+eps)^(-b)
|
||||
* * sin(x * (r+eps)^(-a) * sin(pi*a) + pi * b)
|
||||
*
|
||||
* Note that we additionally factor out exp(exp_term) which helps with large
|
||||
* terms in the exponent of exp(...)
|
||||
*/
|
||||
double x_r_a = x * std::pow(r + eps, -a);
|
||||
return std::exp(x_r_a * cephes::cospi(a) + exp_term) * std::pow(r + eps, -b) *
|
||||
std::sin(x_r_a * cephes::sinpi(a) + M_PI * b);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double wb_P(double exp_term, double eps, double a, double b, double x, double phi) {
|
||||
/* Compute integrand P for Gauss-Legendre quadrature.
|
||||
*
|
||||
* P(eps, a, b, x, phi) = exp(eps * cos(phi) + x * eps^(-a) * cos(a*phi))
|
||||
* * cos(eps * sin(phi) - x * eps^(-a) * sin(a*phi)
|
||||
* + (1-b)*phi)
|
||||
*
|
||||
* Note that we additionally factor out exp(exp_term) which helps with large
|
||||
* terms in the exponent of exp(...)
|
||||
*/
|
||||
double x_eps_a = x * std::pow(eps, -a);
|
||||
return std::exp(eps * std::cos(phi) + x_eps_a * std::cos(a * phi) + exp_term) *
|
||||
std::cos(eps * std::sin(phi) - x_eps_a * std::sin(a * phi) + (1 - b) * phi);
|
||||
}
|
||||
|
||||
/* roots of laguerre polynomial of order 50
|
||||
* scipy.special.roots_laguerre(50)[0] or
|
||||
* sympy.integrals.quadrature.import gauss_laguerre(50, 16)[0] */
|
||||
constexpr double wb_x_laguerre[] = {
|
||||
0.02863051833937908, 0.1508829356769337, 0.3709487815348964, 0.6890906998810479, 1.105625023539913,
|
||||
1.620961751102501, 2.23561037591518, 2.950183366641835, 3.765399774405782, 4.682089387559285,
|
||||
5.70119757478489, 6.823790909794551, 8.051063669390792, 9.384345308258407, 10.82510903154915,
|
||||
12.37498160875746, 14.03575459982991, 15.80939719784467, 17.69807093335025, 19.70414653546156,
|
||||
21.83022330657825, 24.0791514444115, 26.45405784125298, 28.95837601193738, 31.59588095662286,
|
||||
34.37072996309045, 37.28751061055049, 40.35129757358607, 43.56772026999502, 46.94304399160304,
|
||||
50.48426796312992, 54.19924488016862, 58.09682801724853, 62.18705417568891, 66.48137387844482,
|
||||
70.99294482661949, 75.73701154772731, 80.73140480247769, 85.99721113646323, 91.55969041253388,
|
||||
97.44956561485056, 103.7048912366923, 110.3738588076403, 117.5191982031112, 125.2254701334734,
|
||||
133.6120279227287, 142.8583254892541, 153.2603719726036, 165.3856433166825, 180.6983437092145
|
||||
};
|
||||
/* weights for laguerre polynomial of order 50
|
||||
* sympy.integrals.quadrature.import gauss_laguerre(50, 16)[1] */
|
||||
constexpr double wb_w_laguerre[] = {
|
||||
0.07140472613518988, 0.1471486069645884, 0.1856716275748313, 0.1843853825273539,
|
||||
0.1542011686063556, 0.1116853699022688, 0.07105288549019586, 0.04002027691150833,
|
||||
0.02005062308007171, 0.008960851203646281, 0.00357811241531566, 0.00127761715678905,
|
||||
0.0004080302449837189, 0.0001165288322309724, 2.974170493694165e-5, 6.777842526542028e-6,
|
||||
1.37747950317136e-6, 2.492886181720092e-7, 4.010354350427827e-8, 5.723331748141425e-9,
|
||||
7.229434249182665e-10, 8.061710142281779e-11, 7.913393099943723e-12, 6.81573661767678e-13,
|
||||
5.13242671658949e-14, 3.365624762437814e-15, 1.913476326965035e-16, 9.385589781827253e-18,
|
||||
3.950069964503411e-19, 1.417749517827512e-20, 4.309970276292175e-22, 1.101257519845548e-23,
|
||||
2.344617755608987e-25, 4.11854415463823e-27, 5.902246763596448e-29, 6.812008916553065e-31,
|
||||
6.237449498812102e-33, 4.452440579683377e-35, 2.426862352250487e-37, 9.852971481049686e-40,
|
||||
2.891078872318428e-42, 5.906162708112361e-45, 8.01287459750397e-48, 6.789575424396417e-51,
|
||||
3.308173010849252e-54, 8.250964876440456e-58, 8.848728128298018e-62, 3.064894889844417e-66,
|
||||
1.988708229330752e-71, 6.049567152238783e-78
|
||||
};
|
||||
/* roots of legendre polynomial of order 50
|
||||
* sympy.integrals.quadrature.import gauss_legendre(50, 16)[0] */
|
||||
constexpr double wb_x_legendre[] = {
|
||||
-0.998866404420071, -0.9940319694320907, -0.9853540840480059, -0.9728643851066921, -0.9566109552428079,
|
||||
-0.9366566189448779, -0.9130785566557919, -0.885967979523613, -0.8554297694299461, -0.8215820708593359,
|
||||
-0.7845558329003993, -0.7444943022260685, -0.7015524687068223, -0.6558964656854394, -0.6077029271849502,
|
||||
-0.5571583045146501, -0.5044581449074642, -0.4498063349740388, -0.3934143118975651, -0.3355002454194374,
|
||||
-0.276288193779532, -0.2160072368760418, -0.1548905899981459, -0.09317470156008614, -0.03109833832718888,
|
||||
0.03109833832718888, 0.09317470156008614, 0.1548905899981459, 0.2160072368760418, 0.276288193779532,
|
||||
0.3355002454194374, 0.3934143118975651, 0.4498063349740388, 0.5044581449074642, 0.5571583045146501,
|
||||
0.6077029271849502, 0.6558964656854394, 0.7015524687068223, 0.7444943022260685, 0.7845558329003993,
|
||||
0.8215820708593359, 0.8554297694299461, 0.885967979523613, 0.9130785566557919, 0.9366566189448779,
|
||||
0.9566109552428079, 0.9728643851066921, 0.9853540840480059, 0.9940319694320907, 0.998866404420071
|
||||
};
|
||||
/* weights for legendre polynomial of order 50
|
||||
* sympy.integrals.quadrature.import gauss_legendre(50, 16)[1] */
|
||||
constexpr double wb_w_legendre[] = {
|
||||
0.002908622553155141, 0.006759799195745401, 0.01059054838365097, 0.01438082276148557, 0.01811556071348939,
|
||||
0.02178024317012479, 0.02536067357001239, 0.0288429935805352, 0.03221372822357802, 0.03545983561514615,
|
||||
0.03856875661258768, 0.0415284630901477, 0.04432750433880328, 0.04695505130394843, 0.04940093844946632,
|
||||
0.05165570306958114, 0.05371062188899625, 0.05555774480621252, 0.05718992564772838, 0.05860084981322245,
|
||||
0.05978505870426546, 0.06073797084177022, 0.06145589959031666, 0.06193606742068324, 0.06217661665534726,
|
||||
0.06217661665534726, 0.06193606742068324, 0.06145589959031666, 0.06073797084177022, 0.05978505870426546,
|
||||
0.05860084981322245, 0.05718992564772838, 0.05555774480621252, 0.05371062188899625, 0.05165570306958114,
|
||||
0.04940093844946632, 0.04695505130394843, 0.04432750433880328, 0.0415284630901477, 0.03856875661258768,
|
||||
0.03545983561514615, 0.03221372822357802, 0.0288429935805352, 0.02536067357001239, 0.02178024317012479,
|
||||
0.01811556071348939, 0.01438082276148557, 0.01059054838365097, 0.006759799195745401, 0.002908622553155141
|
||||
};
|
||||
/* Fitted parameters for optimal choice of eps
|
||||
* Call: python _precompute/wright_bessel.py 4 */
|
||||
constexpr double wb_A[] = {0.41037, 0.30833, 6.9952, 18.382, -2.8566, 2.1122};
|
||||
|
||||
template<bool log_wb>
|
||||
SPECFUN_HOST_DEVICE inline double wright_bessel_integral(double a, double b, double x) {
|
||||
/* 5. Integral representation
|
||||
*
|
||||
* K(a, b, x, r) = exp(-r + x * r^(-a) * cos(pi*a)) * r^(-b)
|
||||
* * sin(x * r^(-a) * sin(pi*a) + pi * b)
|
||||
* P(eps, a, b, x, phi) = exp(eps * cos(phi) + x * eps^(-a) * cos(a*phi))
|
||||
* * cos(eps * sin(phi) - x * eps^(-a) * sin(a*phi)
|
||||
* + (1-b)*phi)
|
||||
*
|
||||
* Phi(a, b, x) = 1/pi * int_eps^inf K(a, b, x, r) * dr
|
||||
* + eps^(1-b)/pi * int_0^pi P(eps, a, b, x, phi) * dphi
|
||||
*
|
||||
* for any eps > 0.
|
||||
*
|
||||
* Note that P has a misprint in Luchko (2008) Eq. 9, the cos(phi(beta-1)) at
|
||||
* the end of the first line should be removed and the −sin(phi(beta−1)) at
|
||||
* the end of the second line should read +(1-b)*phi.
|
||||
* This integral representation introduced the free parameter eps (from the
|
||||
* radius of complex contour integration). We try to choose eps such that
|
||||
* the integrand behaves smoothly. Note that this is quite diffrent from how
|
||||
* Luchko (2008) deals with eps: he is either looking for the limit eps -> 0
|
||||
* or he sets (silently) eps=1. But having the freedom to set eps is much more
|
||||
* powerful for numerical evaluation.
|
||||
*
|
||||
* As K has a leading exp(-r), we factor this out and apply Gauss-Laguerre
|
||||
* quadrature rule:
|
||||
*
|
||||
* int_0^inf K(a, b, x, r+eps) dr = exp(-eps) int_0^inf exp(-r) Kmod(.., r) dr
|
||||
*
|
||||
* Note the shift r -> r+eps to have integation from 0 to infinity.
|
||||
* The integral over P is done via a Gauss-Legendre quadrature rule.
|
||||
*
|
||||
* Note: Hardest argument range is large z, large b and small eps.
|
||||
*/
|
||||
|
||||
/* We use the free choice of eps to make the integral better behaved.
|
||||
* 1. Concern is oscillatory behaviour of P. Therefore, we'd like to
|
||||
* make the change in the argument of cosine small, i.e. make arc length
|
||||
* int_0^phi sqrt(1 + f'(phi)^2) dphi small, with
|
||||
* f(phi) = eps * sin(phi) - x * eps^(-a) * sin(a*phi) + (1-b)*phi
|
||||
* Proxy, make |f'(phi)| small.
|
||||
* 2. Concern is int_0 K ~ int_0 (r+eps)^(-b) .. dr
|
||||
* This is difficult as r -> 0 for large b. It behaves better for larger
|
||||
* values of eps.
|
||||
*/
|
||||
|
||||
// Minimize oscillatory behavoir of P
|
||||
double eps =
|
||||
(wb_A[0] * b * std::exp(-0.5 * a) +
|
||||
std::exp(
|
||||
wb_A[1] + 1 / (1 + a) * std::log(x) - wb_A[2] * std::exp(-wb_A[3] * a) +
|
||||
wb_A[4] / (1 + std::exp(wb_A[5] * a))
|
||||
));
|
||||
|
||||
if (a >= 4 && x >= 100) {
|
||||
eps += 1; // This part is hard to fit
|
||||
}
|
||||
|
||||
// Large b
|
||||
if (b >= 8) {
|
||||
/* Make P small compared to K by setting eps large enough.
|
||||
* int K ~ exp(-eps) and int P ~ eps^(1-b) */
|
||||
eps = std::fmax(eps, std::pow(b, -b / (1. - b)) + 0.1 * b);
|
||||
}
|
||||
|
||||
// safeguard, higher better for larger a, lower better for tiny a.
|
||||
eps = std::fmin(eps, 150.);
|
||||
eps = std::fmax(eps, 3.); // 3 seems to be a pretty good choice in general.
|
||||
|
||||
// We factor out exp(-exp_term) from wb_Kmod and wb_P to avoid overflow of
|
||||
// exp(..).
|
||||
double exp_term = 0;
|
||||
// From the exponent of K:
|
||||
double r = wb_x_laguerre[50-1]; // largest value of x used in wb_Kmod
|
||||
double x_r_a = x * std::pow(r + eps, -a);
|
||||
exp_term = std::fmax(exp_term, x_r_a * cephes::cospi(a));
|
||||
// From the exponent of P:
|
||||
double x_eps_a = x * std::pow(eps, -a);
|
||||
// phi = 0 => cos(phi) = cos(a * phi) = 1
|
||||
exp_term = std::fmax(exp_term, eps + x_eps_a);
|
||||
// phi = pi => cos(phi) = -1
|
||||
exp_term = std::fmax(exp_term, -eps + x_eps_a * cephes::cospi(a));
|
||||
|
||||
double res1 = 0;
|
||||
double res2 = 0;
|
||||
|
||||
double y;
|
||||
for (int k = 0; k < 50; k++) {
|
||||
res1 += wb_w_laguerre[k] * wb_Kmod(-exp_term, eps, a, b, x, wb_x_laguerre[k]);
|
||||
// y = (b-a)*(x+1)/2.0 + a for integration from a=0 to b=pi
|
||||
y = M_PI * (wb_x_legendre[k] + 1) / 2.0;
|
||||
res2 += wb_w_legendre[k] * wb_P(-exp_term, eps, a, b, x, y);
|
||||
}
|
||||
res1 *= std::exp(-eps);
|
||||
// (b-a)/2.0 * np.sum(w*func(y, *args), axis=-1)
|
||||
res2 *= M_PI / 2.0;
|
||||
res2 *= std::pow(eps, 1 - b);
|
||||
|
||||
if (!log_wb) {
|
||||
// Remember the factored out exp_term from wb_Kmod and wb_P
|
||||
return std::exp(exp_term) / M_PI * (res1 + res2);
|
||||
} else {
|
||||
// logarithm of Wright's function
|
||||
return exp_term + std::log((res1 + res2) / M_PI);
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template<bool log_wb>
|
||||
SPECFUN_HOST_DEVICE inline double wright_bessel_t(double a, double b, double x) {
|
||||
/* Compute Wright's generalized Bessel function for scalar arguments.
|
||||
*
|
||||
* According to [1], it is an entire function defined as
|
||||
*
|
||||
* .. math:: \Phi(a, b; x) = \sum_{k=0}^\infty \frac{x^k}{k! \Gamma(a k + b)}
|
||||
*
|
||||
* So far, only non-negative values of rho=a, beta=b and z=x are implemented.
|
||||
* There are 5 different approaches depending on the ranges of the arguments:
|
||||
*
|
||||
* 1. Taylor series expansion in x=0 [1], for x <= 1.
|
||||
* Involves gamma funtions in each term.
|
||||
* 2. Taylor series expansion in x=0 [2], for large a.
|
||||
* 3. Taylor series in a=0, for tiny a and not too large x.
|
||||
* 4. Asymptotic expansion for large x [3, 4].
|
||||
* Suitable for large x while still small a and b.
|
||||
* 5. Integral representation [5], in principle for all arguments.
|
||||
*
|
||||
* References
|
||||
* ----------
|
||||
* [1] https://dlmf.nist.gov/10.46.E1
|
||||
* [2] P. K. Dunn, G. K. Smyth (2005), Series evaluation of Tweedie exponential
|
||||
* dispersion model densities. Statistics and Computing 15 (2005): 267-280.
|
||||
* [3] E. M. Wright (1935), The asymptotic expansion of the generalized Bessel
|
||||
* function. Proc. London Math. Soc. (2) 38, pp. 257-270.
|
||||
* https://doi.org/10.1112/plms/s2-38.1.257
|
||||
* [4] R. B. Paris (2017), The asymptotics of the generalised Bessel function,
|
||||
* Mathematica Aeterna, Vol. 7, 2017, no. 4, 381 - 406,
|
||||
* https://arxiv.org/abs/1711.03006
|
||||
* [5] Y. F. Luchko (2008), Algorithms for Evaluation of the Wright Function for
|
||||
* the Real Arguments' Values, Fractional Calculus and Applied Analysis 11(1)
|
||||
* http://sci-gems.math.bas.bg/jspui/bitstream/10525/1298/1/fcaa-vol11-num1-2008-57p-75p.pdf
|
||||
*/
|
||||
if (std::isnan(a) || std::isnan(b) || std::isnan(x)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
if (a < 0 || b < 0 || x < 0) {
|
||||
set_error("wright_bessel", SF_ERROR_DOMAIN, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
if (std::isinf(x)) {
|
||||
if (std::isinf(a) || std::isinf(b)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
if (std::isinf(a) || std::isinf(b)) {
|
||||
return std::numeric_limits<double>::quiet_NaN(); // or 0
|
||||
}
|
||||
if (a >= detail::rgamma_zero || b >= detail::rgamma_zero) {
|
||||
set_error("wright_bessel", SF_ERROR_OVERFLOW, NULL);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
if (x == 0) {
|
||||
// return rgamma(b)
|
||||
if (!log_wb) {
|
||||
return cephes::rgamma(b);
|
||||
} else {
|
||||
// logarithm of Wright's function
|
||||
return -cephes::lgam(b);
|
||||
}
|
||||
}
|
||||
if (a == 0) {
|
||||
// return exp(x) * rgamma(b)
|
||||
if (!log_wb) {
|
||||
return detail::exp_rgamma(x, b);
|
||||
} else {
|
||||
// logarithm of Wright's function
|
||||
return x - cephes::lgam(b);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr double exp_inf = 709.78271289338403;
|
||||
int order;
|
||||
if ((a <= 1e-3 && b <= 50 && x <= 9) || (a <= 1e-4 && b <= 70 && x <= 100) ||
|
||||
(a <= 1e-5 && b <= 170 && (x < exp_inf || (log_wb && x <= 1e3)))) {
|
||||
/* Taylor Series expansion in a=0 to order=order => precision <= 1e-11
|
||||
* If beta is also small => precision <= 1e-11.
|
||||
* max order = 5 */
|
||||
if (a <= 1e-5) {
|
||||
if (x <= 1) {
|
||||
order = 2;
|
||||
} else if (x <= 10) {
|
||||
order = 3;
|
||||
} else if (x <= 100) {
|
||||
order = 4;
|
||||
} else { // x < exp_inf
|
||||
order = 5;
|
||||
}
|
||||
} else if (a <= 1e-4) {
|
||||
if (x <= 1e-2) {
|
||||
order = 2;
|
||||
} else if (x <= 1) {
|
||||
order = 3;
|
||||
} else if (x <= 10) {
|
||||
order = 4;
|
||||
} else { // x <= 100
|
||||
order = 5;
|
||||
}
|
||||
} else { // a <= 1e-3
|
||||
if (x <= 1e-5) {
|
||||
order = 2;
|
||||
} else if (x <= 1e-1) {
|
||||
order = 3;
|
||||
} else if (x <= 1) {
|
||||
order = 4;
|
||||
} else { // x <= 9
|
||||
order = 5;
|
||||
}
|
||||
}
|
||||
|
||||
return detail::wb_small_a<log_wb>(a, b, x, order);
|
||||
}
|
||||
|
||||
if (x <= 1) {
|
||||
// 18 term Taylor Series => error mostly smaller 5e-14
|
||||
double res = detail::wb_series(a, b, x, 0, 18);
|
||||
if (log_wb) res = std::log(res);
|
||||
return res;
|
||||
}
|
||||
if (x <= 2) {
|
||||
// 20 term Taylor Series => error mostly smaller 1e-12 to 1e-13
|
||||
return detail::wb_series(a, b, x, 0, 20);
|
||||
}
|
||||
if (a >= 5) {
|
||||
/* Taylor series around the approximate maximum term.
|
||||
* Set number of terms=order. */
|
||||
if (a >= 10) {
|
||||
if (x <= 1e11) {
|
||||
order = 6;
|
||||
} else {
|
||||
order = static_cast<int>(std::fmin(std::log10(x) - 5 + b / 10, 30));
|
||||
}
|
||||
} else {
|
||||
if (x <= 1e4) {
|
||||
order = 6;
|
||||
} else if (x <= 1e8) {
|
||||
order = static_cast<int>(2 * std::log10(x));
|
||||
} else if (x <= 1e10) {
|
||||
order = static_cast<int>(4 * std::log10(x) - 16);
|
||||
} else {
|
||||
order = static_cast<int>(std::fmin(6 * std::log10(x) - 36, 100));
|
||||
}
|
||||
}
|
||||
return detail::wb_large_a<log_wb>(a, b, x, order);
|
||||
}
|
||||
if (std::pow(a * x, 1 / (1. + a)) >= 14 + b * b / (2 * (1 + a))) {
|
||||
/* Asymptotic expansion in Z = (a*x)^(1/(1+a)) up to 8th term 1/Z^8.
|
||||
* For 1/Z^k, the highest term in b is b^(2*k) * a0 / (2^k k! (1+a)^k).
|
||||
* As a0 is a common factor to all orders, this explains a bit the
|
||||
* domain of good convergence set above.
|
||||
* => precision ~ 1e-11 but can go down to ~1e-8 or 1e-7
|
||||
* Note: We ensured a <= 5 as this is a bad approximation for large a. */
|
||||
return detail::wb_asymptotic<log_wb>(a, b, x);
|
||||
}
|
||||
if (0.5 <= a && a <= 1.8 && 100 <= b && 1e5 <= x) {
|
||||
// This is a very hard domain. This condition is placed after wb_asymptotic.
|
||||
// TODO: Explore ways to cover this domain.
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
return detail::wright_bessel_integral<log_wb>(a, b, x);
|
||||
}
|
||||
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double wright_bessel(double a, double b, double x) {
|
||||
return wright_bessel_t<false>(a, b, x);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline float wright_bessel(float a, float b, float x) {
|
||||
return wright_bessel(static_cast<double>(a), static_cast<double>(b), static_cast<double>(x));
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline double log_wright_bessel(double a, double b, double x) {
|
||||
return wright_bessel_t<true>(a, b, x);
|
||||
}
|
||||
|
||||
SPECFUN_HOST_DEVICE inline float log_wright_bessel(float a, float b, float x) {
|
||||
return log_wright_bessel(static_cast<double>(a), static_cast<double>(b), static_cast<double>(x));
|
||||
}
|
||||
|
||||
} // namespace special
|
||||
@ -0,0 +1,35 @@
|
||||
/* Translated from Cython into C++ by SciPy developers in 2023.
|
||||
*
|
||||
* Original author: Josh Wilson, 2016.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
namespace special {
|
||||
namespace detail {
|
||||
|
||||
SPECFUN_HOST_DEVICE inline std::complex<double> zlog1(std::complex<double> z) {
|
||||
/* Compute log, paying special attention to accuracy around 1. We
|
||||
* implement this ourselves because some systems (most notably the
|
||||
* Travis CI machines) are weak in this regime. */
|
||||
std::complex<double> coeff = -1.0;
|
||||
std::complex<double> res = 0.0;
|
||||
|
||||
if (std::abs(z - 1.0) > 0.1) {
|
||||
return std::log(z);
|
||||
}
|
||||
|
||||
z -= 1.0;
|
||||
for (int n = 1; n < 17; n++) {
|
||||
coeff *= -z;
|
||||
res += coeff / static_cast<double>(n);
|
||||
if (std::abs(res / coeff) < std::numeric_limits<double>::epsilon()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace special
|
||||
@ -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,25 @@
|
||||
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
|
||||
|
||||
py3.extension_module(
|
||||
'extending',
|
||||
'extending.pyx',
|
||||
install: false,
|
||||
)
|
||||
|
||||
extending_cpp = fs.copyfile('extending.pyx', 'extending_cpp.pyx')
|
||||
py3.extension_module(
|
||||
'extending_cpp',
|
||||
extending_cpp,
|
||||
install: false,
|
||||
override_options : ['cython_language=cpp']
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user