File indexing completed on 2025-01-18 09:40:06
0001
0002
0003
0004
0005
0006 #ifndef BOOST_MATH_SPECIAL_FUNCTIONS_IGAMMA_INVERSE_HPP
0007 #define BOOST_MATH_SPECIAL_FUNCTIONS_IGAMMA_INVERSE_HPP
0008
0009 #ifdef _MSC_VER
0010 #pragma once
0011 #endif
0012
0013 #include <boost/math/tools/tuple.hpp>
0014 #include <boost/math/special_functions/gamma.hpp>
0015 #include <boost/math/special_functions/sign.hpp>
0016 #include <boost/math/tools/roots.hpp>
0017 #include <boost/math/policies/error_handling.hpp>
0018
0019 namespace boost{ namespace math{
0020
0021 namespace detail{
0022
0023 template <class T>
0024 T find_inverse_s(T p, T q)
0025 {
0026
0027
0028
0029
0030
0031
0032
0033
0034 BOOST_MATH_STD_USING
0035 T t;
0036 if(p < T(0.5))
0037 {
0038 t = sqrt(-2 * log(p));
0039 }
0040 else
0041 {
0042 t = sqrt(-2 * log(q));
0043 }
0044 static const double a[4] = { 3.31125922108741, 11.6616720288968, 4.28342155967104, 0.213623493715853 };
0045 static const double b[5] = { 1, 6.61053765625462, 6.40691597760039, 1.27364489782223, 0.3611708101884203e-1 };
0046 T s = t - tools::evaluate_polynomial(a, t) / tools::evaluate_polynomial(b, t);
0047 if(p < T(0.5))
0048 s = -s;
0049 return s;
0050 }
0051
0052 template <class T>
0053 T didonato_SN(T a, T x, unsigned N, T tolerance = 0)
0054 {
0055
0056
0057
0058
0059
0060
0061
0062
0063 T sum = 1;
0064 if(N >= 1)
0065 {
0066 T partial = x / (a + 1);
0067 sum += partial;
0068 for(unsigned i = 2; i <= N; ++i)
0069 {
0070 partial *= x / (a + i);
0071 sum += partial;
0072 if(partial < tolerance)
0073 break;
0074 }
0075 }
0076 return sum;
0077 }
0078
0079 template <class T, class Policy>
0080 inline T didonato_FN(T p, T a, T x, unsigned N, T tolerance, const Policy& pol)
0081 {
0082
0083
0084
0085
0086
0087
0088
0089
0090 BOOST_MATH_STD_USING
0091 T u = log(p) + boost::math::lgamma(a + 1, pol);
0092 return exp((u + x - log(didonato_SN(a, x, N, tolerance))) / a);
0093 }
0094
0095 template <class T, class Policy>
0096 T find_inverse_gamma(T a, T p, T q, const Policy& pol, bool* p_has_10_digits)
0097 {
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107 BOOST_MATH_STD_USING
0108
0109 T result;
0110 *p_has_10_digits = false;
0111
0112 if(a == 1)
0113 {
0114 result = -log(q);
0115 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0116 }
0117 else if(a < 1)
0118 {
0119 T g = boost::math::tgamma(a, pol);
0120 T b = q * g;
0121 BOOST_MATH_INSTRUMENT_VARIABLE(g);
0122 BOOST_MATH_INSTRUMENT_VARIABLE(b);
0123 if((b >T(0.6)) || ((b >= T(0.45)) && (a >= T(0.3))))
0124 {
0125
0126
0127
0128
0129
0130
0131
0132 T u;
0133 if((b * q > T(1e-8)) && (q > T(1e-5)))
0134 {
0135 u = pow(p * g * a, 1 / a);
0136 BOOST_MATH_INSTRUMENT_VARIABLE(u);
0137 }
0138 else
0139 {
0140 u = exp((-q / a) - constants::euler<T>());
0141 BOOST_MATH_INSTRUMENT_VARIABLE(u);
0142 }
0143 result = u / (1 - (u / (a + 1)));
0144 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0145 }
0146 else if((a < 0.3) && (b >= 0.35))
0147 {
0148
0149 T t = exp(-constants::euler<T>() - b);
0150 T u = t * exp(t);
0151 result = t * exp(u);
0152 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0153 }
0154 else if((b > 0.15) || (a >= 0.3))
0155 {
0156
0157 T y = -log(b);
0158 T u = y - (1 - a) * log(y);
0159 result = y - (1 - a) * log(u) - log(1 + (1 - a) / (1 + u));
0160 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0161 }
0162 else if (b > 0.1)
0163 {
0164
0165 T y = -log(b);
0166 T u = y - (1 - a) * log(y);
0167 result = y - (1 - a) * log(u) - log((u * u + 2 * (3 - a) * u + (2 - a) * (3 - a)) / (u * u + (5 - a) * u + 2));
0168 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0169 }
0170 else
0171 {
0172
0173 T y = -log(b);
0174 T c1 = (a - 1) * log(y);
0175 T c1_2 = c1 * c1;
0176 T c1_3 = c1_2 * c1;
0177 T c1_4 = c1_2 * c1_2;
0178 T a_2 = a * a;
0179 T a_3 = a_2 * a;
0180
0181 T c2 = (a - 1) * (1 + c1);
0182 T c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
0183 T c4 = (a - 1) * ((c1_3 / 3) - (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
0184 T c5 = (a - 1) * (-(c1_4 / 4)
0185 + (11 * a - 17) * c1_3 / 6
0186 + (-3 * a_2 + 13 * a -13) * c1_2
0187 + (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
0188 + (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
0189
0190 T y_2 = y * y;
0191 T y_3 = y_2 * y;
0192 T y_4 = y_2 * y_2;
0193 result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
0194 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0195 if(b < 1e-28f)
0196 *p_has_10_digits = true;
0197 }
0198 }
0199 else
0200 {
0201
0202 T s = find_inverse_s(p, q);
0203
0204 BOOST_MATH_INSTRUMENT_VARIABLE(s);
0205
0206 T s_2 = s * s;
0207 T s_3 = s_2 * s;
0208 T s_4 = s_2 * s_2;
0209 T s_5 = s_4 * s;
0210 T ra = sqrt(a);
0211
0212 BOOST_MATH_INSTRUMENT_VARIABLE(ra);
0213
0214 T w = a + s * ra + (s * s -1) / 3;
0215 w += (s_3 - 7 * s) / (36 * ra);
0216 w -= (3 * s_4 + 7 * s_2 - 16) / (810 * a);
0217 w += (9 * s_5 + 256 * s_3 - 433 * s) / (38880 * a * ra);
0218
0219 BOOST_MATH_INSTRUMENT_VARIABLE(w);
0220
0221 if((a >= 500) && (fabs(1 - w / a) < 1e-6))
0222 {
0223 result = w;
0224 *p_has_10_digits = true;
0225 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0226 }
0227 else if (p > 0.5)
0228 {
0229 if(w < 3 * a)
0230 {
0231 result = w;
0232 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0233 }
0234 else
0235 {
0236 T D = (std::max)(T(2), T(a * (a - 1)));
0237 T lg = boost::math::lgamma(a, pol);
0238 T lb = log(q) + lg;
0239 if(lb < -D * T(2.3))
0240 {
0241
0242 T y = -lb;
0243 T c1 = (a - 1) * log(y);
0244 T c1_2 = c1 * c1;
0245 T c1_3 = c1_2 * c1;
0246 T c1_4 = c1_2 * c1_2;
0247 T a_2 = a * a;
0248 T a_3 = a_2 * a;
0249
0250 T c2 = (a - 1) * (1 + c1);
0251 T c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
0252 T c4 = (a - 1) * ((c1_3 / 3) - (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
0253 T c5 = (a - 1) * (-(c1_4 / 4)
0254 + (11 * a - 17) * c1_3 / 6
0255 + (-3 * a_2 + 13 * a -13) * c1_2
0256 + (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
0257 + (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
0258
0259 T y_2 = y * y;
0260 T y_3 = y_2 * y;
0261 T y_4 = y_2 * y_2;
0262 result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
0263 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0264 }
0265 else
0266 {
0267
0268 T u = -lb + (a - 1) * log(w) - log(1 + (1 - a) / (1 + w));
0269 result = -lb + (a - 1) * log(u) - log(1 + (1 - a) / (1 + u));
0270 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0271 }
0272 }
0273 }
0274 else
0275 {
0276 T z = w;
0277 T ap1 = a + 1;
0278 T ap2 = a + 2;
0279 if(w < 0.15f * ap1)
0280 {
0281
0282 T v = log(p) + boost::math::lgamma(ap1, pol);
0283 z = exp((v + w) / a);
0284 s = boost::math::log1p(z / ap1 * (1 + z / ap2), pol);
0285 z = exp((v + z - s) / a);
0286 s = boost::math::log1p(z / ap1 * (1 + z / ap2), pol);
0287 z = exp((v + z - s) / a);
0288 s = boost::math::log1p(z / ap1 * (1 + z / ap2 * (1 + z / (a + 3))), pol);
0289 z = exp((v + z - s) / a);
0290 BOOST_MATH_INSTRUMENT_VARIABLE(z);
0291 }
0292
0293 if((z <= 0.01 * ap1) || (z > 0.7 * ap1))
0294 {
0295 result = z;
0296 if(z <= T(0.002) * ap1)
0297 *p_has_10_digits = true;
0298 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0299 }
0300 else
0301 {
0302
0303 T ls = log(didonato_SN(a, z, 100, T(1e-4)));
0304 T v = log(p) + boost::math::lgamma(ap1, pol);
0305 z = exp((v + z - ls) / a);
0306 result = z * (1 - (a * log(z) - z - v + ls) / (a - z));
0307
0308 BOOST_MATH_INSTRUMENT_VARIABLE(result);
0309 }
0310 }
0311 }
0312 return result;
0313 }
0314
0315 template <class T, class Policy>
0316 struct gamma_p_inverse_func
0317 {
0318 gamma_p_inverse_func(T a_, T p_, bool inv) : a(a_), p(p_), invert(inv)
0319 {
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329 if(p > T(0.9))
0330 {
0331 p = 1 - p;
0332 invert = !invert;
0333 }
0334 }
0335
0336 boost::math::tuple<T, T, T> operator()(const T& x)const
0337 {
0338 BOOST_FPU_EXCEPTION_GUARD
0339
0340
0341
0342
0343 typedef typename policies::evaluation<T, Policy>::type value_type;
0344
0345 typedef typename policies::normalise<
0346 Policy,
0347 policies::promote_float<false>,
0348 policies::promote_double<false>,
0349 policies::discrete_quantile<>,
0350 policies::assert_undefined<> >::type forwarding_policy;
0351
0352 BOOST_MATH_STD_USING
0353
0354 T f, f1;
0355 value_type ft;
0356 f = static_cast<T>(boost::math::detail::gamma_incomplete_imp(
0357 static_cast<value_type>(a),
0358 static_cast<value_type>(x),
0359 true, invert,
0360 forwarding_policy(), &ft));
0361 f1 = static_cast<T>(ft);
0362 T f2;
0363 T div = (a - x - 1) / x;
0364 f2 = f1;
0365 if(fabs(div) > 1)
0366 {
0367
0368
0369 if (tools::max_value<T>() / fabs(div) < f2)
0370 {
0371
0372 f2 = -tools::max_value<T>() / 2;
0373 }
0374 else
0375 {
0376 f2 *= div;
0377 }
0378 }
0379 else
0380 {
0381 f2 *= div;
0382 }
0383
0384 if(invert)
0385 {
0386 f1 = -f1;
0387 f2 = -f2;
0388 }
0389
0390 return boost::math::make_tuple(static_cast<T>(f - p), f1, f2);
0391 }
0392 private:
0393 T a, p;
0394 bool invert;
0395 };
0396
0397 template <class T, class Policy>
0398 T gamma_p_inv_imp(T a, T p, const Policy& pol)
0399 {
0400 BOOST_MATH_STD_USING
0401
0402 static const char* function = "boost::math::gamma_p_inv<%1%>(%1%, %1%)";
0403
0404 BOOST_MATH_INSTRUMENT_VARIABLE(a);
0405 BOOST_MATH_INSTRUMENT_VARIABLE(p);
0406
0407 if(a <= 0)
0408 return policies::raise_domain_error<T>(function, "Argument a in the incomplete gamma function inverse must be >= 0 (got a=%1%).", a, pol);
0409 if((p < 0) || (p > 1))
0410 return policies::raise_domain_error<T>(function, "Probability must be in the range [0,1] in the incomplete gamma function inverse (got p=%1%).", p, pol);
0411 if(p == 1)
0412 return policies::raise_overflow_error<T>(function, nullptr, Policy());
0413 if(p == 0)
0414 return 0;
0415 bool has_10_digits;
0416 T guess = detail::find_inverse_gamma<T>(a, p, 1 - p, pol, &has_10_digits);
0417 if((policies::digits<T, Policy>() <= 36) && has_10_digits)
0418 return guess;
0419 T lower = tools::min_value<T>();
0420 if(guess <= lower)
0421 guess = tools::min_value<T>();
0422 BOOST_MATH_INSTRUMENT_VARIABLE(guess);
0423
0424
0425
0426
0427
0428
0429 unsigned digits = policies::digits<T, Policy>();
0430 if(digits < 30)
0431 {
0432 digits *= 2;
0433 digits /= 3;
0434 }
0435 else
0436 {
0437 digits /= 2;
0438 digits -= 1;
0439 }
0440 if((a < T(0.125)) && (fabs(gamma_p_derivative(a, guess, pol)) > 1 / sqrt(tools::epsilon<T>())))
0441 digits = policies::digits<T, Policy>() - 2;
0442
0443
0444
0445 std::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0446 guess = tools::halley_iterate(
0447 detail::gamma_p_inverse_func<T, Policy>(a, p, false),
0448 guess,
0449 lower,
0450 tools::max_value<T>(),
0451 digits,
0452 max_iter);
0453 policies::check_root_iterations<T>(function, max_iter, pol);
0454 BOOST_MATH_INSTRUMENT_VARIABLE(guess);
0455 if(guess == lower)
0456 guess = policies::raise_underflow_error<T>(function, "Expected result known to be non-zero, but is smaller than the smallest available number.", pol);
0457 return guess;
0458 }
0459
0460 template <class T, class Policy>
0461 T gamma_q_inv_imp(T a, T q, const Policy& pol)
0462 {
0463 BOOST_MATH_STD_USING
0464
0465 static const char* function = "boost::math::gamma_q_inv<%1%>(%1%, %1%)";
0466
0467 if(a <= 0)
0468 return policies::raise_domain_error<T>(function, "Argument a in the incomplete gamma function inverse must be >= 0 (got a=%1%).", a, pol);
0469 if((q < 0) || (q > 1))
0470 return policies::raise_domain_error<T>(function, "Probability must be in the range [0,1] in the incomplete gamma function inverse (got q=%1%).", q, pol);
0471 if(q == 0)
0472 return policies::raise_overflow_error<T>(function, nullptr, Policy());
0473 if(q == 1)
0474 return 0;
0475 bool has_10_digits;
0476 T guess = detail::find_inverse_gamma<T>(a, 1 - q, q, pol, &has_10_digits);
0477 if((policies::digits<T, Policy>() <= 36) && has_10_digits)
0478 return guess;
0479 T lower = tools::min_value<T>();
0480 if(guess <= lower)
0481 guess = tools::min_value<T>();
0482
0483
0484
0485
0486
0487
0488 unsigned digits = policies::digits<T, Policy>();
0489 if(digits < 30)
0490 {
0491 digits *= 2;
0492 digits /= 3;
0493 }
0494 else
0495 {
0496 digits /= 2;
0497 digits -= 1;
0498 }
0499 if((a < 0.125) && (fabs(gamma_p_derivative(a, guess, pol)) > 1 / sqrt(tools::epsilon<T>())))
0500 digits = policies::digits<T, Policy>();
0501
0502
0503
0504 std::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0505 guess = tools::halley_iterate(
0506 detail::gamma_p_inverse_func<T, Policy>(a, q, true),
0507 guess,
0508 lower,
0509 tools::max_value<T>(),
0510 digits,
0511 max_iter);
0512 policies::check_root_iterations<T>(function, max_iter, pol);
0513 if(guess == lower)
0514 guess = policies::raise_underflow_error<T>(function, "Expected result known to be non-zero, but is smaller than the smallest available number.", pol);
0515 return guess;
0516 }
0517
0518 }
0519
0520 template <class T1, class T2, class Policy>
0521 inline typename tools::promote_args<T1, T2>::type
0522 gamma_p_inv(T1 a, T2 p, const Policy& pol)
0523 {
0524 typedef typename tools::promote_args<T1, T2>::type result_type;
0525 return detail::gamma_p_inv_imp(
0526 static_cast<result_type>(a),
0527 static_cast<result_type>(p), pol);
0528 }
0529
0530 template <class T1, class T2, class Policy>
0531 inline typename tools::promote_args<T1, T2>::type
0532 gamma_q_inv(T1 a, T2 p, const Policy& pol)
0533 {
0534 typedef typename tools::promote_args<T1, T2>::type result_type;
0535 return detail::gamma_q_inv_imp(
0536 static_cast<result_type>(a),
0537 static_cast<result_type>(p), pol);
0538 }
0539
0540 template <class T1, class T2>
0541 inline typename tools::promote_args<T1, T2>::type
0542 gamma_p_inv(T1 a, T2 p)
0543 {
0544 return gamma_p_inv(a, p, policies::policy<>());
0545 }
0546
0547 template <class T1, class T2>
0548 inline typename tools::promote_args<T1, T2>::type
0549 gamma_q_inv(T1 a, T2 p)
0550 {
0551 return gamma_q_inv(a, p, policies::policy<>());
0552 }
0553
0554 }
0555 }
0556
0557 #endif
0558
0559
0560