1 // -*- C++ -*-
2 /**
3 * @brief OpenTURNS wrapper to a library of special functions
4 *
5 * Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6 *
7 * This library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21 #include <cmath>
22 #include <limits>
23 #include <cstdlib>
24 #include <iostream>
25
26 #include "openturns/SpecFunc.hxx"
27 #include "openturns/BetaFunctions.hxx"
28 #include "openturns/ExponentialIntegralFunctions.hxx"
29 #include "openturns/Debye.hxx"
30 #include "Faddeeva.hh"
31 #include "openturns/GammaFunctions.hxx"
32 #include "openturns/Exception.hxx"
33 #include "openturns/ResourceMap.hxx"
34 #include "openturns/Function.hxx"
35 #include "openturns/GaussKronrod.hxx"
36 #include "openturns/PlatformInfo.hxx"
37
38 #ifdef OPENTURNS_HAVE_BOOST
39
40 #include <boost/math/special_functions/bessel.hpp>
41 #include <boost/version.hpp>
42
43 #ifdef OPENTURNS_HAVE_MPC
44 #include <boost/multiprecision/mpc.hpp>
45 #endif
46
47 #ifdef OPENTURNS_HAVE_MPFR
48 #include <boost/multiprecision/mpfr.hpp>
49 #endif
50
51 #if (BOOST_VERSION >= 105600)
52
53 #include <boost/math/special_functions/bessel_prime.hpp>
54
55 #endif
56
57 #else
58
59 #include "openturns/GaussKronrod.hxx"
60 #include "openturns/SymbolicFunction.hxx"
61 #include "openturns/Interval.hxx"
62
63 #endif
64
65 BEGIN_NAMESPACE_OPENTURNS
66
67 // 0.39894228040143267 = 1 / sqrt(2.pi)
68 const Scalar SpecFunc::ISQRT2PI = 0.3989422804014326779399462;
69 // 2.5066282746310005024 = sqrt(2.pi)
70 const Scalar SpecFunc::SQRT2PI = 2.506628274631000502415765;
71 // 0.91893853320467274177 = log(sqrt(2.pi))
72 const Scalar SpecFunc::LOGSQRT2PI = 0.91893853320467274178;
73 // 0.57721566490153286 = Euler constant gamma
74 const Scalar SpecFunc::EulerConstant = 0.57721566490153286;
75 // 1.64493406684822643 = pi^2 / 6
76 const Scalar SpecFunc::PI2_6 = 1.64493406684822643;
77 // 1.28254983016118640 = pi / sqrt(6)
78 const Scalar SpecFunc::PI_SQRT6 = 1.28254983016118640;
79 // 0.45005320754569466 = gamma * sqrt(6) / pi
80 const Scalar SpecFunc::EULERSQRT6_PI = 0.45005320754569466;
81 // 3.28986813369645287 = pi^2 / 3
82 const Scalar SpecFunc::PI2_3 = 3.28986813369645287;
83 // 0.55132889542179204 = sqrt(3) / pi
84 const Scalar SpecFunc::SQRT3_PI = 0.55132889542179204;
85 // 1.81379936423421785 = pi / sqrt(3)
86 const Scalar SpecFunc::PI_SQRT3 = 1.81379936423421785;
87 // 6.283185307179586476925286 = 2*pi
88 const Scalar SpecFunc::TWOPI = 6.283185307179586476925286;
89 // 1.20205690315959429 = Zeta(3)
90 const Scalar SpecFunc::ZETA3 = 1.20205690315959429;
91 // Scalar limits
92 const Scalar SpecFunc::MinScalar = std::numeric_limits<Scalar>::min();
93 const Scalar SpecFunc::LogMinScalar = std::log(MinScalar);
94 const Scalar SpecFunc::MaxScalar = std::numeric_limits<Scalar>::max();
95 const Scalar SpecFunc::LogMaxScalar = std::log(MaxScalar);
96 const Scalar SpecFunc::LowestScalar = -MaxScalar;
97 const Scalar SpecFunc::ScalarEpsilon = std::numeric_limits<Scalar>::epsilon();
98 // Maximum number of iterations for the algorithms
99 const UnsignedInteger SpecFunc::MaximumIteration = ResourceMap::GetAsUnsignedInteger("SpecFunc-MaximumIteration");
100 const Scalar SpecFunc::Precision = ResourceMap::GetAsScalar("SpecFunc-Precision");
101
102 // Information about capabilities
IsBoostAvailable()103 Bool SpecFunc::IsBoostAvailable()
104 {
105 #ifdef OPENTURNS_HAVE_BOOST
106 return true;
107 #else
108 return false;
109 #endif
110 }
111
IsMPFRAvailable()112 Bool SpecFunc::IsMPFRAvailable()
113 {
114 #ifdef OPENTURNS_HAVE_MPFR
115 return true;
116 #else
117 return false;
118 #endif
119 }
120
IsMPCAvailable()121 Bool SpecFunc::IsMPCAvailable()
122 {
123 #ifdef OPENTURNS_HAVE_MPC
124 return true;
125 #else
126 return false;
127 #endif
128 }
129
130 // Some facilities for NaN and inf
IsNaN(const Scalar value)131 Bool SpecFunc::IsNaN(const Scalar value)
132 {
133 return value != value;
134 }
135
IsInf(const Scalar value)136 Bool SpecFunc::IsInf(const Scalar value)
137 {
138 return (value == value) && IsNaN(value - value);
139 }
140
IsNormal(const Scalar value)141 Bool SpecFunc::IsNormal(const Scalar value)
142 {
143 return value - value == 0.0;
144 }
145
146 // Modified first kind Bessel function of order 0: BesselI0(x) = \sum_{m=0}\infty\frac{1}{m!^2}\left(\frac{x}{2}\right)^{2m}
SmallCaseBesselI0(const Scalar x)147 Scalar SpecFunc::SmallCaseBesselI0(const Scalar x)
148 {
149 const Scalar x2 = x * x;
150 Scalar value = 1.0;
151 Scalar r = 1.0;
152 UnsignedInteger k = 1;
153 while ((std::abs(r / value) > 0.0) && (k < SpecFunc::MaximumIteration))
154 {
155 r *= 0.25 * x2 / (k * k);
156 value += r;
157 ++k;
158 }
159 return value;
160 }
161
LargeCaseLogBesselI0(const Scalar x)162 Scalar SpecFunc::LargeCaseLogBesselI0(const Scalar x)
163 {
164 static Scalar A[12] = {0.125, 7.03125e-02,
165 7.32421875e-02, 1.1215209960938e-01,
166 2.2710800170898e-01, 5.7250142097473e-01,
167 1.7277275025845, 6.0740420012735,
168 2.4380529699556e+01, 1.1001714026925e+02,
169 5.5133589612202e+02, 3.0380905109224e+03
170 };
171 const Scalar ax = std::abs(x);
172 UnsignedInteger k0 = 12;
173 if (ax >= 35.0) k0 = 9;
174 if (ax >= 50.0) k0 = 7;
175 Scalar value = 1.0;
176 const Scalar xR = 1.0 / ax;
177 Scalar xRPow = xR;
178 for (UnsignedInteger k = 0; k < k0; ++k)
179 {
180 value += A[k] * xRPow;
181 xRPow *= xR;
182 }
183 value = std::log(value) + ax - 0.5 * std::log(2.0 * M_PI * ax);
184 return value;
185 }
186
BesselI0(const Scalar x)187 Scalar SpecFunc::BesselI0(const Scalar x)
188 {
189 if (x == 0.0) return 1.0;
190 // Small argument
191 if (std::abs(x) <= 23.5) return SmallCaseBesselI0(x);
192 // Large argument
193 else return std::exp(LargeCaseLogBesselI0(x));
194 }
195
LogBesselI0(const Scalar x)196 Scalar SpecFunc::LogBesselI0(const Scalar x)
197 {
198 if (x == 0.0) return 0.0;
199 // Small argument
200 if (std::abs(x) <= 23.5) return std::log(SmallCaseBesselI0(x));
201 // Large argument
202 else return LargeCaseLogBesselI0(x);
203 }
204
205 // Modified first kind Bessel function of order 1: BesselI1(x) = \sum_{m=0}\infty\frac{1}{m!(m+1)!}\left(\frac{x}{2}\right)^{2m+1}
SmallCaseBesselI1(const Scalar x)206 Scalar SpecFunc::SmallCaseBesselI1(const Scalar x)
207 {
208 const Scalar x2 = x * x;
209 Scalar value = 1.0;
210 Scalar r = 1.0;
211 UnsignedInteger k = 1;
212 while ((std::abs(r / value) > 0.0) && (k < SpecFunc::MaximumIteration))
213 {
214 r *= 0.25 * x2 / (k * (k + 1));
215 value += r;
216 ++k;
217 }
218 value *= 0.5 * x;
219 return value;
220 }
221
LargeCaseLogBesselI1(const Scalar x)222 Scalar SpecFunc::LargeCaseLogBesselI1(const Scalar x)
223 {
224 static Scalar B[12] = { -0.375, -1.171875e-01,
225 -1.025390625e-01, -1.4419555664063e-01,
226 -2.7757644653320e-01, -6.7659258842468e-01,
227 -1.9935317337513, -6.8839142681099,
228 -2.7248827311269e+01, -1.2159789187654e+02,
229 -6.0384407670507e+02, -3.3022722944809e+03
230 };
231 const Scalar ax = std::abs(x);
232 UnsignedInteger k0 = 12;
233 if (ax >= 35.0) k0 = 9;
234 if (ax >= 50.0) k0 = 7;
235 Scalar value = 1.0;
236 const Scalar xR = 1.0 / ax;
237 Scalar xRPow = xR;
238 for (UnsignedInteger k = 0; k < k0; ++k)
239 {
240 value += B[k] * xRPow;
241 xRPow *= xR;
242 }
243 value = std::log(value) + ax - 0.5 * std::log(2.0 * M_PI * ax);
244 return value;
245 }
246
BesselI1(const Scalar x)247 Scalar SpecFunc::BesselI1(const Scalar x)
248 {
249 if (x == 0.0) return 0.0;
250 // Small argument
251 if (std::abs(x) <= 22.0) return SmallCaseBesselI1(x);
252 else
253 {
254 const Scalar signX = x <= 0.0 ? -1.0 : 1.0;
255 const Scalar value = signX * std::exp(LargeCaseLogBesselI1(x));
256 return value;
257 }
258 }
259
LogBesselI1(const Scalar x)260 Scalar SpecFunc::LogBesselI1(const Scalar x)
261 {
262 if (x <= 0.0) return LowestScalar;
263 // Small argument
264 if (std::abs(x) <= 22.0) return std::log(SmallCaseBesselI1(x));
265 else return LargeCaseLogBesselI1(x);
266 }
267
268 // Difference between the logarithms of BesselI1 and BesselI0:
269 // DeltaLogBesselI10(x) = log(BesselI1(x)) - log(BesselI0(x))
LargeCaseDeltaLogBesselI10(const Scalar x)270 Scalar SpecFunc::LargeCaseDeltaLogBesselI10(const Scalar x)
271 {
272 static Scalar A[12] = {0.125, 7.03125e-02,
273 7.32421875e-02, 1.1215209960938e-01,
274 2.2710800170898e-01, 5.7250142097473e-01,
275 1.7277275025845, 6.0740420012735,
276 2.4380529699556e+01, 1.1001714026925e+02,
277 5.5133589612202e+02, 3.0380905109224e+03
278 };
279 static Scalar B[12] = { -0.375, -1.171875e-01,
280 -1.025390625e-01, -1.4419555664063e-01,
281 -2.7757644653320e-01, -6.7659258842468e-01,
282 -1.9935317337513, -6.8839142681099,
283 -2.7248827311269e+01, -1.2159789187654e+02,
284 -6.0384407670507e+02, -3.3022722944809e+03
285 };
286 const Scalar ax = std::abs(x);
287 UnsignedInteger k0 = 12;
288 if (ax >= 35.0) k0 = 9;
289 if (ax >= 50.0) k0 = 7;
290 Scalar valueI0 = 1.0;
291 Scalar valueI1 = 1.0;
292 const Scalar xR = 1.0 / ax;
293 Scalar xRPow = xR;
294 for (UnsignedInteger k = 0; k < k0; ++k)
295 {
296 valueI0 += A[k] * xRPow;
297 valueI1 += B[k] * xRPow;
298 xRPow *= xR;
299 }
300 return std::log(valueI1) - std::log(valueI0);
301 }
302
DeltaLogBesselI10(const Scalar x)303 Scalar SpecFunc::DeltaLogBesselI10(const Scalar x)
304 {
305 if (!(x > 0.0)) return LowestScalar;
306 // Small argument. The threshold is such that the relative error is less than
307 // epsilon machine in double precision (Maple)
308 if (!(x > 0.848296173838189821792665527416e-2)) return std::log(0.5 * x) + x * x * (-0.125 + 5.0 * x * x / 384.0);
309 // Medium argument
310 if (!(x > 22.0)) return std::log(SmallCaseBesselI1(x) / SmallCaseBesselI0(x));
311 // Large argument
312 else return LargeCaseDeltaLogBesselI10(x);
313 }
314
315 class LogBesselKIntegrandEvaluation : public EvaluationImplementation
316 {
317 public:
LogBesselKIntegrandEvaluation(const Scalar nu,const Scalar x)318 LogBesselKIntegrandEvaluation(const Scalar nu, const Scalar x) : EvaluationImplementation(), nu_(nu), x_(x) {}
clone() const319 LogBesselKIntegrandEvaluation * clone() const override
320 {
321 return new LogBesselKIntegrandEvaluation(*this);
322 }
getInputDimension() const323 UnsignedInteger getInputDimension() const override
324 {
325 return 1;
326 }
getOutputDimension() const327 UnsignedInteger getOutputDimension() const override
328 {
329 return 1;
330 }
331
operator ()(const Point & inP) const332 Point operator()(const Point & inP) const override
333 {
334 const Scalar t = inP[0];
335 Point outP(1);
336 if (nu_ == 0)
337 outP[0] = exp(-x_ * cosh(t));
338 else
339 outP[0] = exp(-x_ * cosh(t)) * std::pow(sinh(t), 2.0 * nu_);
340 return outP;
341 }
342
343 private:
344 Scalar nu_ = 0.0;
345 Scalar x_ = 0.0;
346 };
347
348 // Logarithm of the modified second kind Bessel function of order nu: LogBesselK(nu, x)=log(\frac{\pi}{2}\frac{I_{-\nu}(x)-I_[\nu}(x)}{\sin{\nu\pi}})
LogBesselK(const Scalar nu,const Scalar x)349 Scalar SpecFunc::LogBesselK(const Scalar nu,
350 const Scalar x)
351 {
352 if (!(x > 0.0)) throw InvalidArgumentException(HERE) << "Error: x must be positive, here x=" << x;
353 // Reflection formula
354 if (nu < 0.0) return LogBesselK(-nu, x);
355 // Special cases
356 if (nu == 0.5) return 0.5 * std::log(M_PI / (2.0 * x)) - x;
357 if (nu == 1.5)
358 {
359 const Scalar num = 1.0 + 1.0 / x;
360 return 0.5 * std::log(M_PI * num * num / (2.0 * x)) - x;
361 }
362 if (nu == 2.5)
363 {
364 const Scalar num = 1.0 + (3.0 / x) * (1.0 + 1.0 / x);
365 return 0.5 * std::log(M_PI * num * num / (2.0 * x)) - x;
366 }
367 #ifdef OPENTURNS_HAVE_BOOST
368 return std::log(boost::math::cyl_bessel_k(nu, x));
369 #else
370 Scalar logFactor = 0.0;
371 const Function integrand(LogBesselKIntegrandEvaluation(nu, x));
372 UnsignedInteger precision = PlatformInfo::GetNumericalPrecision();
373 PlatformInfo::SetNumericalPrecision(16);
374 Scalar upper = -1.0;
375 if (nu == 0.0)
376 {
377 upper = std::log(-2.0 * std::log(ScalarEpsilon) / x);
378 }
379 else
380 {
381 logFactor = nu * std::log(0.5 * x) - LogGamma(0.5 + nu) + 0.5 * std::log(M_PI);
382 upper = std::log(ScalarEpsilon) / (2.0 * nu) - LambertW(-0.25 * x * std::exp(0.5 * std::log(ScalarEpsilon) / nu) / nu, false);
383 }
384 Scalar epsilon = -1.0;
385 const Scalar integral = GaussKronrod().integrate(integrand, Interval(ScalarEpsilon, upper), epsilon)[0];
386 PlatformInfo::SetNumericalPrecision(precision);
387 if (!IsNormal(integral) || (integral == 0.0)) return LowestScalar;
388 return logFactor + std::log(integral);
389 #endif
390 }
391
392 // Modified second kind Bessel function of order nu: BesselK(nu, x)=\frac{\pi}{2}\frac{I_{-\nu}(x)-I_[\nu}(x)}{\sin{\nu\pi}}
BesselK(const Scalar nu,const Scalar x)393 Scalar SpecFunc::BesselK(const Scalar nu,
394 const Scalar x)
395 {
396 #ifdef OPENTURNS_HAVE_BOOST
397 return boost::math::cyl_bessel_k(nu, x);
398 #else
399 if (!(x > 0.0)) throw InvalidArgumentException(HERE) << "Error: x must be positive, here x=" << x;
400 // Reflection formula
401 if (nu < 0.0) return BesselK(-nu, x);
402 // First the limit cases
403 if ((std::abs(x) < 0.0056) && (nu == 0.0))
404 {
405 const Scalar logX = std::log(x);
406 const Scalar x2 = 0.25 * x * x;
407 return M_LN2 - logX - EulerConstant + x2 * (M_LN2 - logX + 1.0 - EulerConstant + 0.25 * x2 * (M_LN2 - logX + 1.5 - EulerConstant));
408 }
409 if (std::abs(x) < 1e-8) return 0.5 * std::exp(LogGamma(nu) - nu * std::log(0.5 * x));
410 if ((std::abs(x) > 1e4) && (x > nu)) return std::sqrt(M_PI / (2.0 * x)) * std::exp(-x);
411 const Scalar logK = LogBesselK(nu, x);
412 if (logK >= LogMaxScalar) return MaxScalar;
413 return std::exp(logK);
414 #endif
415 }
416
417 // Modified second kind Bessel derivative function of order nu: BesselKDerivative(nu, x)=dBesselK(nu, x) / dx
BesselKDerivative(const Scalar nu,const Scalar x)418 Scalar SpecFunc::BesselKDerivative(const Scalar nu,
419 const Scalar x)
420 {
421 #if defined(OPENTURNS_HAVE_BOOST) && (BOOST_VERSION >= 105600)
422 return boost::math::cyl_bessel_k_prime(nu, x);
423 #else
424 if (x == 0.0) return LowestScalar;
425 return -0.5 * (BesselK(nu - 1, x) + BesselK(nu + 1.0, x));
426 #endif
427 }
428
429 // LnBeta function: LnBeta(a, b) = \log(Beta(a, b))
LnBeta(const Scalar a,const Scalar b)430 Scalar SpecFunc::LnBeta(const Scalar a,
431 const Scalar b)
432 {
433 const Scalar first = std::min(a, b);
434 if (!(first > 0.0)) throw InvalidArgumentException(HERE) << "Error: cannot compute the LogBeta function when a or b is nonpositive, a=" << a << ", b=" << b;
435 const Scalar second = std::max(a, b);
436 const Scalar sum = a + b;
437 // Common case: a and b small
438 if (second < 7.75) return lgamma(first) - lgamma(sum) + lgamma(second);
439 const Scalar correctionSecond = GammaCorrection(second);
440 const Scalar correctionSum = GammaCorrection(sum);
441 // Case a and b large
442 if (first >= 7.75)
443 {
444 // b not very large wrt a
445 if (second < 103.25 * first) return correctionSecond - correctionSum + GammaCorrection(first) - 0.5 * std::log(second) + second * std::log(second / first) + (sum - 0.5) * log1p(-second / sum) + LOGSQRT2PI;
446 else
447 {
448 const Scalar epsilon = 1.0 / second;
449 Scalar value = std::log(epsilon);
450 // Here we use an expansion of (log(Gamma(b)) - log(Gamma(a+b))) / a - log(b) pour a << b
451 // The expansion is in double Padé form wrt a and b
452 const Scalar c1 = -1.0 + first;
453 const Scalar c2 = c1 * (2 * first - 1);
454 const Scalar c3 = c1 * c1;
455 const Scalar c4 = -6.0 + 3.0 * first;
456 const Scalar c5 = first * first;
457 value += epsilon * (-0.5 * c1 + epsilon * (c2 / 12.0 + epsilon * (-(c3 * first) / 12.0 + epsilon * (((-1.0 + 3.0 * first * c1) * c2) / 120.0 + epsilon * (-(c3 * (-1.0 + 2.0 * first * c1) * first) / 60.0 + epsilon * (((1.0 + first * (3.0 + c5 * c4)) * c2) / 252.0 + epsilon * (-(c3 * (2.0 + first * (4.0 + first * (-1.0 + first * c4))) * first) / 168.0 + ((-3.0 + (-9.0 + (-1.0 + (15.0 + (5.0 + (-15.0 + 5.0 * first) * first) * first) * first) * first) * first) * epsilon * c2) / 720.0)))))));
458 value *= first;
459 value += LogGamma(first);
460 return value;
461 }
462 }
463 // Case one of a,b large, the other small
464 return LogGamma(first) + correctionSecond - correctionSum + first * (1.0 - std::log(sum)) + (second - 0.5) * log1p(-first / sum);
465 }
466
467 // LogBeta = LnBeta
LogBeta(const Scalar a,const Scalar b)468 Scalar SpecFunc::LogBeta(const Scalar a,
469 const Scalar b)
470 {
471 return LnBeta(a, b);
472 }
473
474 // Beta function: Beta(a, b) = \int_0^1 t^{a-1}(1-t)^{b-1} dt
Beta(const Scalar a,const Scalar b)475 Scalar SpecFunc::Beta(const Scalar a,
476 const Scalar b)
477 {
478 return std::exp(LnBeta(a, b));
479 }
480
481 // Incomplete Beta function: BetaInc(a, b, x) = \int_0^x t^{a-1}(1-t)^{b-1} dt
IncompleteBeta(const Scalar a,const Scalar b,const Scalar x,const Bool tail)482 Scalar SpecFunc::IncompleteBeta(const Scalar a,
483 const Scalar b,
484 const Scalar x,
485 const Bool tail)
486 {
487 return RegularizedIncompleteBeta(a, b, x, tail) * Beta(a, b);
488 }
489
490 // Incomplete Beta function inverse
IncompleteBetaInverse(const Scalar a,const Scalar b,const Scalar x,const Bool tail)491 Scalar SpecFunc::IncompleteBetaInverse(const Scalar a,
492 const Scalar b,
493 const Scalar x,
494 const Bool tail)
495 {
496 return RegularizedIncompleteBetaInverse(a, b, x / Beta(a, b), tail);
497 }
498
499 // Regularized Incomplete Beta function: RegularizedIncompleteBeta(a, b, x) = 1/beta(a, b) * \int_0^x t^{a-1}(1-t)^{b-1} dt
RegularizedIncompleteBeta(const Scalar a,const Scalar b,const Scalar x,const Bool tail)500 Scalar SpecFunc::RegularizedIncompleteBeta(const Scalar a,
501 const Scalar b,
502 const Scalar x,
503 const Bool tail)
504 {
505 return BetaFunctions::RegularizedIncompleteBeta(a, b, x, tail);
506 }
507
508 // Regularized incomplete Beta Function inverse
RegularizedIncompleteBetaInverse(const Scalar a,const Scalar b,const Scalar x,const Bool tail)509 Scalar SpecFunc::RegularizedIncompleteBetaInverse(const Scalar a,
510 const Scalar b,
511 const Scalar x,
512 const Bool tail)
513 {
514 return BetaFunctions::RegularizedIncompleteBetaInverse(a, b, x, tail);
515 }
516
517 // Dawson function: Dawson(x) = \exp(-x^2) * \int_0^x \exp(t^2) dt
Dawson(const Scalar x)518 Scalar SpecFunc::Dawson(const Scalar x)
519 {
520 return Faddeeva::Dawson(x);
521 }
522
Dawson(const Complex & z)523 Complex SpecFunc::Dawson(const Complex & z)
524 {
525 return Faddeeva::Dawson(z);
526 }
527
528 // Debye function of order n: DebyeN(x, n) = n / x^n \int_0^x t^n/(\exp(t)-1) dt
Debye(const Scalar x,const UnsignedInteger n)529 Scalar SpecFunc::Debye(const Scalar x,
530 const UnsignedInteger n)
531 {
532 if (!(n > 0 && n <= 20)) throw InvalidArgumentException(HERE) << "Error: cannot compute Debye function of order outside of {1,...,20}, here order=" << n;
533 if (x < 0.0) return Debye(-x, n) - n * x / (n + 1.0);
534 // The threshold is such that the overall error is less than 1.0e-16
535 if (x < 1.0e-8) return 1.0 - n * x / (2.0 * (n + 1.0));
536 return debyen(x, static_cast<int>(n)) * n / pow(x, static_cast<int>(n));
537 }
538
539 // DiLog function: DiLog(x) = -\int_0^x \log(1-t)/t dt
DiLog(const Scalar x)540 Scalar SpecFunc::DiLog(const Scalar x)
541 {
542 // Special case for 0
543 if (x == 0.0) return 0.0;
544 // Special case for 1
545 if (x == 1.0) return PI2_6;
546 // No real value on (1, \infty)
547 if (!(x <= 1.0)) throw InvalidArgumentException(HERE) << "Error: the DiLog function does not take real values for arguments greater than 1, here argument=" << x;
548 // Special case for x close to 1
549 if (x >= 0.999997)
550 {
551 const Scalar z = 1.0 - x;
552 const Scalar logZ = std::log(z);
553 return PI2_6 + z * (logZ - 1.0 + z * (logZ - 0.5) * 0.5);
554 }
555 // Use DiLog(x) = -DiLog(1 / x) - \pi^2 / 6 - \log^2(-x) / 2
556 // to map (-\infty, -1) into (-1, 0) for the argument
557 if (x < -1.0) return -DiLog(1.0 / x) - PI2_6 - 0.5 * pow(log(-x), 2);
558 // Use DiLog(x) = DiLog(x^2) / 2 - DiLog(-x)
559 // to map [-1, 0) into (0, 1]
560 if (x < 0.0) return 0.5 * DiLog(x * x) - DiLog(-x);
561 // Use DiLog(x) = \pi^2 / 6 - DiLog(1 - x) - \log(x)\log(1-x)
562 // to map (1/2, 1] into [0, 1/2)
563 if (x > 0.5) return PI2_6 - DiLog(1.0 - x) - std::log(x) * log1p(-x);
564 // Use the definition of DiLog in terms of series
565 // DiLog(x)=\sum_{k=1}^{\infty} x^k/k^2
566 // for (0, 1/2)
567 // This upper bound is an easy-to compute tight upper bound of the number of iterations
568 const UnsignedInteger nMax = static_cast<UnsignedInteger>(round(8 + 68 * x));
569 Scalar value = 0.0;
570 Scalar powerX = 1.0;
571 for (UnsignedInteger n = 1; n <= nMax; ++n)
572 {
573 powerX *= x;
574 value += powerX / (n * n);
575 }
576 return value;
577 }
578
579 // Exponential integral function: Ei(x) = -\int_{-x}^{\infty}exp(-t)/t dt
Ei(const Scalar x)580 Scalar SpecFunc::Ei(const Scalar x)
581 {
582 return ExponentialIntegralFunctions::Ei(x);
583 }
584
585 // Complex exponential integral function: Ei(z) = -\int_{-z}^{\infty}exp(-t)/t dt
Ei(const Complex & z)586 Complex SpecFunc::Ei(const Complex & z)
587 {
588 return ExponentialIntegralFunctions::Ei(z);
589 }
590
591 // Complex Faddeeva function: faddeeva(z) = \exp(-z^2)\erfc(-I*z)
Faddeeva(const Complex & z)592 Complex SpecFunc::Faddeeva(const Complex & z)
593 {
594 return Faddeeva::w(z);
595 }
596
FaddeevaIm(const Scalar x)597 Scalar SpecFunc::FaddeevaIm(const Scalar x)
598 {
599 return Faddeeva::w_im(x);
600 }
601
602 // Factorial function
LogFactorial(const UnsignedInteger n)603 Scalar SpecFunc::LogFactorial(const UnsignedInteger n)
604 {
605 static Scalar A[128] = {0.0, 0.0, 0.69314718055994531, 1.7917594692280550, 3.1780538303479456, 4.7874917427820460, 6.5792512120101010, 8.5251613610654143, 10.604602902745250, 12.801827480081470, 15.104412573075515, 17.502307845873886, 19.987214495661886, 22.552163853123423, 25.191221182738682, 27.899271383840892, 30.671860106080673, 33.505073450136889, 36.395445208033054, 39.339884187199494, 42.335616460753485, 45.380138898476908, 48.471181351835224, 51.606675567764374, 54.784729398112319, 58.003605222980520, 61.261701761002002, 64.557538627006331, 67.889743137181535, 71.257038967168009, 74.658236348830164, 78.092223553315311, 81.557959456115037, 85.054467017581517, 88.580827542197679, 92.136175603687092, 95.719694542143202, 99.330612454787427, 102.96819861451381, 106.63176026064346, 110.32063971475740, 114.03421178146170, 117.77188139974507, 121.53308151543863, 125.31727114935690, 129.12393363912721, 132.95257503561631, 136.80272263732637, 140.67392364823426, 144.56574394634489, 148.47776695177303, 152.40959258449736, 156.36083630307879, 160.33112821663091, 164.32011226319518, 168.32744544842765, 172.35279713916280, 176.39584840699735, 180.45629141754377, 184.53382886144949, 188.62817342367159, 192.73904728784490, 196.86618167288999, 201.00931639928153, 205.16819948264120, 209.34258675253684, 213.53224149456326, 217.73693411395423, 221.95644181913033, 226.19054832372759, 230.43904356577695, 234.70172344281827, 238.97838956183432, 243.26884900298271, 247.57291409618688, 251.89040220972319, 256.22113555000953, 260.56494097186321, 264.92164979855280, 269.29109765101982, 273.67312428569370, 278.06757344036614, 282.47429268763040, 286.89313329542699, 291.32395009427031, 295.76660135076062, 300.22094864701413, 304.68685676566872, 309.16419358014692, 313.65282994987906, 318.15263962020933, 322.66349912672618, 327.18528770377522, 331.71788719692847, 336.26118197919848, 340.81505887079902, 345.37940706226685, 349.95411804077024, 354.53908551944081, 359.13420536957540, 363.73937555556349, 368.35449607240475, 372.97946888568902, 377.61419787391866, 382.25858877306003, 386.91254912321755, 391.57598821732962, 396.24881705179153, 400.93094827891575, 405.62229616114489, 410.32277652693731, 415.03230672824964, 419.75080559954473, 424.47819341825707, 429.21439186665157, 433.95932399501482, 438.71291418612118, 443.47508812091894, 448.24577274538461, 453.02489623849614, 457.81238798127818, 462.60817852687492, 467.41219957160818, 472.22438392698060, 477.04466549258563, 481.87297922988793, 486.70926113683941, 491.55344822329800};
606 if (n < 128) return A[n];
607 /* Stieltjes approximation, see http://www.luschny.de/math/factorial/approx/SimpleCases.html */
608 static Scalar a0 = 1.0 / 12.0;
609 static Scalar a1 = 1.0 / 30.0;
610 static Scalar a2 = 53.0 / 210.0;
611 static Scalar a3 = 195.0 / 371.0;
612 static Scalar a4 = 22999.0 / 22737.0;
613 static Scalar a5 = 29944523.0 / 19733142.0;
614 static Scalar a6 = 109535241009.0 / 48264275462.0;
615 const Scalar z = n + 1.0;
616 return LOGSQRT2PI + (z - 0.5) * std::log(z) - z + a0 / (z + a1 / (z + a2 / (z + a3 / (z + a4 / (z + a5 / (z + a6 / z))))));
617 }
618
Factorial(const UnsignedInteger n)619 Scalar SpecFunc::Factorial(const UnsignedInteger n)
620 {
621 if (n < LogMaxScalar) return std::exp(LogFactorial(n));
622 throw InternalException(HERE) << "Error: n=" << n << " is too large for n! to fit into the return type";
623 return MaxScalar;
624 }
625
626 // Gamma function: Gamma(a) = \int_0^{\infty} t^{a-1}\exp(-t) dt
Gamma(const Scalar a)627 Scalar SpecFunc::Gamma(const Scalar a)
628 {
629 return tgamma(a);
630 }
631
632 // igamma1pm1(a) = 1 / Gamma(1 + a) - 1
IGamma1pm1(const Scalar a)633 Scalar SpecFunc::IGamma1pm1(const Scalar a)
634 {
635 if (a < -0.5) return a + IGamma1pm1(a + 1.0);
636 if (a > 0.5) return (IGamma1pm1(a - 1.0) - a) / (1.0 + a);
637 return a * (0.55840397973848040460 + (-0.28693519326375203685 +
638 (-0.28661639813928077048 + (0.56469202267873782108e-2 +
639 (0.12419086997568707715e-1 + (-0.28455238049924868708e-2 +
640 (-0.11632361891621759586e-3 + 0.43452535150473018757e-4 * a) * a) * a) * a) * a) * a) * a) /
641 (0.967409607349687017271 + (0.602145048219053169110 +
642 (0.258051013832915714221 + (0.677000815494264186575e-1 +
643 (0.142113296913915958898e-1 + (0.183821175266489590252e-2 +
644 0.192078142776918599109e-3 * a) * a) * a) * a) * a) * a);
645 }
646
647 // GammaCorrection(a) = LogGamma(a) - log(sqrt(2.Pi)) + a - (a - 1/2) log(a)
GammaCorrection(const Scalar a)648 Scalar SpecFunc::GammaCorrection(const Scalar a)
649 {
650 if (!(a > 0.0)) throw InvalidArgumentException(HERE) << "Error: cannot compute GammaCorrection for nonpositive a, here a=" << a;
651 // Use an asymptotic series for large argument
652 if (a > 7.7490453948312251620)
653 {
654 const Scalar ia2 = 1.0 / (a * a);
655 return (0.83333333333333333333e-1 + (-0.27777777777777777778e-2 +
656 (0.79365079365079365079e-3 + (-0.59523809523809523810e-3 +
657 (0.84175084175084175084e-3 + (-0.19175269175269175269e-2 +
658 (0.64102564102564102564e-2 + (-0.29550653594771241830e-1 +
659 (0.17964437236883057316e0 - 0.13924322169059011164e1 * ia2) * ia2) * ia2) * ia2) * ia2) * ia2) * ia2) * ia2) * ia2) / a;
660 }
661 return LogGamma(a) + a - (a - 0.5) * std::log(a) - LOGSQRT2PI;
662 }
663
664 // Complex gamma function: Gamma(a) = \int_0^{\infty} t^{a-1}\exp(-t) dt,
665 // Computed using Lanczos approximation, using a C++ translation of
666 // Paul Godfrey's matlab implementation
667 // http://home.att.net/~numericana/answer/info/godfrey.htm#matlabgamma
Gamma(const Complex & a)668 Complex SpecFunc::Gamma(const Complex & a)
669 {
670 if (a.imag() == 0.0) return Gamma(a.real());
671 return std::exp(LogGamma(a));
672 }
673
LogGamma(const Complex & a)674 Complex SpecFunc::LogGamma(const Complex & a)
675 {
676 if (a.imag() == 0.0) return LogGamma(a.real());
677 const Scalar sqrt2Pi = sqrt(2.0 * M_PI);
678 Complex z(a);
679 Bool flip = false;
680 if (z.real() < 0.0)
681 {
682 z = -z;
683 flip = true;
684 }
685 const UnsignedInteger coefficientsSize = 11;
686 static const Scalar coefficients[coefficientsSize] =
687 {
688 1.000000000000000174663, 5716.400188274341379136,
689 -14815.30426768413909044, 14291.49277657478554025,
690 -6348.160217641458813289, 1301.608286058321874105,
691 -108.1767053514369634679, 2.605696505611755827729,
692 -0.7423452510201416151527e-2, 0.5384136432509564062961e-7,
693 -0.4023533141268236372067e-8
694 };
695 const Scalar g = coefficientsSize - 2.0;
696 Complex t(z + g);
697 Complex s(0.0);
698 Complex ss(t - 0.5);
699 for (UnsignedInteger k = coefficientsSize - 1; k > 0; --k)
700 {
701 s += coefficients[k] / t;
702 t -= 1.0;
703 }
704 s += coefficients[0];
705 s = std::log(s * sqrt2Pi) + (z - 0.5) * std::log(ss) - ss;
706 Complex f(s);
707 if (flip) f = f + Log1p(-M_PI * std::exp(-f) / (a * f * sin(M_PI * a)));
708 return f;
709 }
710
711 // Natural logarithm of the Gamma function
LnGamma(const Scalar a)712 Scalar SpecFunc::LnGamma(const Scalar a)
713 {
714 return LogGamma(a);
715 }
716
717 // LogGamma = LnGamma
LogGamma(const Scalar a)718 Scalar SpecFunc::LogGamma(const Scalar a)
719 {
720 return lgamma(a);
721 }
722
LogGamma1p(const Scalar a)723 Scalar SpecFunc::LogGamma1p(const Scalar a)
724 {
725 return (std::abs(a) < 0.5 ? -a * (0.34229051727072805652 +
726 (0.75305954018877769214 +
727 (0.25594427350421023219 +
728 (-0.54867134418632830931 +
729 (-0.57006260085649768851 +
730 (-0.20361938002564003637 +
731 (-0.27922966566918143201e-1 -
732 0.10180389882069314488e-2 * a) * a) * a) * a) * a) * a) * a) /
733 (0.59300282040876235168 +
734 (0.21496034951064079616e1 +
735 (0.30947091018029240660e1 +
736 (0.22448538584537209829e1 +
737 (0.85741167089803858333 +
738 (0.16321946228463159159 +
739 (0.12893353820029086191e-1 +
740 0.24787923059095734273e-3 * a) * a) * a) * a) * a) * a) * a) : LogGamma(a - 1.0));
741 }
742
743 // Incomplete Gamma function: IncompleteGamma(a, x) = \int_0^x t^{a-1}\exp(-t) dt
IncompleteGamma(const Scalar a,const Scalar x,const Bool tail)744 Scalar SpecFunc::IncompleteGamma(const Scalar a,
745 const Scalar x,
746 const Bool tail)
747 {
748 return GammaFunctions::IncompleteGamma(a, x, tail);
749 }
750
751 // Incomplete Gamma function inverse with respect to x
IncompleteGammaInverse(const Scalar a,const Scalar x,const Bool tail)752 Scalar SpecFunc::IncompleteGammaInverse(const Scalar a,
753 const Scalar x,
754 const Bool tail)
755 {
756 return GammaFunctions::IncompleteGammaInverse(a, x, tail);
757 }
758
759 // Regularized incomplete Gamma function: RegularizedIncompleteGamma(a, x) = \int_0^x t^{a-1}\exp(-t) dt / \Gamma(a)
RegularizedIncompleteGamma(const Scalar a,const Scalar x,const Bool tail)760 Scalar SpecFunc::RegularizedIncompleteGamma(const Scalar a,
761 const Scalar x,
762 const Bool tail)
763 {
764 return GammaFunctions::RegularizedIncompleteGamma(a, x, tail);
765 }
766
767 // Regularized incomplete Gamma function inverse with respect to x
RegularizedIncompleteGammaInverse(const Scalar a,const Scalar x,const Bool tail)768 Scalar SpecFunc::RegularizedIncompleteGammaInverse(const Scalar a,
769 const Scalar x,
770 const Bool tail)
771 {
772 return GammaFunctions::RegularizedIncompleteGammaInverse(a, x, tail);
773 }
774
775 // Digamma function: Psi(x) = ((dgamma/dx) / gamma)(x)
776 // Derivative of a Lanczos approximation of log(Gamma)
DiGamma(const Scalar x)777 Scalar SpecFunc::DiGamma(const Scalar x)
778 {
779 // Check that the argument is not non positive, i.e. not in {0, -1, -2, ...}
780 if ((x <= 0.0) && (x == round(x))) throw InvalidArgumentException(HERE) << "Error: the argument of the DiGamma function cannot be a non positive integer.";
781 // Approximation for small arguments
782 // Here, 0.025 is a bound that insure Scalar precision approximation
783 if ( std::abs(x) <= 0.025 ) return -1.0 / x - 0.57721566490153286 + (1.6449340668482264 + (-1.2020569031595943 + (1.0823232337111381 + (-1.0369277551433699 + (1.0173430619844491 + (-1.0083492773819228 + (1.0040773561979442 + (-1.0020083928260822 + 1.0009945751278180 * x) * x) * x) * x) * x) * x) * x) * x) * x;
784 // If the argument is negative, use the reflexion formula
785 if (x < 0.0) return -M_PI / tan(M_PI * x) + DiGamma(1.0 - x);
786 // Shift the argument until it reaches the asymptotic expansion region
787 // Here, 7.69 is a bound that insure Scalar precision of the approximation
788 Scalar z = x;
789 Scalar value = 0.0;
790 while ( z < 7.33 )
791 {
792 value -= 1.0 / z;
793 z += 1.0;
794 }
795 // Use the asymptotic expansion in Horner form
796 const Scalar y = 1.0 / (z * z);
797 return value + std::log(z) - 0.5 / z + (-0.83333333333333333e-1 + (0.83333333333333333e-2 + (-0.39682539682539683e-2 + (0.41666666666666667e-2 + (-0.75757575757575758e-2 + (0.21092796092796093e-1 + (-0.83333333333333333e-1 + (.44325980392156863 - 3.0539543302701197 * y) * y) * y) * y) * y) * y) * y) * y) * y;
798 }
799
Psi(const Scalar x)800 Scalar SpecFunc::Psi(const Scalar x)
801 {
802 return DiGamma(x);
803 }
804
805 // Inverse of the DiGamma function
DiGammaInv(const Scalar a)806 Scalar SpecFunc::DiGammaInv(const Scalar a)
807 {
808 // Initialization using an asymptotic approximation of the DiGamma function
809 Scalar x = a < -2.22 ? -1.0 / (a - EulerConstant) : std::exp(a) + 0.5;
810 // Use a Newton scheme
811 Scalar d = 0.0;
812 for (UnsignedInteger k = 0; k < 6; ++k)
813 {
814 d = (DiGamma(x) - a) / TriGamma(x);
815 if (d == 0.0) break;
816 x -= d;
817 }
818 return x;
819 }
820
821 // Trigamma function: TriGamma(x) = ((d^2gamma/dx^2) / gamma)(x)
TriGamma(const Scalar x)822 Scalar SpecFunc::TriGamma(const Scalar x)
823 {
824 // Check that the argument is not non positive, i.e. not in {0, -1, -2, ...}
825 if ((x <= 0.0) && (x == round(x))) throw InvalidArgumentException(HERE) << "Error: the argument of the TriGamma function cannot be a non positive integer.";
826 // Approximation for small arguments
827 // Here, 0.02 is a bound that insure Scalar precision approximation
828 if ( std::abs(x) <= 0.02 ) return 1.0 / (x * x) + 1.6449340668482264 + (-2.4041138063191886 + (3.2469697011334144 + (-4.1477110205734796 + (5.0867153099222453 + (-6.0500956642915368 + (7.0285414933856097 + (-8.0160671426086576 + (9.0089511761503616 - 10.004941886041195 * x) * x) * x) * x) * x) * x) * x) * x) * x;
829 // If the argument is negative, use the reflexion formula
830 if (x < 0.0) return pow(M_PI / sin(M_PI * x), 2.0) - TriGamma(1.0 - x);
831 // Shift the argument until it reaches the asymptotic expansion region
832 // Here, 7.69 is a bound that insure Scalar precision of the approximation
833 Scalar z = x;
834 Scalar value = 0.0;
835 while ( z < 7.69 )
836 {
837 value += 1.0 / (z * z);
838 z += 1.0;
839 }
840 // Use the asymptotic expansion in Horner form
841 const Scalar y = 1.0 / (z * z);
842 return value + 0.5 * y + (1. + (.16666666666666667 + (-0.33333333333333333e-1 + (0.23809523809523810e-1 + (-0.33333333333333333e-1 + (0.75757575757575758e-1 + (-.25311355311355311 + (1.1666666666666667 + (-7.0921568627450980 + 54.971177944862155 * y) * y) * y) * y) * y) * y) * y) * y) * y) / z;
843 }
844
845 /* Stirlerr(n) = log(n!) - log( sqrt(2*pi*n)*(n/e)^n )
846 * = log Gamma(n+1) - 1/2 * [log(2*pi) + log(n)] - n*[log(n) - 1]
847 * = log Gamma(n+1) - (n + 1/2) * log(n) + n - log(2*pi)/2 */
Stirlerr(const UnsignedInteger n)848 Scalar SpecFunc::Stirlerr(const UnsignedInteger n)
849 {
850 static const Scalar stirlerrTable[26] =
851 {
852 0.000000000000000000000, 8.10614667953272582e-02, 4.13406959554092941e-02,
853 2.76779256849983391e-02, 2.07906721037650931e-02, 1.66446911898211922e-02,
854 1.38761288230707480e-02, 1.18967099458917701e-02, 1.04112652619720965e-02,
855 9.25546218271273292e-03, 8.33056343336287126e-03, 7.57367548795184079e-03,
856 6.94284010720952987e-03, 6.40899418800420707e-03, 5.95137011275884774e-03,
857 5.55473355196280137e-03, 5.20765591960964044e-03, 4.90139594843473786e-03,
858 4.62915374933402859e-03, 4.38556024923232427e-03, 4.16631969199692246e-03,
859 3.96795421864085962e-03, 3.78761806844443458e-03, 3.62296022468309471e-03,
860 3.47202138297876696e-03, 3.33315563672809288e-03
861 };
862 if (n < 26) return stirlerrTable[n];
863 static Scalar S0 = 8.33333333333333333e-02;
864 static Scalar S1 = 2.77777777777777778e-03;
865 static Scalar S2 = 7.93650793650793651e-04;
866 static Scalar S3 = 5.95238095238095238e-04;
867 static Scalar S4 = 8.41750841750841751e-04;
868 const Scalar nn = (1.0 * n) * n;
869 if (n > 2559) return (S0 - S1 / nn) / n;
870 if (n > 82) return (S0 - (S1 - S2 / nn) / nn) / n;
871 if (n > 50) return (S0 - (S1 - (S2 - S3 / nn) / nn) / nn) / n;
872 return (S0 - (S1 - (S2 - (S3 - S4 / nn) / nn) / nn) / nn) / n;
873 }
874
875 // Hypergeometric function of type (1,1): HyperGeom_1_1(p1, q1, x) = \sum_{n=0}^{\infty} [\prod_{k=0}^{n-1} (p1 + k) / (q1 + k)] * x^n / n!
HyperGeom_1_1(const Scalar p1,const Scalar q1,const Scalar x)876 Scalar SpecFunc::HyperGeom_1_1(const Scalar p1,
877 const Scalar q1,
878 const Scalar x)
879 {
880 if (q1 == p1) return std::exp(x);
881 if (x == 0) return 1.0;
882 #ifdef OPENTURNS_HAVE_MPFR
883 boost::multiprecision::mpfr_float_500 pochhammerP1(p1);
884 boost::multiprecision::mpfr_float_500 pochhammerQ1(q1);
885 boost::multiprecision::mpfr_float_500 factorial(1.0);
886 boost::multiprecision::mpfr_float_500 term(1.0);
887 boost::multiprecision::mpfr_float_500 sum(term);
888 boost::multiprecision::mpfr_float_500 eps(1.0);
889 boost::multiprecision::mpfr_float_500 z(x);
890 Bool absEps = abs(eps) > Precision;
891 Bool absEpsPrec = absEps;
892 do
893 {
894 absEpsPrec = absEps;
895 term *= pochhammerP1 * z / (pochhammerQ1 * factorial);
896 pochhammerP1 += 1.0;
897 pochhammerQ1 += 1.0;
898 factorial += 1.0;
899 sum += term;
900 eps = term / sum;
901 absEps = abs(eps) > Precision;
902 }
903 while (absEps || absEpsPrec);
904 return sum.convert_to<Scalar>();
905
906 #else
907 Scalar term = 1.0;
908 Scalar t = x;
909 Scalar pochhammerP1 = p1;
910 if (x < 0)
911 {
912 pochhammerP1 = q1 - p1;
913 t = -x;
914 term = std::exp(x);
915 }
916 Scalar pochhammerQ1 = q1;
917 Scalar factorial = 1.0;
918 Scalar sum = term;
919 Scalar eps = 1.0;
920 Bool absEps = std::abs(eps) > Precision;
921 Bool absEpsPrec = absEps;
922 do
923 {
924 absEpsPrec = absEps;
925 term *= pochhammerP1 * t / (pochhammerQ1 * factorial);
926 ++pochhammerP1;
927 ++pochhammerQ1;
928 ++factorial;
929 sum += term;
930 eps = term / sum;
931 absEps = std::abs(eps) > Precision;
932 }
933 while (absEps || absEpsPrec);
934 return sum;
935 #endif
936 }
937
938 // Complex hypergeometric function of type (1,1): HyperGeom_1_1(p1, q1, x) = \sum_{n=0}^{\infty} [\prod_{k=0}^{n-1} (p1 + k) / (q1 + k)] * x^n / n!
HyperGeom_1_1(const Scalar p1,const Scalar q1,const Complex & x)939 Complex SpecFunc::HyperGeom_1_1(const Scalar p1,
940 const Scalar q1,
941 const Complex & x)
942 {
943 #ifdef OPENTURNS_HAVE_MPC
944 LOGDEBUG("Use MPC implementation");
945 boost::multiprecision::mpc_complex_500 pochhammerP1(p1);
946 boost::multiprecision::mpc_complex_500 pochhammerQ1(q1);
947 boost::multiprecision::mpc_complex_500 factorial{1.0, 0.0};
948 boost::multiprecision::mpc_complex_500 term{1.0, 0.0};
949 boost::multiprecision::mpc_complex_500 sum(term);
950 boost::multiprecision::mpc_complex_500 eps{1.0, 0.0};
951 boost::multiprecision::mpc_complex_500 z(x);
952 Bool absEps = abs(eps) > Precision;
953 Bool absEpsPrec = absEps;
954 do
955 {
956 absEpsPrec = absEps;
957 term *= pochhammerP1 * z / (pochhammerQ1 * factorial);
958 pochhammerP1 += 1.0;
959 pochhammerQ1 += 1.0;
960 factorial += 1.0;
961 sum += term;
962 eps = term / sum;
963 absEps = abs(eps) > Precision;
964 }
965 while (absEps || absEpsPrec);
966 return sum.convert_to<Complex>();
967 #else
968 Complex pochhammerP1(p1);
969 Complex pochhammerQ1(q1);
970 Scalar factorial = 1.0;
971 Complex term(1.0);
972 Complex sum(term);
973 Complex eps(1.0);
974 Bool absEps = std::abs(eps) > Precision;
975 Bool absEpsPrec = absEps;
976 do
977 {
978 absEpsPrec = absEps;
979 term *= pochhammerP1 * x / (pochhammerQ1 * factorial);
980 pochhammerP1 += 1.0;
981 pochhammerQ1 += 1.0;
982 ++factorial;
983 sum += term;
984 eps = term / sum;
985 absEps = std::abs(eps) > Precision;
986 }
987 while (absEps || absEpsPrec);
988 return sum;
989 #endif
990 }
991
992 // Hypergeometric function of type (2,1): HyperGeom_2_1(p1, p2, q1, x) = sum_{n=0}^{\infty} [prod_{k=0}^{n-1} (p1 + k) . (p2 + k) / (q1 + k)] * x^n / n!
HyperGeom_2_1(const Scalar p1,const Scalar p2,const Scalar q1,const Scalar x)993 Scalar SpecFunc::HyperGeom_2_1(const Scalar p1,
994 const Scalar p2,
995 const Scalar q1,
996 const Scalar x)
997 {
998 #ifdef OPENTURNS_HAVE_MPFR
999 boost::multiprecision::mpfr_float_500 pochhammerP1(p1);
1000 boost::multiprecision::mpfr_float_500 pochhammerP2(p2);
1001 boost::multiprecision::mpfr_float_500 pochhammerQ1(q1);
1002 boost::multiprecision::mpfr_float_500 factorial(1.0);
1003 boost::multiprecision::mpfr_float_500 z(x);
1004 boost::multiprecision::mpfr_float_500 term(1.0);
1005 boost::multiprecision::mpfr_float_500 sum(term);
1006 boost::multiprecision::mpfr_float_500 eps(1.0);
1007 Bool absEps = abs(eps) > Precision;
1008 Bool absEpsPrec = absEps;
1009 do
1010 {
1011 absEpsPrec = absEps;
1012 term *= pochhammerP1 * pochhammerP2 * z / (pochhammerQ1 * factorial);
1013 ++pochhammerP1;
1014 ++pochhammerP2;
1015 ++pochhammerQ1;
1016 ++factorial;
1017 sum += term;
1018 eps = abs(term / sum);
1019 absEps = abs(eps) > Precision;
1020 }
1021 while (absEps || absEpsPrec);
1022 return sum.convert_to<Scalar>();
1023 #else
1024 Scalar pochhammerP1 = p1;
1025 Scalar pochhammerP2 = p2;
1026 Scalar pochhammerQ1 = q1;
1027 Scalar factorial = 1.0;
1028 Scalar term = 1.0;
1029 Scalar sum = term;
1030 Scalar eps = 1.0;
1031 Bool absEps = std::abs(eps) > Precision;
1032 Bool absEpsPrec = absEps;
1033 do
1034 {
1035 absEpsPrec = absEps;
1036 term *= pochhammerP1 * pochhammerP2 * x / (pochhammerQ1 * factorial);
1037 ++pochhammerP1;
1038 ++pochhammerP2;
1039 ++pochhammerQ1;
1040 ++factorial;
1041 sum += term;
1042 eps = std::abs(term / sum);
1043 absEps = std::abs(eps) > Precision;
1044 }
1045 while (absEps || absEpsPrec);
1046 return sum;
1047 #endif
1048 }
1049
1050 // Hypergeometric function of type (2,2): HyperGeom_2_1(p1, p2, q1, q2, x) = sum_{n=0}^{\infty} [prod_{k=0}^{n-1} (p1 + k) . (p2 + k) / (q1 + k) / (q2 + k)] * x^n / n!
HyperGeom_2_2(const Scalar p1,const Scalar p2,const Scalar q1,const Scalar q2,const Scalar x)1051 Scalar SpecFunc::HyperGeom_2_2(const Scalar p1,
1052 const Scalar p2,
1053 const Scalar q1,
1054 const Scalar q2,
1055 const Scalar x)
1056 {
1057 if (x == 0.0) return 1.0;
1058 #ifdef OPENTURNS_HAVE_MPFR
1059 boost::multiprecision::mpfr_float_500 pochhammerP1(p1);
1060 boost::multiprecision::mpfr_float_500 pochhammerP2(p2);
1061 boost::multiprecision::mpfr_float_500 pochhammerQ1(q1);
1062 boost::multiprecision::mpfr_float_500 pochhammerQ2(q2);
1063 boost::multiprecision::mpfr_float_500 factorial(1.0);
1064 boost::multiprecision::mpfr_float_500 z(x);
1065 boost::multiprecision::mpfr_float_500 term(0.0);
1066 boost::multiprecision::mpfr_float_500 sum(term);
1067 boost::multiprecision::mpfr_float_500 eps(1.0);
1068 Bool absEps = abs(eps) > Precision;
1069 Bool absEpsPrec = absEps;
1070 do
1071 {
1072 absEpsPrec = absEps;
1073 term += pochhammerP1 * pochhammerP2 * z / (pochhammerQ1 * pochhammerQ2 * factorial);
1074 ++pochhammerP1;
1075 ++pochhammerP2;
1076 ++pochhammerQ1;
1077 ++pochhammerQ2;
1078 ++factorial;
1079 sum += term;
1080 eps = abs(term / sum);
1081 absEps = abs(eps) > Precision;
1082 }
1083 while (absEps || absEpsPrec);
1084 return sum.convert_to<Scalar>();
1085 #else
1086 Scalar pochhammerP1 = p1;
1087 Scalar pochhammerP2 = p2;
1088 Scalar pochhammerQ1 = q1;
1089 Scalar pochhammerQ2 = q2;
1090 Scalar factorial = 1.0;
1091 Scalar term = 0.0;
1092 Scalar sum = term;
1093 Scalar eps = 1.0;
1094 Bool absEps = std::abs(eps) > Precision;
1095 Bool absEpsPrec = absEps;
1096 const Scalar logX = std::log(std::abs(x));
1097 Scalar signX = x > 0.0 ? 1.0 : -1.0;
1098 Scalar signTerm = 1.0;
1099 do
1100 {
1101 absEpsPrec = absEps;
1102 term += std::log(pochhammerP1) + std::log(pochhammerP2) + logX - std::log(pochhammerQ1) - std::log(pochhammerQ2) - std::log(factorial);
1103 ++pochhammerP1;
1104 ++pochhammerP2;
1105 ++pochhammerQ1;
1106 ++pochhammerQ2;
1107 ++factorial;
1108 sum += signTerm * std::exp(term);
1109 signTerm *= signX;
1110 eps = std::abs(term / sum);
1111 absEps = std::abs(eps) > Precision;
1112 }
1113 while (absEps || absEpsPrec);
1114 return sum;
1115 #endif
1116 }
1117
1118 // Erf function Erf(x) = 2 / sqrt(Pi) . \int_0^x \exp(-t^2) dt
Erf(const Scalar x)1119 Scalar SpecFunc::Erf(const Scalar x)
1120 {
1121 return Faddeeva::erf(x);
1122 }
1123
Erf(const Complex & z)1124 Complex SpecFunc::Erf(const Complex & z)
1125 {
1126 return Faddeeva::erf(z);
1127 }
1128
1129 // Erf function ErfI(x) = -i.erf(ix)
ErfI(const Scalar x)1130 Scalar SpecFunc::ErfI(const Scalar x)
1131 {
1132 return Faddeeva::erfi(x);
1133 }
1134
ErfI(const Complex & z)1135 Complex SpecFunc::ErfI(const Complex & z)
1136 {
1137 return Faddeeva::erfi(z);
1138 }
1139
1140 // Erf function ErfC(x) = 1 - Erf(x)
ErfC(const Scalar x)1141 Scalar SpecFunc::ErfC(const Scalar x)
1142 {
1143 return Faddeeva::erfc(x);
1144 }
1145
ErfC(const Complex & z)1146 Complex SpecFunc::ErfC(const Complex & z)
1147 {
1148 return Faddeeva::erfc(z);
1149 }
1150
1151 // Erf function ErfCX(x) = exp(x^2).erfC(x)
ErfCX(const Scalar x)1152 Scalar SpecFunc::ErfCX(const Scalar x)
1153 {
1154 return Faddeeva::erfcx(x);
1155 }
1156
ErfCX(const Complex & z)1157 Complex SpecFunc::ErfCX(const Complex & z)
1158 {
1159 return Faddeeva::erfcx(z);
1160 }
1161
1162 // Inverse of the Erf function
1163 // We use a rational approximation followed by one Halley's iteration (higher order Newton iteration)
ErfInverse(const Scalar x)1164 Scalar SpecFunc::ErfInverse(const Scalar x)
1165 {
1166 Scalar p = 0.5 * (x + 1.0);
1167 static const Scalar a[6] =
1168 {
1169 -3.969683028665376e+01, 2.209460984245205e+02,
1170 -2.759285104469687e+02, 1.383577518672690e+02,
1171 -3.066479806614716e+01, 2.506628277459239e+00
1172 };
1173 static const Scalar b[5] =
1174 {
1175 -5.447609879822406e+01, 1.615858368580409e+02,
1176 -1.556989798598866e+02, 6.680131188771972e+01,
1177 -1.328068155288572e+01
1178 };
1179 static const Scalar c[6] =
1180 {
1181 -7.784894002430293e-03, -3.223964580411365e-01,
1182 -2.400758277161838e+00, -2.549732539343734e+00,
1183 4.374664141464968e+00, 2.938163982698783e+00
1184 };
1185 static const Scalar d[4] =
1186 {
1187 7.784695709041462e-03, 3.224671290700398e-01,
1188 2.445134137142996e+00, 3.754408661907416e+00
1189 };
1190 Scalar q = -1.0;
1191 Scalar t = -1.0;
1192 Scalar u = -1.0;
1193 q = std::min(p, 1.0 - p);
1194 if (q > 0.02425)
1195 {
1196 /* Rational approximation for central region. */
1197 u = q - 0.5;
1198 t = u * u;
1199 u = u * (((((a[0] * t + a[1]) * t + a[2]) * t + a[3]) * t + a[4]) * t + a[5])
1200 / (((((b[0] * t + b[1]) * t + b[2]) * t + b[3]) * t + b[4]) * t + 1.0);
1201 }
1202 else
1203 {
1204 /* Rational approximation for tail region. */
1205 t = sqrt(-2.0 * std::log(q));
1206 u = (((((c[0] * t + c[1]) * t + c[2]) * t + c[3]) * t + c[4]) * t + c[5])
1207 / ((((d[0] * t + d[1]) * t + d[2]) * t + d[3]) * t + 1.0);
1208 }
1209 /* The relative error of the approximation has absolute value less
1210 than 1.15e-9. One iteration of Halley's rational method (third
1211 order) gives full machine precision... */
1212 t = 0.5 + 0.5 * Erf(u * M_SQRT1_2) - q; /* f(u) = error */
1213 // 2.50662827463100050241576528481 = sqrt(2.pi)
1214 t = t * 2.50662827463100050241576528481 * std::exp(0.5 * u * u); /* f(u)/df(u) */
1215 u = u - t / (1.0 + 0.5 * u * t); /* Halley's method */
1216 return (p > 0.5 ? -M_SQRT1_2 * u : M_SQRT1_2 * u);
1217 }
1218
1219 /* Evaluation of the principal branch of Lambert W function.
1220 Based on formulas exposed in:
1221 Robert M. Corless, G. H. Gonnet, D. E. G. Hare, D. J. Jeffrey, and D. E. Knuth, "On the Lambert W Function", Advances in Computational Mathematics, volume 5, 1996, pp. 329--359
1222 */
LambertW(const Scalar x,const Bool principal)1223 Scalar SpecFunc::LambertW(const Scalar x,
1224 const Bool principal)
1225 {
1226 Scalar w = -1.0;
1227 // -0.36787944117144232159552377016146086 = -1 / exp(1)
1228 if (x <= -0.3678794411714423215955238) return w;
1229 // Principal branch, defined over [-1/e,+inf], LambertW >= -1
1230 if (principal)
1231 {
1232 if (x == 0) return 0.0;
1233 if(x < 6.46) w = x * (3.0 + 4.0 * x) / (3.0 + x * (7.0 + 2.5 * x));
1234 // Large argument, use asymptotic expansion, formula 4.18
1235 else
1236 {
1237 const Scalar t1 = std::log(x);
1238 w = t1 - std::log(t1);
1239 }
1240 }
1241 // Second real branch, defined over [-1/e, 0[, LambertW <= -1
1242 else
1243 {
1244 if (x >= 0.0) return - std::numeric_limits<Scalar>::infinity();
1245 if (x < -0.1) w = -2.0;
1246 else
1247 {
1248 const Scalar t1 = std::log(-x);
1249 w = t1 - std::log(-t1);
1250 }
1251 }
1252 // Halley's iteration
1253 for (UnsignedInteger i = 0; i < 3; ++i)
1254 {
1255 const Scalar expW = std::exp(w);
1256 const Scalar numerator = w * expW - x;
1257 const Scalar dw = numerator / (expW * (w + 1.0) - 0.5 * (w + 2.0) * numerator / (w + 1.0));
1258 w -= dw;
1259 }
1260 return w;
1261 }
1262
1263 // Accurate evaluation of log(1+z) for |z|<<1
Log1p(const Complex & z)1264 Complex SpecFunc::Log1p(const Complex & z)
1265 {
1266 if (std::norm(z) < 1e-5) return z * (1.0 + z * (-0.5 + z / 3.0));
1267 return std::log(1.0 + z);
1268 }
1269
1270 // Accurate evaluation of exp(z)-1 for |z|<<1
Expm1(const Complex & z)1271 Complex SpecFunc::Expm1(const Complex & z)
1272 {
1273 if (std::norm(z) < 1e-5) return z * (1.0 + 0.5 * z * (1.0 + z / 3.0));
1274 return std::exp(z) - 1.0;
1275 }
1276
1277 // Accurate evaluation of log(1-exp(-x)) for all x > 0
Log1MExp(const Scalar x)1278 Complex SpecFunc::Log1MExp(const Scalar x)
1279 {
1280 if (!(x > 0.0)) throw InvalidArgumentException(HERE) << "Error: x must be positive";
1281 if (x <= M_LN2) return std::log(-expm1(-x));
1282 return log1p(-std::exp(-x));
1283 }
1284
1285 // Integer log2
Log2(const Unsigned64BitsInteger n)1286 UnsignedInteger SpecFunc::Log2(const Unsigned64BitsInteger n)
1287 {
1288 if (!(n > 0)) throw InvalidArgumentException(HERE) << "Error: n must be positive";
1289
1290 // De Bruijn sequence
1291 const UnsignedInteger tab64[64] =
1292 {
1293 63, 0, 58, 1, 59, 47, 53, 2,
1294 60, 39, 48, 27, 54, 33, 42, 3,
1295 61, 51, 37, 40, 49, 18, 28, 20,
1296 55, 30, 34, 11, 43, 14, 22, 4,
1297 62, 57, 46, 52, 38, 26, 32, 41,
1298 50, 36, 17, 19, 29, 10, 13, 21,
1299 56, 45, 25, 31, 35, 16, 9, 12,
1300 44, 24, 15, 8, 23, 7, 6, 5
1301 };
1302
1303 // http://www.pearsonhighered.com/samplechapter/0201914654.pdf
1304 Unsigned64BitsInteger value = n;
1305 value |= value >> 1;
1306 value |= value >> 2;
1307 value |= value >> 4;
1308 value |= value >> 8;
1309 value |= value >> 16;
1310 value |= value >> 32;
1311
1312 return tab64[((Unsigned64BitsInteger)((value - (value >> 1)) * 0x07EDD5E59A4E28C2)) >> 58];
1313 }
1314
1315 // Compute the smallest power of two greater or equal to the given n
NextPowerOfTwo(const UnsignedInteger n)1316 UnsignedInteger SpecFunc::NextPowerOfTwo(const UnsignedInteger n)
1317 {
1318 UnsignedInteger powerOfTwo = 1;
1319 while (powerOfTwo < n) powerOfTwo <<= 1;
1320 return powerOfTwo;
1321 }
1322
1323 // Integer power
IPow(const Scalar x,const SignedInteger n)1324 Scalar SpecFunc::IPow(const Scalar x, const SignedInteger n)
1325 {
1326 if (n == 0) return 1.0;
1327 if (x == 0) return 0.0;
1328 if (x < 0.0)
1329 {
1330 if (n % 2) return -std::pow(-x, 1.0 * n);
1331 return std::pow(-x, 1.0 * n);
1332 }
1333 return std::pow(x, 1.0 * n);
1334 }
1335
1336 // Integer root
IRoot(const Scalar x,const SignedInteger n)1337 Scalar SpecFunc::IRoot(const Scalar x, const SignedInteger n)
1338 {
1339 if (n == 0) throw InvalidArgumentException(HERE) << "Cannot take the zeroth root of anything!";
1340 if (x == 0) return 0.0;
1341 if (x < 0.0)
1342 {
1343 if (n % 2 == 0) throw InvalidArgumentException(HERE) << "Cannot take an even root of a negative number";
1344 return -std::pow(-x, 1.0 / n);
1345 }
1346 return std::pow(x, 1.0 / n);
1347 }
1348
1349
1350 // Compute the number of bits sets to 1 in n
1351 // Best known algorithm for 64 bits n and fast multiply
BitCount(const Unsigned64BitsInteger n)1352 UnsignedInteger SpecFunc::BitCount(const Unsigned64BitsInteger n)
1353 {
1354 // types and constants used in the functions below
1355
1356 const Unsigned64BitsInteger m1 = 0x5555555555555555; //binary: 0101...
1357 const Unsigned64BitsInteger m2 = 0x3333333333333333; //binary: 00110011..
1358 const Unsigned64BitsInteger m4 = 0x0f0f0f0f0f0f0f0f; //binary: 4 zeros, 4 ones ...
1359 const Unsigned64BitsInteger h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...
1360
1361 // This uses fewer arithmetic operations than any other known
1362 // implementation on machines with fast multiplication.
1363 // It uses 12 arithmetic operations, one of which is a multiply.
1364 Unsigned64BitsInteger x = n;
1365 x -= (x >> 1) & m1; // put count of each 2 bits into those 2 bits
1366 x = (x & m2) + ((x >> 2) & m2); // put count of each 4 bits into those 4 bits
1367 x = (x + (x >> 4)) & m4; // put count of each 8 bits into those 8 bits
1368 return (x * h01) >> 56; // returns left 8 bits of x + (x << 8) + (x << 16) + (x << 24) + ...
1369 }
1370
1371 // Missing functions in cmath wrt math.h as of C++98
Acosh(const Scalar x)1372 Scalar SpecFunc::Acosh(const Scalar x)
1373 {
1374 if (!(x >= 1.0)) throw InvalidArgumentException(HERE) << "Error: acosh is only defined for x>=1, here x=" << x;
1375 return 2.0 * std::log(sqrt(0.5 * (x + 1.0)) + sqrt(0.5 * (x - 1.0)));
1376 }
1377
Asinh(const Scalar x)1378 Scalar SpecFunc::Asinh(const Scalar x)
1379 {
1380 if (std::abs(x) < 0.0081972522783123062436) return x * (1.0 + x * x * (-1.0 / 6.0 + 3.0 * x * x / 40.0));
1381 return std::log(x + sqrt(1.0 + x * x));
1382 }
1383
Atanh(const Scalar x)1384 Scalar SpecFunc::Atanh(const Scalar x)
1385 {
1386 if (std::abs(x) < 0.0069422277258991260322) return x * (1.0 + x * x * (1.0 / 3.0 + x * x / 5.0));
1387 if (x > 0.0) return 0.5 * log1p(2.0 * x / (1.0 - x));
1388 return -0.5 * log1p(-2.0 * x / (1.0 + x));
1389 }
1390
Cbrt(const Scalar x)1391 Scalar SpecFunc::Cbrt(const Scalar x)
1392 {
1393 if (x == 0.0) return 0.0;
1394 return (x < 0.0 ? -std::exp(std::log(-x) / 3.0) : std::exp(std::log(x) / 3.0));
1395 }
1396
BinomialCoefficient(const UnsignedInteger n,const UnsignedInteger k)1397 UnsignedInteger SpecFunc::BinomialCoefficient(const UnsignedInteger n,
1398 const UnsignedInteger k)
1399 {
1400 if (k > n) return 0; // by convention
1401 UnsignedInteger value = 1;
1402 for (UnsignedInteger i = 0; i < std::min(k, n - k); ++ i)
1403 {
1404 value *= n - i;
1405 value /= i + 1;
1406 }
1407 return value;
1408 }
1409
1410
1411 END_NAMESPACE_OPENTURNS
1412