hypergeometric_quantile.hpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // Copyright 2008 John Maddock
  2. //
  3. // Use, modification and distribution are subject to the
  4. // Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt
  6. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. #ifndef BOOST_MATH_DISTRIBUTIONS_DETAIL_HG_QUANTILE_HPP
  8. #define BOOST_MATH_DISTRIBUTIONS_DETAIL_HG_QUANTILE_HPP
  9. #include <boost/math/policies/error_handling.hpp>
  10. #include <boost/math/distributions/detail/hypergeometric_pdf.hpp>
  11. namespace boost{ namespace math{ namespace detail{
  12. template <class T>
  13. inline unsigned round_x_from_p(unsigned x, T p, T cum, T fudge_factor, unsigned lbound, unsigned /*ubound*/, const policies::discrete_quantile<policies::integer_round_down>&)
  14. {
  15. if((p < cum * fudge_factor) && (x != lbound))
  16. {
  17. BOOST_MATH_INSTRUMENT_VARIABLE(x-1);
  18. return --x;
  19. }
  20. return x;
  21. }
  22. template <class T>
  23. inline unsigned round_x_from_p(unsigned x, T p, T cum, T fudge_factor, unsigned /*lbound*/, unsigned ubound, const policies::discrete_quantile<policies::integer_round_up>&)
  24. {
  25. if((cum < p * fudge_factor) && (x != ubound))
  26. {
  27. BOOST_MATH_INSTRUMENT_VARIABLE(x+1);
  28. return ++x;
  29. }
  30. return x;
  31. }
  32. template <class T>
  33. inline unsigned round_x_from_p(unsigned x, T p, T cum, T fudge_factor, unsigned lbound, unsigned ubound, const policies::discrete_quantile<policies::integer_round_inwards>&)
  34. {
  35. if(p >= 0.5)
  36. return round_x_from_p(x, p, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_down>());
  37. return round_x_from_p(x, p, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_up>());
  38. }
  39. template <class T>
  40. inline unsigned round_x_from_p(unsigned x, T p, T cum, T fudge_factor, unsigned lbound, unsigned ubound, const policies::discrete_quantile<policies::integer_round_outwards>&)
  41. {
  42. if(p >= 0.5)
  43. return round_x_from_p(x, p, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_up>());
  44. return round_x_from_p(x, p, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_down>());
  45. }
  46. template <class T>
  47. inline unsigned round_x_from_p(unsigned x, T /*p*/, T /*cum*/, T /*fudge_factor*/, unsigned /*lbound*/, unsigned /*ubound*/, const policies::discrete_quantile<policies::integer_round_nearest>&)
  48. {
  49. return x;
  50. }
  51. template <class T>
  52. inline unsigned round_x_from_q(unsigned x, T q, T cum, T fudge_factor, unsigned lbound, unsigned /*ubound*/, const policies::discrete_quantile<policies::integer_round_down>&)
  53. {
  54. if((q * fudge_factor > cum) && (x != lbound))
  55. {
  56. BOOST_MATH_INSTRUMENT_VARIABLE(x-1);
  57. return --x;
  58. }
  59. return x;
  60. }
  61. template <class T>
  62. inline unsigned round_x_from_q(unsigned x, T q, T cum, T fudge_factor, unsigned /*lbound*/, unsigned ubound, const policies::discrete_quantile<policies::integer_round_up>&)
  63. {
  64. if((q < cum * fudge_factor) && (x != ubound))
  65. {
  66. BOOST_MATH_INSTRUMENT_VARIABLE(x+1);
  67. return ++x;
  68. }
  69. return x;
  70. }
  71. template <class T>
  72. inline unsigned round_x_from_q(unsigned x, T q, T cum, T fudge_factor, unsigned lbound, unsigned ubound, const policies::discrete_quantile<policies::integer_round_inwards>&)
  73. {
  74. if(q < 0.5)
  75. return round_x_from_q(x, q, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_down>());
  76. return round_x_from_q(x, q, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_up>());
  77. }
  78. template <class T>
  79. inline unsigned round_x_from_q(unsigned x, T q, T cum, T fudge_factor, unsigned lbound, unsigned ubound, const policies::discrete_quantile<policies::integer_round_outwards>&)
  80. {
  81. if(q >= 0.5)
  82. return round_x_from_q(x, q, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_down>());
  83. return round_x_from_q(x, q, cum, fudge_factor, lbound, ubound, policies::discrete_quantile<policies::integer_round_up>());
  84. }
  85. template <class T>
  86. inline unsigned round_x_from_q(unsigned x, T /*q*/, T /*cum*/, T /*fudge_factor*/, unsigned /*lbound*/, unsigned /*ubound*/, const policies::discrete_quantile<policies::integer_round_nearest>&)
  87. {
  88. return x;
  89. }
  90. template <class T, class Policy>
  91. unsigned hypergeometric_quantile_imp(T p, T q, unsigned r, unsigned n, unsigned N, const Policy& pol)
  92. {
  93. #ifdef BOOST_MSVC
  94. # pragma warning(push)
  95. # pragma warning(disable:4267)
  96. #endif
  97. typedef typename Policy::discrete_quantile_type discrete_quantile_type;
  98. BOOST_MATH_STD_USING
  99. BOOST_FPU_EXCEPTION_GUARD
  100. T result;
  101. T fudge_factor = 1 + tools::epsilon<T>() * ((N <= boost::math::prime(boost::math::max_prime - 1)) ? 50 : 2 * N);
  102. unsigned base = static_cast<unsigned>((std::max)(0, (int)(n + r) - (int)(N)));
  103. unsigned lim = (std::min)(r, n);
  104. BOOST_MATH_INSTRUMENT_VARIABLE(p);
  105. BOOST_MATH_INSTRUMENT_VARIABLE(q);
  106. BOOST_MATH_INSTRUMENT_VARIABLE(r);
  107. BOOST_MATH_INSTRUMENT_VARIABLE(n);
  108. BOOST_MATH_INSTRUMENT_VARIABLE(N);
  109. BOOST_MATH_INSTRUMENT_VARIABLE(fudge_factor);
  110. BOOST_MATH_INSTRUMENT_VARIABLE(base);
  111. BOOST_MATH_INSTRUMENT_VARIABLE(lim);
  112. if(p <= 0.5)
  113. {
  114. unsigned x = base;
  115. result = hypergeometric_pdf<T>(x, r, n, N, pol);
  116. T diff = result;
  117. if (diff == 0)
  118. {
  119. ++x;
  120. // We want to skip through x values as fast as we can until we start getting non-zero values,
  121. // otherwise we're just making lots of expensive PDF calls:
  122. T log_pdf = boost::math::lgamma(static_cast<T>(n + 1), pol)
  123. + boost::math::lgamma(static_cast<T>(r + 1), pol)
  124. + boost::math::lgamma(static_cast<T>(N - n + 1), pol)
  125. + boost::math::lgamma(static_cast<T>(N - r + 1), pol)
  126. - boost::math::lgamma(static_cast<T>(N + 1), pol)
  127. - boost::math::lgamma(static_cast<T>(x + 1), pol)
  128. - boost::math::lgamma(static_cast<T>(n - x + 1), pol)
  129. - boost::math::lgamma(static_cast<T>(r - x + 1), pol)
  130. - boost::math::lgamma(static_cast<T>(N - n - r + x + 1), pol);
  131. while (log_pdf < tools::log_min_value<T>())
  132. {
  133. log_pdf += -log(static_cast<T>(x + 1)) + log(static_cast<T>(n - x)) + log(static_cast<T>(r - x)) - log(static_cast<T>(N - n - r + x + 1));
  134. ++x;
  135. }
  136. // By the time we get here, log_pdf may be fairly inaccurate due to
  137. // roundoff errors, get a fresh PDF calculation before proceeding:
  138. diff = hypergeometric_pdf<T>(x, r, n, N, pol);
  139. }
  140. while(result < p)
  141. {
  142. diff = (diff > tools::min_value<T>() * 8)
  143. ? T(n - x) * T(r - x) * diff / (T(x + 1) * T(N + x + 1 - n - r))
  144. : hypergeometric_pdf<T>(x + 1, r, n, N, pol);
  145. if(result + diff / 2 > p)
  146. break;
  147. ++x;
  148. result += diff;
  149. #ifdef BOOST_MATH_INSTRUMENT
  150. if(diff != 0)
  151. {
  152. BOOST_MATH_INSTRUMENT_VARIABLE(x);
  153. BOOST_MATH_INSTRUMENT_VARIABLE(diff);
  154. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  155. }
  156. #endif
  157. }
  158. return round_x_from_p(x, p, result, fudge_factor, base, lim, discrete_quantile_type());
  159. }
  160. else
  161. {
  162. unsigned x = lim;
  163. result = 0;
  164. T diff = hypergeometric_pdf<T>(x, r, n, N, pol);
  165. if (diff == 0)
  166. {
  167. // We want to skip through x values as fast as we can until we start getting non-zero values,
  168. // otherwise we're just making lots of expensive PDF calls:
  169. --x;
  170. T log_pdf = boost::math::lgamma(static_cast<T>(n + 1), pol)
  171. + boost::math::lgamma(static_cast<T>(r + 1), pol)
  172. + boost::math::lgamma(static_cast<T>(N - n + 1), pol)
  173. + boost::math::lgamma(static_cast<T>(N - r + 1), pol)
  174. - boost::math::lgamma(static_cast<T>(N + 1), pol)
  175. - boost::math::lgamma(static_cast<T>(x + 1), pol)
  176. - boost::math::lgamma(static_cast<T>(n - x + 1), pol)
  177. - boost::math::lgamma(static_cast<T>(r - x + 1), pol)
  178. - boost::math::lgamma(static_cast<T>(N - n - r + x + 1), pol);
  179. while (log_pdf < tools::log_min_value<T>())
  180. {
  181. log_pdf += log(static_cast<T>(x)) - log(static_cast<T>(n - x + 1)) - log(static_cast<T>(r - x + 1)) + log(static_cast<T>(N - n - r + x));
  182. --x;
  183. }
  184. // By the time we get here, log_pdf may be fairly inaccurate due to
  185. // roundoff errors, get a fresh PDF calculation before proceeding:
  186. diff = hypergeometric_pdf<T>(x, r, n, N, pol);
  187. }
  188. while(result + diff / 2 < q)
  189. {
  190. result += diff;
  191. diff = (diff > tools::min_value<T>() * 8)
  192. ? x * T(N + x - n - r) * diff / (T(1 + n - x) * T(1 + r - x))
  193. : hypergeometric_pdf<T>(x - 1, r, n, N, pol);
  194. --x;
  195. #ifdef BOOST_MATH_INSTRUMENT
  196. if(diff != 0)
  197. {
  198. BOOST_MATH_INSTRUMENT_VARIABLE(x);
  199. BOOST_MATH_INSTRUMENT_VARIABLE(diff);
  200. BOOST_MATH_INSTRUMENT_VARIABLE(result);
  201. }
  202. #endif
  203. }
  204. return round_x_from_q(x, q, result, fudge_factor, base, lim, discrete_quantile_type());
  205. }
  206. #ifdef BOOST_MSVC
  207. # pragma warning(pop)
  208. #endif
  209. }
  210. template <class T, class Policy>
  211. inline unsigned hypergeometric_quantile(T p, T q, unsigned r, unsigned n, unsigned N, const Policy&)
  212. {
  213. BOOST_FPU_EXCEPTION_GUARD
  214. typedef typename tools::promote_args<T>::type result_type;
  215. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  216. typedef typename policies::normalise<
  217. Policy,
  218. policies::promote_float<false>,
  219. policies::promote_double<false>,
  220. policies::assert_undefined<> >::type forwarding_policy;
  221. return detail::hypergeometric_quantile_imp<value_type>(p, q, r, n, N, forwarding_policy());
  222. }
  223. }}} // namespaces
  224. #endif