igamma_inverse.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. // (C) Copyright John Maddock 2006.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_SPECIAL_FUNCTIONS_IGAMMA_INVERSE_HPP
  6. #define BOOST_MATH_SPECIAL_FUNCTIONS_IGAMMA_INVERSE_HPP
  7. #ifdef _MSC_VER
  8. #pragma once
  9. #endif
  10. #include <boost/math/tools/tuple.hpp>
  11. #include <boost/math/special_functions/gamma.hpp>
  12. #include <boost/math/special_functions/sign.hpp>
  13. #include <boost/math/tools/roots.hpp>
  14. #include <boost/math/policies/error_handling.hpp>
  15. namespace boost{ namespace math{
  16. namespace detail{
  17. template <class T>
  18. T find_inverse_s(T p, T q)
  19. {
  20. //
  21. // Computation of the Incomplete Gamma Function Ratios and their Inverse
  22. // ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
  23. // ACM Transactions on Mathematical Software, Vol. 12, No. 4,
  24. // December 1986, Pages 377-393.
  25. //
  26. // See equation 32.
  27. //
  28. BOOST_MATH_STD_USING
  29. T t;
  30. if(p < 0.5)
  31. {
  32. t = sqrt(-2 * log(p));
  33. }
  34. else
  35. {
  36. t = sqrt(-2 * log(q));
  37. }
  38. static const double a[4] = { 3.31125922108741, 11.6616720288968, 4.28342155967104, 0.213623493715853 };
  39. static const double b[5] = { 1, 6.61053765625462, 6.40691597760039, 1.27364489782223, 0.3611708101884203e-1 };
  40. T s = t - tools::evaluate_polynomial(a, t) / tools::evaluate_polynomial(b, t);
  41. if(p < 0.5)
  42. s = -s;
  43. return s;
  44. }
  45. template <class T>
  46. T didonato_SN(T a, T x, unsigned N, T tolerance = 0)
  47. {
  48. //
  49. // Computation of the Incomplete Gamma Function Ratios and their Inverse
  50. // ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
  51. // ACM Transactions on Mathematical Software, Vol. 12, No. 4,
  52. // December 1986, Pages 377-393.
  53. //
  54. // See equation 34.
  55. //
  56. T sum = 1;
  57. if(N >= 1)
  58. {
  59. T partial = x / (a + 1);
  60. sum += partial;
  61. for(unsigned i = 2; i <= N; ++i)
  62. {
  63. partial *= x / (a + i);
  64. sum += partial;
  65. if(partial < tolerance)
  66. break;
  67. }
  68. }
  69. return sum;
  70. }
  71. template <class T, class Policy>
  72. inline T didonato_FN(T p, T a, T x, unsigned N, T tolerance, const Policy& pol)
  73. {
  74. //
  75. // Computation of the Incomplete Gamma Function Ratios and their Inverse
  76. // ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
  77. // ACM Transactions on Mathematical Software, Vol. 12, No. 4,
  78. // December 1986, Pages 377-393.
  79. //
  80. // See equation 34.
  81. //
  82. BOOST_MATH_STD_USING
  83. T u = log(p) + boost::math::lgamma(a + 1, pol);
  84. return exp((u + x - log(didonato_SN(a, x, N, tolerance))) / a);
  85. }
  86. template <class T, class Policy>
  87. T find_inverse_gamma(T a, T p, T q, const Policy& pol, bool* p_has_10_digits)
  88. {
  89. //
  90. // In order to understand what's going on here, you will
  91. // need to refer to:
  92. //
  93. // Computation of the Incomplete Gamma Function Ratios and their Inverse
  94. // ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
  95. // ACM Transactions on Mathematical Software, Vol. 12, No. 4,
  96. // December 1986, Pages 377-393.
  97. //
  98. BOOST_MATH_STD_USING
  99. T result;
  100. *p_has_10_digits = false;
  101. if(a == 1)
  102. {
  103. result = -log(q);
  104. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  105. }
  106. else if(a < 1)
  107. {
  108. T g = boost::math::tgamma(a, pol);
  109. T b = q * g;
  110. BOOST_MATH_INSTRUMENT_VARIABLE(g);
  111. BOOST_MATH_INSTRUMENT_VARIABLE(b);
  112. if((b > 0.6) || ((b >= 0.45) && (a >= 0.3)))
  113. {
  114. // DiDonato & Morris Eq 21:
  115. //
  116. // There is a slight variation from DiDonato and Morris here:
  117. // the first form given here is unstable when p is close to 1,
  118. // making it impossible to compute the inverse of Q(a,x) for small
  119. // q. Fortunately the second form works perfectly well in this case.
  120. //
  121. T u;
  122. if((b * q > 1e-8) && (q > 1e-5))
  123. {
  124. u = pow(p * g * a, 1 / a);
  125. BOOST_MATH_INSTRUMENT_VARIABLE(u);
  126. }
  127. else
  128. {
  129. u = exp((-q / a) - constants::euler<T>());
  130. BOOST_MATH_INSTRUMENT_VARIABLE(u);
  131. }
  132. result = u / (1 - (u / (a + 1)));
  133. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  134. }
  135. else if((a < 0.3) && (b >= 0.35))
  136. {
  137. // DiDonato & Morris Eq 22:
  138. T t = exp(-constants::euler<T>() - b);
  139. T u = t * exp(t);
  140. result = t * exp(u);
  141. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  142. }
  143. else if((b > 0.15) || (a >= 0.3))
  144. {
  145. // DiDonato & Morris Eq 23:
  146. T y = -log(b);
  147. T u = y - (1 - a) * log(y);
  148. result = y - (1 - a) * log(u) - log(1 + (1 - a) / (1 + u));
  149. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  150. }
  151. else if (b > 0.1)
  152. {
  153. // DiDonato & Morris Eq 24:
  154. T y = -log(b);
  155. T u = y - (1 - a) * log(y);
  156. result = y - (1 - a) * log(u) - log((u * u + 2 * (3 - a) * u + (2 - a) * (3 - a)) / (u * u + (5 - a) * u + 2));
  157. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  158. }
  159. else
  160. {
  161. // DiDonato & Morris Eq 25:
  162. T y = -log(b);
  163. T c1 = (a - 1) * log(y);
  164. T c1_2 = c1 * c1;
  165. T c1_3 = c1_2 * c1;
  166. T c1_4 = c1_2 * c1_2;
  167. T a_2 = a * a;
  168. T a_3 = a_2 * a;
  169. T c2 = (a - 1) * (1 + c1);
  170. T c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
  171. 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);
  172. T c5 = (a - 1) * (-(c1_4 / 4)
  173. + (11 * a - 17) * c1_3 / 6
  174. + (-3 * a_2 + 13 * a -13) * c1_2
  175. + (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
  176. + (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
  177. T y_2 = y * y;
  178. T y_3 = y_2 * y;
  179. T y_4 = y_2 * y_2;
  180. result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
  181. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  182. if(b < 1e-28f)
  183. *p_has_10_digits = true;
  184. }
  185. }
  186. else
  187. {
  188. // DiDonato and Morris Eq 31:
  189. T s = find_inverse_s(p, q);
  190. BOOST_MATH_INSTRUMENT_VARIABLE(s);
  191. T s_2 = s * s;
  192. T s_3 = s_2 * s;
  193. T s_4 = s_2 * s_2;
  194. T s_5 = s_4 * s;
  195. T ra = sqrt(a);
  196. BOOST_MATH_INSTRUMENT_VARIABLE(ra);
  197. T w = a + s * ra + (s * s -1) / 3;
  198. w += (s_3 - 7 * s) / (36 * ra);
  199. w -= (3 * s_4 + 7 * s_2 - 16) / (810 * a);
  200. w += (9 * s_5 + 256 * s_3 - 433 * s) / (38880 * a * ra);
  201. BOOST_MATH_INSTRUMENT_VARIABLE(w);
  202. if((a >= 500) && (fabs(1 - w / a) < 1e-6))
  203. {
  204. result = w;
  205. *p_has_10_digits = true;
  206. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  207. }
  208. else if (p > 0.5)
  209. {
  210. if(w < 3 * a)
  211. {
  212. result = w;
  213. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  214. }
  215. else
  216. {
  217. T D = (std::max)(T(2), T(a * (a - 1)));
  218. T lg = boost::math::lgamma(a, pol);
  219. T lb = log(q) + lg;
  220. if(lb < -D * 2.3)
  221. {
  222. // DiDonato and Morris Eq 25:
  223. T y = -lb;
  224. T c1 = (a - 1) * log(y);
  225. T c1_2 = c1 * c1;
  226. T c1_3 = c1_2 * c1;
  227. T c1_4 = c1_2 * c1_2;
  228. T a_2 = a * a;
  229. T a_3 = a_2 * a;
  230. T c2 = (a - 1) * (1 + c1);
  231. T c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
  232. 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);
  233. T c5 = (a - 1) * (-(c1_4 / 4)
  234. + (11 * a - 17) * c1_3 / 6
  235. + (-3 * a_2 + 13 * a -13) * c1_2
  236. + (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
  237. + (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
  238. T y_2 = y * y;
  239. T y_3 = y_2 * y;
  240. T y_4 = y_2 * y_2;
  241. result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
  242. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  243. }
  244. else
  245. {
  246. // DiDonato and Morris Eq 33:
  247. T u = -lb + (a - 1) * log(w) - log(1 + (1 - a) / (1 + w));
  248. result = -lb + (a - 1) * log(u) - log(1 + (1 - a) / (1 + u));
  249. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  250. }
  251. }
  252. }
  253. else
  254. {
  255. T z = w;
  256. T ap1 = a + 1;
  257. T ap2 = a + 2;
  258. if(w < 0.15f * ap1)
  259. {
  260. // DiDonato and Morris Eq 35:
  261. T v = log(p) + boost::math::lgamma(ap1, pol);
  262. z = exp((v + w) / a);
  263. s = boost::math::log1p(z / ap1 * (1 + z / ap2), pol);
  264. z = exp((v + z - s) / a);
  265. s = boost::math::log1p(z / ap1 * (1 + z / ap2), pol);
  266. z = exp((v + z - s) / a);
  267. s = boost::math::log1p(z / ap1 * (1 + z / ap2 * (1 + z / (a + 3))), pol);
  268. z = exp((v + z - s) / a);
  269. BOOST_MATH_INSTRUMENT_VARIABLE(z);
  270. }
  271. if((z <= 0.01 * ap1) || (z > 0.7 * ap1))
  272. {
  273. result = z;
  274. if(z <= 0.002 * ap1)
  275. *p_has_10_digits = true;
  276. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  277. }
  278. else
  279. {
  280. // DiDonato and Morris Eq 36:
  281. T ls = log(didonato_SN(a, z, 100, T(1e-4)));
  282. T v = log(p) + boost::math::lgamma(ap1, pol);
  283. z = exp((v + z - ls) / a);
  284. result = z * (1 - (a * log(z) - z - v + ls) / (a - z));
  285. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  286. }
  287. }
  288. }
  289. return result;
  290. }
  291. template <class T, class Policy>
  292. struct gamma_p_inverse_func
  293. {
  294. gamma_p_inverse_func(T a_, T p_, bool inv) : a(a_), p(p_), invert(inv)
  295. {
  296. //
  297. // If p is too near 1 then P(x) - p suffers from cancellation
  298. // errors causing our root-finding algorithms to "thrash", better
  299. // to invert in this case and calculate Q(x) - (1-p) instead.
  300. //
  301. // Of course if p is *very* close to 1, then the answer we get will
  302. // be inaccurate anyway (because there's not enough information in p)
  303. // but at least we will converge on the (inaccurate) answer quickly.
  304. //
  305. if(p > 0.9)
  306. {
  307. p = 1 - p;
  308. invert = !invert;
  309. }
  310. }
  311. boost::math::tuple<T, T, T> operator()(const T& x)const
  312. {
  313. BOOST_FPU_EXCEPTION_GUARD
  314. //
  315. // Calculate P(x) - p and the first two derivates, or if the invert
  316. // flag is set, then Q(x) - q and it's derivatives.
  317. //
  318. typedef typename policies::evaluation<T, Policy>::type value_type;
  319. // typedef typename lanczos::lanczos<T, Policy>::type evaluation_type;
  320. typedef typename policies::normalise<
  321. Policy,
  322. policies::promote_float<false>,
  323. policies::promote_double<false>,
  324. policies::discrete_quantile<>,
  325. policies::assert_undefined<> >::type forwarding_policy;
  326. BOOST_MATH_STD_USING // For ADL of std functions.
  327. T f, f1;
  328. value_type ft;
  329. f = static_cast<T>(boost::math::detail::gamma_incomplete_imp(
  330. static_cast<value_type>(a),
  331. static_cast<value_type>(x),
  332. true, invert,
  333. forwarding_policy(), &ft));
  334. f1 = static_cast<T>(ft);
  335. T f2;
  336. T div = (a - x - 1) / x;
  337. f2 = f1;
  338. if((fabs(div) > 1) && (tools::max_value<T>() / fabs(div) < f2))
  339. {
  340. // overflow:
  341. f2 = -tools::max_value<T>() / 2;
  342. }
  343. else
  344. {
  345. f2 *= div;
  346. }
  347. if(invert)
  348. {
  349. f1 = -f1;
  350. f2 = -f2;
  351. }
  352. return boost::math::make_tuple(static_cast<T>(f - p), f1, f2);
  353. }
  354. private:
  355. T a, p;
  356. bool invert;
  357. };
  358. template <class T, class Policy>
  359. T gamma_p_inv_imp(T a, T p, const Policy& pol)
  360. {
  361. BOOST_MATH_STD_USING // ADL of std functions.
  362. static const char* function = "boost::math::gamma_p_inv<%1%>(%1%, %1%)";
  363. BOOST_MATH_INSTRUMENT_VARIABLE(a);
  364. BOOST_MATH_INSTRUMENT_VARIABLE(p);
  365. if(a <= 0)
  366. return policies::raise_domain_error<T>(function, "Argument a in the incomplete gamma function inverse must be >= 0 (got a=%1%).", a, pol);
  367. if((p < 0) || (p > 1))
  368. return policies::raise_domain_error<T>(function, "Probabilty must be in the range [0,1] in the incomplete gamma function inverse (got p=%1%).", p, pol);
  369. if(p == 1)
  370. return policies::raise_overflow_error<T>(function, 0, Policy());
  371. if(p == 0)
  372. return 0;
  373. bool has_10_digits;
  374. T guess = detail::find_inverse_gamma<T>(a, p, 1 - p, pol, &has_10_digits);
  375. if((policies::digits<T, Policy>() <= 36) && has_10_digits)
  376. return guess;
  377. T lower = tools::min_value<T>();
  378. if(guess <= lower)
  379. guess = tools::min_value<T>();
  380. BOOST_MATH_INSTRUMENT_VARIABLE(guess);
  381. //
  382. // Work out how many digits to converge to, normally this is
  383. // 2/3 of the digits in T, but if the first derivative is very
  384. // large convergence is slow, so we'll bump it up to full
  385. // precision to prevent premature termination of the root-finding routine.
  386. //
  387. unsigned digits = policies::digits<T, Policy>();
  388. if(digits < 30)
  389. {
  390. digits *= 2;
  391. digits /= 3;
  392. }
  393. else
  394. {
  395. digits /= 2;
  396. digits -= 1;
  397. }
  398. if((a < 0.125) && (fabs(gamma_p_derivative(a, guess, pol)) > 1 / sqrt(tools::epsilon<T>())))
  399. digits = policies::digits<T, Policy>() - 2;
  400. //
  401. // Go ahead and iterate:
  402. //
  403. boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
  404. guess = tools::halley_iterate(
  405. detail::gamma_p_inverse_func<T, Policy>(a, p, false),
  406. guess,
  407. lower,
  408. tools::max_value<T>(),
  409. digits,
  410. max_iter);
  411. policies::check_root_iterations<T>(function, max_iter, pol);
  412. BOOST_MATH_INSTRUMENT_VARIABLE(guess);
  413. if(guess == lower)
  414. guess = policies::raise_underflow_error<T>(function, "Expected result known to be non-zero, but is smaller than the smallest available number.", pol);
  415. return guess;
  416. }
  417. template <class T, class Policy>
  418. T gamma_q_inv_imp(T a, T q, const Policy& pol)
  419. {
  420. BOOST_MATH_STD_USING // ADL of std functions.
  421. static const char* function = "boost::math::gamma_q_inv<%1%>(%1%, %1%)";
  422. if(a <= 0)
  423. return policies::raise_domain_error<T>(function, "Argument a in the incomplete gamma function inverse must be >= 0 (got a=%1%).", a, pol);
  424. if((q < 0) || (q > 1))
  425. return policies::raise_domain_error<T>(function, "Probabilty must be in the range [0,1] in the incomplete gamma function inverse (got q=%1%).", q, pol);
  426. if(q == 0)
  427. return policies::raise_overflow_error<T>(function, 0, Policy());
  428. if(q == 1)
  429. return 0;
  430. bool has_10_digits;
  431. T guess = detail::find_inverse_gamma<T>(a, 1 - q, q, pol, &has_10_digits);
  432. if((policies::digits<T, Policy>() <= 36) && has_10_digits)
  433. return guess;
  434. T lower = tools::min_value<T>();
  435. if(guess <= lower)
  436. guess = tools::min_value<T>();
  437. //
  438. // Work out how many digits to converge to, normally this is
  439. // 2/3 of the digits in T, but if the first derivative is very
  440. // large convergence is slow, so we'll bump it up to full
  441. // precision to prevent premature termination of the root-finding routine.
  442. //
  443. unsigned digits = policies::digits<T, Policy>();
  444. if(digits < 30)
  445. {
  446. digits *= 2;
  447. digits /= 3;
  448. }
  449. else
  450. {
  451. digits /= 2;
  452. digits -= 1;
  453. }
  454. if((a < 0.125) && (fabs(gamma_p_derivative(a, guess, pol)) > 1 / sqrt(tools::epsilon<T>())))
  455. digits = policies::digits<T, Policy>();
  456. //
  457. // Go ahead and iterate:
  458. //
  459. boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
  460. guess = tools::halley_iterate(
  461. detail::gamma_p_inverse_func<T, Policy>(a, q, true),
  462. guess,
  463. lower,
  464. tools::max_value<T>(),
  465. digits,
  466. max_iter);
  467. policies::check_root_iterations<T>(function, max_iter, pol);
  468. if(guess == lower)
  469. guess = policies::raise_underflow_error<T>(function, "Expected result known to be non-zero, but is smaller than the smallest available number.", pol);
  470. return guess;
  471. }
  472. } // namespace detail
  473. template <class T1, class T2, class Policy>
  474. inline typename tools::promote_args<T1, T2>::type
  475. gamma_p_inv(T1 a, T2 p, const Policy& pol)
  476. {
  477. typedef typename tools::promote_args<T1, T2>::type result_type;
  478. return detail::gamma_p_inv_imp(
  479. static_cast<result_type>(a),
  480. static_cast<result_type>(p), pol);
  481. }
  482. template <class T1, class T2, class Policy>
  483. inline typename tools::promote_args<T1, T2>::type
  484. gamma_q_inv(T1 a, T2 p, const Policy& pol)
  485. {
  486. typedef typename tools::promote_args<T1, T2>::type result_type;
  487. return detail::gamma_q_inv_imp(
  488. static_cast<result_type>(a),
  489. static_cast<result_type>(p), pol);
  490. }
  491. template <class T1, class T2>
  492. inline typename tools::promote_args<T1, T2>::type
  493. gamma_p_inv(T1 a, T2 p)
  494. {
  495. return gamma_p_inv(a, p, policies::policy<>());
  496. }
  497. template <class T1, class T2>
  498. inline typename tools::promote_args<T1, T2>::type
  499. gamma_q_inv(T1 a, T2 p)
  500. {
  501. return gamma_q_inv(a, p, policies::policy<>());
  502. }
  503. } // namespace math
  504. } // namespace boost
  505. #endif // BOOST_MATH_SPECIAL_FUNCTIONS_IGAMMA_INVERSE_HPP