/* 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 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(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 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 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 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 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::quiet_NaN(); } if (a < 0 || b < 0 || x < 0) { set_error("wright_bessel", SF_ERROR_DOMAIN, NULL); return std::numeric_limits::quiet_NaN(); } if (std::isinf(x)) { if (std::isinf(a) || std::isinf(b)) { return std::numeric_limits::quiet_NaN(); } return std::numeric_limits::infinity(); } if (std::isinf(a) || std::isinf(b)) { return std::numeric_limits::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::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(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(std::fmin(std::log10(x) - 5 + b / 10, 30)); } } else { if (x <= 1e4) { order = 6; } else if (x <= 1e8) { order = static_cast(2 * std::log10(x)); } else if (x <= 1e10) { order = static_cast(4 * std::log10(x) - 16); } else { order = static_cast(std::fmin(6 * std::log10(x) - 36, 100)); } } return detail::wb_large_a(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(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::quiet_NaN(); } return detail::wright_bessel_integral(a, b, x); } XSF_HOST_DEVICE inline double wright_bessel(double a, double b, double x) { return wright_bessel_t(a, b, x); } XSF_HOST_DEVICE inline float wright_bessel(float a, float b, float x) { return wright_bessel(static_cast(a), static_cast(b), static_cast(x)); } XSF_HOST_DEVICE inline double log_wright_bessel(double a, double b, double x) { return wright_bessel_t(a, b, x); } XSF_HOST_DEVICE inline float log_wright_bessel(float a, float b, float x) { return log_wright_bessel(static_cast(a), static_cast(b), static_cast(x)); } } // namespace xsf