This commit is contained in:
2024-11-29 18:15:30 +00:00
parent 40aade2d8e
commit bc9415586e
5298 changed files with 1938676 additions and 80 deletions

View 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__)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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)

View 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)

View 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

View 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))

File diff suppressed because it is too large Load Diff

View 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: ...

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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,
)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View 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

View 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

View File

@ -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)

View File

@ -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)

View File

@ -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]: ...

View 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}>"

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View File

@ -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

View 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

View File

@ -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)

View 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)

View File

@ -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

View File

@ -0,0 +1,3 @@
from typing import Any
def __getattr__(name) -> Any: ...

View File

@ -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)

View 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)

View 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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View 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

View 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

View File

@ -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(beta1)) 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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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