gamma_inva.hpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // (C) Copyright John Maddock 2006.
  2. // (C) Copyright Matt Borland 2024.
  3. // Use, modification and distribution are subject to the
  4. // Boost Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // This is not a complete header file, it is included by gamma.hpp
  8. // after it has defined it's definitions. This inverts the incomplete
  9. // gamma functions P and Q on the first parameter "a" using a generic
  10. // root finding algorithm (TOMS Algorithm 748).
  11. //
  12. #ifndef BOOST_MATH_SP_DETAIL_GAMMA_INVA
  13. #define BOOST_MATH_SP_DETAIL_GAMMA_INVA
  14. #ifdef _MSC_VER
  15. #pragma once
  16. #endif
  17. #include <boost/math/tools/config.hpp>
  18. #include <boost/math/tools/toms748_solve.hpp>
  19. namespace boost{ namespace math{
  20. #ifdef BOOST_MATH_HAS_NVRTC
  21. template <typename T, typename Policy>
  22. BOOST_MATH_GPU_ENABLED auto erfc_inv(T x, const Policy&);
  23. #endif
  24. namespace detail{
  25. template <class T, class Policy>
  26. struct gamma_inva_t
  27. {
  28. BOOST_MATH_GPU_ENABLED gamma_inva_t(T z_, T p_, bool invert_) : z(z_), p(p_), invert(invert_) {}
  29. BOOST_MATH_GPU_ENABLED T operator()(T a)
  30. {
  31. return invert ? p - boost::math::gamma_q(a, z, Policy()) : boost::math::gamma_p(a, z, Policy()) - p;
  32. }
  33. private:
  34. T z, p;
  35. bool invert;
  36. };
  37. template <class T, class Policy>
  38. BOOST_MATH_GPU_ENABLED T inverse_poisson_cornish_fisher(T lambda, T p, T q, const Policy& pol)
  39. {
  40. BOOST_MATH_STD_USING
  41. // mean:
  42. T m = lambda;
  43. // standard deviation:
  44. T sigma = sqrt(lambda);
  45. // skewness
  46. T sk = 1 / sigma;
  47. // kurtosis:
  48. // T k = 1/lambda;
  49. // Get the inverse of a std normal distribution:
  50. T x = boost::math::erfc_inv(p > q ? 2 * q : 2 * p, pol) * constants::root_two<T>();
  51. // Set the sign:
  52. if(p < 0.5)
  53. x = -x;
  54. T x2 = x * x;
  55. // w is correction term due to skewness
  56. T w = x + sk * (x2 - 1) / 6;
  57. /*
  58. // Add on correction due to kurtosis.
  59. // Disabled for now, seems to make things worse?
  60. //
  61. if(lambda >= 10)
  62. w += k * x * (x2 - 3) / 24 + sk * sk * x * (2 * x2 - 5) / -36;
  63. */
  64. w = m + sigma * w;
  65. return w > tools::min_value<T>() ? w : tools::min_value<T>();
  66. }
  67. template <class T, class Policy>
  68. BOOST_MATH_GPU_ENABLED T gamma_inva_imp(const T& z, const T& p, const T& q, const Policy& pol)
  69. {
  70. BOOST_MATH_STD_USING // for ADL of std lib math functions
  71. //
  72. // Special cases first:
  73. //
  74. if(p == 0)
  75. {
  76. return policies::raise_overflow_error<T>("boost::math::gamma_p_inva<%1%>(%1%, %1%)", nullptr, Policy());
  77. }
  78. if(q == 0)
  79. {
  80. return tools::min_value<T>();
  81. }
  82. //
  83. // Function object, this is the functor whose root
  84. // we have to solve:
  85. //
  86. gamma_inva_t<T, Policy> f(z, (p < q) ? p : q, (p < q) ? false : true);
  87. //
  88. // Tolerance: full precision.
  89. //
  90. tools::eps_tolerance<T> tol(policies::digits<T, Policy>());
  91. //
  92. // Now figure out a starting guess for what a may be,
  93. // we'll start out with a value that'll put p or q
  94. // right bang in the middle of their range, the functions
  95. // are quite sensitive so we should need too many steps
  96. // to bracket the root from there:
  97. //
  98. T guess;
  99. T factor = 8;
  100. if(z >= 1)
  101. {
  102. //
  103. // We can use the relationship between the incomplete
  104. // gamma function and the poisson distribution to
  105. // calculate an approximate inverse, for large z
  106. // this is actually pretty accurate, but it fails badly
  107. // when z is very small. Also set our step-factor according
  108. // to how accurate we think the result is likely to be:
  109. //
  110. guess = 1 + inverse_poisson_cornish_fisher(z, q, p, pol);
  111. if(z > 5)
  112. {
  113. if(z > 1000)
  114. factor = 1.01f;
  115. else if(z > 50)
  116. factor = 1.1f;
  117. else if(guess > 10)
  118. factor = 1.25f;
  119. else
  120. factor = 2;
  121. if(guess < 1.1)
  122. factor = 8;
  123. }
  124. }
  125. else if(z > 0.5)
  126. {
  127. guess = z * 1.2f;
  128. }
  129. else
  130. {
  131. guess = -0.4f / log(z);
  132. }
  133. //
  134. // Max iterations permitted:
  135. //
  136. std::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
  137. //
  138. // Use our generic derivative-free root finding procedure.
  139. // We could use Newton steps here, taking the PDF of the
  140. // Poisson distribution as our derivative, but that's
  141. // even worse performance-wise than the generic method :-(
  142. //
  143. std::pair<T, T> r = bracket_and_solve_root(f, guess, factor, false, tol, max_iter, pol);
  144. if(max_iter >= policies::get_max_root_iterations<Policy>())
  145. return policies::raise_evaluation_error<T>("boost::math::gamma_p_inva<%1%>(%1%, %1%)", "Unable to locate the root within a reasonable number of iterations, closest approximation so far was %1%", r.first, pol);
  146. return (r.first + r.second) / 2;
  147. }
  148. } // namespace detail
  149. template <class T1, class T2, class Policy>
  150. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T1, T2>::type
  151. gamma_p_inva(T1 x, T2 p, const Policy& pol)
  152. {
  153. typedef typename tools::promote_args<T1, T2>::type result_type;
  154. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  155. typedef typename policies::normalise<
  156. Policy,
  157. policies::promote_float<false>,
  158. policies::promote_double<false>,
  159. policies::discrete_quantile<>,
  160. policies::assert_undefined<> >::type forwarding_policy;
  161. if(p == 0)
  162. {
  163. policies::raise_overflow_error<result_type>("boost::math::gamma_p_inva<%1%>(%1%, %1%)", nullptr, Policy());
  164. }
  165. if(p == 1)
  166. {
  167. return tools::min_value<result_type>();
  168. }
  169. return policies::checked_narrowing_cast<result_type, forwarding_policy>(
  170. detail::gamma_inva_imp(
  171. static_cast<value_type>(x),
  172. static_cast<value_type>(p),
  173. static_cast<value_type>(1 - static_cast<value_type>(p)),
  174. pol), "boost::math::gamma_p_inva<%1%>(%1%, %1%)");
  175. }
  176. template <class T1, class T2, class Policy>
  177. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T1, T2>::type
  178. gamma_q_inva(T1 x, T2 q, const Policy& pol)
  179. {
  180. typedef typename tools::promote_args<T1, T2>::type result_type;
  181. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  182. typedef typename policies::normalise<
  183. Policy,
  184. policies::promote_float<false>,
  185. policies::promote_double<false>,
  186. policies::discrete_quantile<>,
  187. policies::assert_undefined<> >::type forwarding_policy;
  188. if(q == 1)
  189. {
  190. policies::raise_overflow_error<result_type>("boost::math::gamma_q_inva<%1%>(%1%, %1%)", nullptr, Policy());
  191. }
  192. if(q == 0)
  193. {
  194. return tools::min_value<result_type>();
  195. }
  196. return policies::checked_narrowing_cast<result_type, forwarding_policy>(
  197. detail::gamma_inva_imp(
  198. static_cast<value_type>(x),
  199. static_cast<value_type>(1 - static_cast<value_type>(q)),
  200. static_cast<value_type>(q),
  201. pol), "boost::math::gamma_q_inva<%1%>(%1%, %1%)");
  202. }
  203. template <class T1, class T2>
  204. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T1, T2>::type
  205. gamma_p_inva(T1 x, T2 p)
  206. {
  207. return boost::math::gamma_p_inva(x, p, policies::policy<>());
  208. }
  209. template <class T1, class T2>
  210. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T1, T2>::type
  211. gamma_q_inva(T1 x, T2 q)
  212. {
  213. return boost::math::gamma_q_inva(x, q, policies::policy<>());
  214. }
  215. } // namespace math
  216. } // namespace boost
  217. #endif // BOOST_MATH_SP_DETAIL_GAMMA_INVA