RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
special_functions.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <concepts>
5#include <limits>
6#include <stdexcept>
7#include <string>
8#include <type_traits>
9
11
12namespace randlib
13{
14
21
22namespace math
23{
24
30template <std::floating_point Real>
31Real besselI(std::type_identity_t<Real> nu, Real x);
32
38template <std::floating_point Real>
39Real besselK(std::type_identity_t<Real> nu, Real x);
40
45template <std::floating_point Real>
46Real riemannZeta(Real s);
47
52template <std::floating_point Real>
53Real lgammaCorrection(Real x);
54
55namespace portable
56{
57template <std::floating_point Real>
58Real besselI(std::type_identity_t<Real> nu, Real x);
59template <std::floating_point Real>
60Real besselK(std::type_identity_t<Real> nu, Real x);
61template <std::floating_point Real>
62Real riemannZeta(Real s);
63} // namespace portable
64
65} // namespace math
66
67} // namespace randlib
68
69#include <randlib/math/beta.hpp>
75
76namespace randlib
77{
78
79#if !defined(RANDLIB_PORTABLE_SPECIAL_FUNCTIONS) && defined(__cpp_lib_math_special_functions)
80#define RANDLIB_HAS_STD_SPECIAL_FUNCTIONS
81#endif
82
83namespace math
84{
85
86namespace detail
87{
88
89template <std::floating_point Real>
90inline constexpr Real machineEpsilon = std::numeric_limits<Real>::epsilon();
91constexpr int maxSeriesIterations = 100000;
92
98template <std::floating_point Real>
99inline void reciprocalGammaTerms(Real mu, Real &gam1, Real &gam2, Real &gampl, Real &gammi)
100{
101 gampl = 1.0 / std::tgamma(1.0 + mu);
102 gammi = 1.0 / std::tgamma(1.0 - mu);
103 gam2 = 0.5 * (gammi + gampl);
104 if (std::fabs(mu) < 0.01) {
106 static constexpr Real a2 = 0.5772156649015328606L, a4 = -0.0420026350340952355L, a6 = -0.0421977345555443367L,
107 a8 = 0.0072189432466630995L;
108 Real muSq = mu * mu;
109 gam1 = -(a2 + muSq * (a4 + muSq * (a6 + muSq * a8)));
110 } else {
111 gam1 = (gammi - gampl) / (2 * mu);
112 }
113}
114
122template <std::floating_point Real>
123inline void besselIK(Real nu, Real x, Real &ri, Real &rk)
124{
125 constexpr Real FPMIN = std::numeric_limits<Real>::min() / machineEpsilon<Real>;
126 constexpr Real XMIN = 2;
127 const int nl = static_cast<int>(nu + 0.5);
128 const Real xmu = nu - nl, xmu2 = xmu * xmu;
129 const Real xi = 1 / x, xi2 = 2 * xi;
130
132 Real h = std::max(nu * xi, FPMIN);
133 Real b = xi2 * nu, d = 0, c = h;
134 for (int i = 0; i < maxSeriesIterations; ++i) {
135 b += xi2;
136 d = 1.0 / (b + d);
137 c = b + 1.0 / c;
138 Real del = c * d;
139 h *= del;
140 if (std::fabs(del - 1) < machineEpsilon<Real>)
141 break;
142 }
143
145 Real ril = FPMIN, ripl = h * ril;
146 const Real ril1 = ril;
147 Real fact = nu * xi;
148 for (int l = nl - 1; l >= 0; --l) {
149 Real ritemp = fact * ril + ripl;
150 fact -= xi;
151 ripl = fact * ritemp + ril;
152 ril = ritemp;
153 }
154 const Real f = ripl / ril;
155
156 Real rkmu = 0, rk1 = 0;
157 if (x < XMIN) {
159 Real x2 = Real{0.5} * x, pimu = static_cast<Real>(randlib::consts::pi) * xmu;
160 Real factor = (std::fabs(pimu) < machineEpsilon<Real>) ? Real{1} : pimu / std::sin(pimu);
161 Real dd = -std::log(x2), e = xmu * dd;
162 Real factor2 = (std::fabs(e) < machineEpsilon<Real>) ? Real{1} : std::sinh(e) / e;
163 Real gam1, gam2, gampl, gammi;
164 reciprocalGammaTerms(xmu, gam1, gam2, gampl, gammi);
165 Real ff = factor * (gam1 * std::cosh(e) + gam2 * factor2 * dd);
166 Real sum = ff;
167 e = std::exp(e);
168 Real p = Real{0.5} * e / gampl, q = Real{0.5} / (e * gammi), cc = 1, x2Sq = x2 * x2;
169 Real sum1 = p;
170 for (int i = 1; i <= maxSeriesIterations; ++i) {
171 ff = (i * ff + p + q) / (i * i - xmu2);
172 cc *= x2Sq / i;
173 p /= i - xmu;
174 q /= i + xmu;
175 Real del = cc * ff;
176 sum += del;
177 sum1 += cc * (p - i * ff);
178 if (std::fabs(del) < std::fabs(sum) * machineEpsilon<Real>)
179 break;
180 }
181 rkmu = sum;
182 rk1 = sum1 * xi2;
183 } else {
185 Real bb = 2 * (1 + x), dd = 1 / bb, hh = dd, delh = dd;
186 Real q1 = 0, q2 = 1, a1 = Real{0.25} - xmu2, q = a1, cc = a1, a = -a1;
187 Real s = 1 + q * delh;
188 for (int i = 1; i < maxSeriesIterations; ++i) {
189 a -= 2 * i;
190 cc = -a * cc / (i + 1.0);
191 Real qnew = (q1 - bb * q2) / a;
192 q1 = q2;
193 q2 = qnew;
194 q += cc * qnew;
195 bb += 2.0;
196 dd = 1.0 / (bb + a * dd);
197 delh = (bb * dd - 1.0) * delh;
198 hh += delh;
199 Real dels = q * delh;
200 s += dels;
201 if (std::fabs(dels / s) < machineEpsilon<Real>)
202 break;
203 }
204 hh *= a1;
205 rkmu = std::sqrt(static_cast<Real>(randlib::consts::pi) / (2 * x)) * std::exp(-x) / s;
206 rk1 = rkmu * (xmu + x + 0.5 - hh) * xi;
207 }
208
210 Real rkmup = xmu * xi * rkmu - rk1;
211 Real rimu = xi / (f * rkmu - rkmup);
212 ri = (rimu * ril1) / ril;
213 for (int i = 1; i <= nl; ++i) {
214 Real rktemp = (xmu + i) * xi2 * rk1 + rkmu;
215 rkmu = rk1;
216 rk1 = rktemp;
217 }
218 rk = rkmu;
219}
220
221template <std::floating_point Real>
222inline void checkBesselArguments(Real nu, Real x)
223{
224 if (nu < 0.0)
225 throw std::domain_error("Bessel function: order should be non-negative, but it's equal to " +
226 std::to_string(nu));
227 if (x < 0.0)
228 throw std::domain_error("Bessel function: argument should be non-negative, but it's equal to " +
229 std::to_string(x));
230}
231
232} // namespace detail
233
234namespace portable
235{
236
237template <std::floating_point Real>
238inline Real besselI(std::type_identity_t<Real> nu, Real x)
239{
241 if (x == 0.0)
242 return (nu == 0.0) ? 1.0 : 0.0;
243 const Real overflowLimit = std::numeric_limits<Real>::max_exponent == std::numeric_limits<double>::max_exponent
244 ? Real{700}
245 : std::log(std::numeric_limits<Real>::max()) - Real{10};
246 if (x > overflowLimit) {
248 return std::numeric_limits<Real>::infinity();
249 }
250 if (x < 5.0 || x * x < 4.0 * (nu + 1.0)) {
252 Real y = Real{0.25} * x * x, term = 1, sum = 1;
253 for (int k = 1; k < detail::maxSeriesIterations; ++k) {
254 term *= y / (k * (nu + k));
255 sum += term;
256 if (term < detail::machineEpsilon<Real> * sum)
257 break;
258 }
259 return std::exp(nu * std::log(0.5 * x) - std::lgamma(nu + 1.0)) * sum;
260 }
261 Real ri = 0, rk = 0;
262 detail::besselIK(nu, x, ri, rk);
263 return ri;
264}
265
266template <std::floating_point Real>
267inline Real besselK(std::type_identity_t<Real> nu, Real x)
268{
270 if (x == 0.0)
271 return std::numeric_limits<Real>::infinity();
272 const Real underflowLimit = std::numeric_limits<Real>::min_exponent == std::numeric_limits<double>::min_exponent
273 ? Real{745}
274 : -std::log(std::numeric_limits<Real>::denorm_min());
275 if (x > underflowLimit)
276 return 0.0;
277 Real ri = 0, rk = 0;
278 detail::besselIK(nu, x, ri, rk);
279 return rk;
280}
281
282template <std::floating_point Real>
283inline Real riemannZeta(Real s)
284{
285 if (!(s > 1.0))
286 throw std::domain_error("Riemann zeta function: argument should be greater than 1, but it's equal to " +
287 std::to_string(s));
288 if (s > 60.0)
289 return Real{1} + std::pow(Real{2}, -s) + std::pow(Real{3}, -s);
290
292 static constexpr int N = 20;
293 static constexpr Real B2jOverFact[] = {Real{1} / 12,
294 -Real{1} / 720,
295 Real{1} / 30240,
296 -Real{1} / 1209600,
297 Real{1} / 47900160,
298 -Real{691} / Real{1307674368000},
299 Real{1} / Real{74724249600},
300 -Real{3617} / Real{10670622842880000},
301 Real{43867} / Real{5109094217170944000},
302 -Real{174611} / static_cast<Real>(802857662698291200000.0L)};
303 Real sum = 0;
304 for (int k = 1; k < N; ++k)
305 sum += std::pow(static_cast<Real>(k), -s);
306 Real nPow = std::pow(static_cast<Real>(N), -s);
307 sum += N * nPow / (s - 1) + Real{0.5} * nPow;
308 Real rising = s, term = nPow / N;
309 for (int j = 0; j < 10; ++j) {
310 sum += B2jOverFact[j] * rising * term;
311 rising *= (s + 2 * j + 1) * (s + 2 * j + 2);
312 term /= static_cast<Real>(N) * N;
313 }
314 return sum;
315}
316
317} // namespace portable
318
319template <std::floating_point Real>
320inline Real besselI(std::type_identity_t<Real> nu, Real x)
321{
322#ifdef RANDLIB_HAS_STD_SPECIAL_FUNCTIONS
323 return std::cyl_bessel_i(nu, x);
324#else
325 return portable::besselI(nu, x);
326#endif
327}
328
329template <std::floating_point Real>
330inline Real besselK(std::type_identity_t<Real> nu, Real x)
331{
332#ifdef RANDLIB_HAS_STD_SPECIAL_FUNCTIONS
333 return std::cyl_bessel_k(nu, x);
334#else
335 return portable::besselK(nu, x);
336#endif
337}
338
339template <std::floating_point Real>
340inline Real riemannZeta(Real s)
341{
342#ifdef RANDLIB_HAS_STD_SPECIAL_FUNCTIONS
343 return std::riemann_zeta(s);
344#else
345 return portable::riemannZeta(s);
346#endif
347}
348
349template <std::floating_point Real>
350inline Real lgammaCorrection(Real x)
351{
352 if (x < 10.0)
353 throw std::domain_error("Stirling correction: argument should be at least 10, but it's equal to " +
354 std::to_string(x));
355 static constexpr Real c[] = {Real{1} / 12, -Real{1} / 360, Real{1} / 1260, -Real{1} / 1680,
356 Real{1} / 1188, -Real{691} / 360360, Real{1} / 156};
357 Real xInv = 1 / x, xInvSq = xInv * xInv;
358 Real sum = c[6];
359 for (int i = 5; i >= 0; --i)
360 sum = sum * xInvSq + c[i];
361 return sum * xInv;
362}
363
364} // namespace math
365
366} // namespace randlib
constexpr long double pi
Definition constants.hpp:22
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
void reciprocalGammaTerms(Real mu, Real &gam1, Real &gam2, Real &gampl, Real &gammi)
Values needed by Temme's series for |μ| ≤ 1/2: gam1 = (1/Γ(1-μ) - 1/Γ(1+μ)) / (2μ),...
Definition special_functions.hpp:99
constexpr Real machineEpsilon
Definition special_functions.hpp:90
void besselIK(Real nu, Real x, Real &ri, Real &rk)
Temme's method with Steed's continued fraction (Numerical Recipes, "bessik")
Definition special_functions.hpp:123
constexpr int maxSeriesIterations
Definition special_functions.hpp:91
void checkBesselArguments(Real nu, Real x)
Definition special_functions.hpp:222
Real besselK(std::type_identity_t< Real > nu, Real x)
Definition special_functions.hpp:267
Real riemannZeta(Real s)
Definition special_functions.hpp:283
Real besselI(std::type_identity_t< Real > nu, Real x)
Definition special_functions.hpp:238
Real besselI(std::type_identity_t< Real > nu, Real x)
Definition special_functions.hpp:320
Real lgammaCorrection(Real x)
Definition special_functions.hpp:350
Real riemannZeta(Real s)
Definition special_functions.hpp:340
Real besselK(std::type_identity_t< Real > nu, Real x)
Definition special_functions.hpp:330
Definition bivariate_normal.hpp:10