Sam Chaudry
Upload folder using huggingface_hub
7885a28 verified
raw
history blame
42.6 kB
/* 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 xsf {
namespace detail {
// rgamma_zero: smallest value x for which rgamma(x) == 0 as x gets large
constexpr double rgamma_zero = 178.47241115886637;
XSF_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);
}
XSF_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>
XSF_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>
XSF_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>
XSF_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;
}
XSF_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);
}
XSF_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>
XSF_HOST_DEVICE inline double wright_bessel_integral(double a, double b, double x) {
/* 5. Integral representation
*
* K(a, b, x, r) = exp(-r + x * r^(-a) * cos(pi*a)) * r^(-b)
* * sin(x * r^(-a) * sin(pi*a) + pi * b)
* P(eps, a, b, x, phi) = exp(eps * cos(phi) + x * eps^(-a) * cos(a*phi))
* * cos(eps * sin(phi) - x * eps^(-a) * sin(a*phi)
* + (1-b)*phi)
*
* Phi(a, b, x) = 1/pi * int_eps^inf K(a, b, x, r) * dr
* + eps^(1-b)/pi * int_0^pi P(eps, a, b, x, phi) * dphi
*
* for any eps > 0.
*
* Note that P has a misprint in Luchko (2008) Eq. 9, the cos(phi(beta-1)) at
* the end of the first line should be removed and the −sin(phi(beta−1)) at
* the end of the second line should read +(1-b)*phi.
* This integral representation introduced the free parameter eps (from the
* radius of complex contour integration). We try to choose eps such that
* the integrand behaves smoothly. Note that this is quite diffrent from how
* Luchko (2008) deals with eps: he is either looking for the limit eps -> 0
* or he sets (silently) eps=1. But having the freedom to set eps is much more
* powerful for numerical evaluation.
*
* As K has a leading exp(-r), we factor this out and apply Gauss-Laguerre
* quadrature rule:
*
* int_0^inf K(a, b, x, r+eps) dr = exp(-eps) int_0^inf exp(-r) Kmod(.., r) dr
*
* Note the shift r -> r+eps to have integation from 0 to infinity.
* The integral over P is done via a Gauss-Legendre quadrature rule.
*
* Note: Hardest argument range is large z, large b and small eps.
*/
/* We use the free choice of eps to make the integral better behaved.
* 1. Concern is oscillatory behaviour of P. Therefore, we'd like to
* make the change in the argument of cosine small, i.e. make arc length
* int_0^phi sqrt(1 + f'(phi)^2) dphi small, with
* f(phi) = eps * sin(phi) - x * eps^(-a) * sin(a*phi) + (1-b)*phi
* Proxy, make |f'(phi)| small.
* 2. Concern is int_0 K ~ int_0 (r+eps)^(-b) .. dr
* This is difficult as r -> 0 for large b. It behaves better for larger
* values of eps.
*/
// Minimize oscillatory behavoir of P
double eps =
(wb_A[0] * b * std::exp(-0.5 * a) +
std::exp(
wb_A[1] + 1 / (1 + a) * std::log(x) - wb_A[2] * std::exp(-wb_A[3] * a) +
wb_A[4] / (1 + std::exp(wb_A[5] * a))
));
if (a >= 4 && x >= 100) {
eps += 1; // This part is hard to fit
}
// Large b
if (b >= 8) {
/* Make P small compared to K by setting eps large enough.
* int K ~ exp(-eps) and int P ~ eps^(1-b) */
eps = std::fmax(eps, std::pow(b, -b / (1. - b)) + 0.1 * b);
}
// safeguard, higher better for larger a, lower better for tiny a.
eps = std::fmin(eps, 150.);
eps = std::fmax(eps, 3.); // 3 seems to be a pretty good choice in general.
// We factor out exp(-exp_term) from wb_Kmod and wb_P to avoid overflow of
// exp(..).
double exp_term = 0;
// From the exponent of K:
double r = wb_x_laguerre[50-1]; // largest value of x used in wb_Kmod
double x_r_a = x * std::pow(r + eps, -a);
exp_term = std::fmax(exp_term, x_r_a * cephes::cospi(a));
// From the exponent of P:
double x_eps_a = x * std::pow(eps, -a);
// phi = 0 => cos(phi) = cos(a * phi) = 1
exp_term = std::fmax(exp_term, eps + x_eps_a);
// phi = pi => cos(phi) = -1
exp_term = std::fmax(exp_term, -eps + x_eps_a * cephes::cospi(a));
double res1 = 0;
double res2 = 0;
double y;
for (int k = 0; k < 50; k++) {
res1 += wb_w_laguerre[k] * wb_Kmod(-exp_term, eps, a, b, x, wb_x_laguerre[k]);
// y = (b-a)*(x+1)/2.0 + a for integration from a=0 to b=pi
y = M_PI * (wb_x_legendre[k] + 1) / 2.0;
res2 += wb_w_legendre[k] * wb_P(-exp_term, eps, a, b, x, y);
}
res1 *= std::exp(-eps);
// (b-a)/2.0 * np.sum(w*func(y, *args), axis=-1)
res2 *= M_PI / 2.0;
res2 *= std::pow(eps, 1 - b);
if (!log_wb) {
// Remember the factored out exp_term from wb_Kmod and wb_P
return std::exp(exp_term) / M_PI * (res1 + res2);
} else {
// logarithm of Wright's function
return exp_term + std::log((res1 + res2) / M_PI);
}
}
} // namespace detail
template<bool log_wb>
XSF_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
double res = detail::wb_series(a, b, x, 0, 20);
if (log_wb) res = std::log(res);
return res;
}
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);
}
XSF_HOST_DEVICE inline double wright_bessel(double a, double b, double x) {
return wright_bessel_t<false>(a, b, x);
}
XSF_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));
}
XSF_HOST_DEVICE inline double log_wright_bessel(double a, double b, double x) {
return wright_bessel_t<true>(a, b, x);
}
XSF_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 xsf