inv_discrete_quantile.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. // Copyright John Maddock 2007.
  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_DISTRIBUTIONS_DETAIL_INV_DISCRETE_QUANTILE
  6. #define BOOST_MATH_DISTRIBUTIONS_DETAIL_INV_DISCRETE_QUANTILE
  7. #include <boost/math/tools/config.hpp>
  8. #include <boost/math/tools/cstdint.hpp>
  9. #include <boost/math/tools/precision.hpp>
  10. #include <boost/math/tools/toms748_solve.hpp>
  11. #include <boost/math/tools/tuple.hpp>
  12. namespace boost{ namespace math{ namespace detail{
  13. //
  14. // Functor for root finding algorithm:
  15. //
  16. template <class Dist>
  17. struct distribution_quantile_finder
  18. {
  19. typedef typename Dist::value_type value_type;
  20. typedef typename Dist::policy_type policy_type;
  21. BOOST_MATH_GPU_ENABLED distribution_quantile_finder(const Dist d, value_type p, bool c)
  22. : dist(d), target(p), comp(c) {}
  23. BOOST_MATH_GPU_ENABLED value_type operator()(value_type const& x)
  24. {
  25. return comp ? value_type(target - cdf(complement(dist, x))) : value_type(cdf(dist, x) - target);
  26. }
  27. private:
  28. Dist dist;
  29. value_type target;
  30. bool comp;
  31. };
  32. //
  33. // The purpose of adjust_bounds, is to toggle the last bit of the
  34. // range so that both ends round to the same integer, if possible.
  35. // If they do both round the same then we terminate the search
  36. // for the root *very* quickly when finding an integer result.
  37. // At the point that this function is called we know that "a" is
  38. // below the root and "b" above it, so this change can not result
  39. // in the root no longer being bracketed.
  40. //
  41. template <class Real, class Tol>
  42. BOOST_MATH_GPU_ENABLED void adjust_bounds(Real& /* a */, Real& /* b */, Tol const& /* tol */){}
  43. template <class Real>
  44. BOOST_MATH_GPU_ENABLED void adjust_bounds(Real& /* a */, Real& b, tools::equal_floor const& /* tol */)
  45. {
  46. BOOST_MATH_STD_USING
  47. b -= tools::epsilon<Real>() * b;
  48. }
  49. template <class Real>
  50. BOOST_MATH_GPU_ENABLED void adjust_bounds(Real& a, Real& /* b */, tools::equal_ceil const& /* tol */)
  51. {
  52. BOOST_MATH_STD_USING
  53. a += tools::epsilon<Real>() * a;
  54. }
  55. template <class Real>
  56. BOOST_MATH_GPU_ENABLED void adjust_bounds(Real& a, Real& b, tools::equal_nearest_integer const& /* tol */)
  57. {
  58. BOOST_MATH_STD_USING
  59. a += tools::epsilon<Real>() * a;
  60. b -= tools::epsilon<Real>() * b;
  61. }
  62. //
  63. // This is where all the work is done:
  64. //
  65. template <class Dist, class Tolerance>
  66. BOOST_MATH_GPU_ENABLED typename Dist::value_type
  67. do_inverse_discrete_quantile(
  68. const Dist& dist,
  69. const typename Dist::value_type& p,
  70. bool comp,
  71. typename Dist::value_type guess,
  72. const typename Dist::value_type& multiplier,
  73. typename Dist::value_type adder,
  74. const Tolerance& tol,
  75. boost::math::uintmax_t& max_iter)
  76. {
  77. typedef typename Dist::value_type value_type;
  78. typedef typename Dist::policy_type policy_type;
  79. constexpr auto function = "boost::math::do_inverse_discrete_quantile<%1%>";
  80. BOOST_MATH_STD_USING
  81. distribution_quantile_finder<Dist> f(dist, p, comp);
  82. //
  83. // Max bounds of the distribution:
  84. //
  85. value_type min_bound, max_bound;
  86. boost::math::tie(min_bound, max_bound) = support(dist);
  87. if(guess > max_bound)
  88. guess = max_bound;
  89. if(guess < min_bound)
  90. guess = min_bound;
  91. value_type fa = f(guess);
  92. boost::math::uintmax_t count = max_iter - 1;
  93. value_type fb(fa), a(guess), b =0; // Compiler warning C4701: potentially uninitialized local variable 'b' used
  94. if(fa == 0)
  95. return guess;
  96. //
  97. // For small expected results, just use a linear search:
  98. //
  99. if(guess < 10)
  100. {
  101. b = a;
  102. while((a < 10) && (fa * fb >= 0))
  103. {
  104. if(fb <= 0)
  105. {
  106. a = b;
  107. b = a + 1;
  108. if(b > max_bound)
  109. b = max_bound;
  110. fb = f(b);
  111. --count;
  112. if(fb == 0)
  113. return b;
  114. if(a == b)
  115. return b; // can't go any higher!
  116. }
  117. else
  118. {
  119. b = a;
  120. a = BOOST_MATH_GPU_SAFE_MAX(value_type(b - 1), value_type(0));
  121. if(a < min_bound)
  122. a = min_bound;
  123. fa = f(a);
  124. --count;
  125. if(fa == 0)
  126. return a;
  127. if(a == b)
  128. return a; // We can't go any lower than this!
  129. }
  130. }
  131. }
  132. //
  133. // Try and bracket using a couple of additions first,
  134. // we're assuming that "guess" is likely to be accurate
  135. // to the nearest int or so:
  136. //
  137. else if((adder != 0) && (a + adder != a))
  138. {
  139. //
  140. // If we're looking for a large result, then bump "adder" up
  141. // by a bit to increase our chances of bracketing the root:
  142. //
  143. //adder = BOOST_MATH_GPU_SAFE_MAX(adder, 0.001f * guess);
  144. if(fa < 0)
  145. {
  146. b = a + adder;
  147. if(b > max_bound)
  148. b = max_bound;
  149. }
  150. else
  151. {
  152. b = BOOST_MATH_GPU_SAFE_MAX(value_type(a - adder), value_type(0));
  153. if(b < min_bound)
  154. b = min_bound;
  155. }
  156. fb = f(b);
  157. --count;
  158. if(fb == 0)
  159. return b;
  160. if(count && (fa * fb >= 0))
  161. {
  162. //
  163. // We didn't bracket the root, try
  164. // once more:
  165. //
  166. a = b;
  167. fa = fb;
  168. if(fa < 0)
  169. {
  170. b = a + adder;
  171. if(b > max_bound)
  172. b = max_bound;
  173. }
  174. else
  175. {
  176. b = BOOST_MATH_GPU_SAFE_MAX(value_type(a - adder), value_type(0));
  177. if(b < min_bound)
  178. b = min_bound;
  179. }
  180. fb = f(b);
  181. --count;
  182. }
  183. if(a > b)
  184. {
  185. BOOST_MATH_GPU_SAFE_SWAP(a, b);
  186. BOOST_MATH_GPU_SAFE_SWAP(fa, fb);
  187. }
  188. }
  189. //
  190. // If the root hasn't been bracketed yet, try again
  191. // using the multiplier this time:
  192. //
  193. if((boost::math::sign)(fb) == (boost::math::sign)(fa))
  194. {
  195. if(fa < 0)
  196. {
  197. //
  198. // Zero is to the right of x2, so walk upwards
  199. // until we find it:
  200. //
  201. while(((boost::math::sign)(fb) == (boost::math::sign)(fa)) && (a != b))
  202. {
  203. if(count == 0)
  204. return policies::raise_evaluation_error(function, "Unable to bracket root, last nearest value was %1%", b, policy_type()); // LCOV_EXCL_LINE
  205. a = b;
  206. fa = fb;
  207. b *= multiplier;
  208. if(b > max_bound)
  209. b = max_bound;
  210. fb = f(b);
  211. --count;
  212. BOOST_MATH_INSTRUMENT_CODE("a = " << a << " b = " << b << " fa = " << fa << " fb = " << fb << " count = " << count);
  213. }
  214. }
  215. else
  216. {
  217. //
  218. // Zero is to the left of a, so walk downwards
  219. // until we find it:
  220. //
  221. while(((boost::math::sign)(fb) == (boost::math::sign)(fa)) && (a != b))
  222. {
  223. if(fabs(a) < tools::min_value<value_type>())
  224. {
  225. // Escape route just in case the answer is zero!
  226. max_iter -= count;
  227. max_iter += 1;
  228. return 0;
  229. }
  230. if(count == 0)
  231. return policies::raise_evaluation_error(function, "Unable to bracket root, last nearest value was %1%", a, policy_type()); // LCOV_EXCL_LINE
  232. b = a;
  233. fb = fa;
  234. a /= multiplier;
  235. if(a < min_bound)
  236. a = min_bound;
  237. fa = f(a);
  238. --count;
  239. BOOST_MATH_INSTRUMENT_CODE("a = " << a << " b = " << b << " fa = " << fa << " fb = " << fb << " count = " << count);
  240. }
  241. }
  242. }
  243. max_iter -= count;
  244. if(fa == 0)
  245. return a;
  246. if(fb == 0)
  247. return b;
  248. if(a == b)
  249. return b; // Ran out of bounds trying to bracket - there is no answer!
  250. //
  251. // Adjust bounds so that if we're looking for an integer
  252. // result, then both ends round the same way:
  253. //
  254. adjust_bounds(a, b, tol);
  255. //
  256. // We don't want zero or denorm lower bounds:
  257. //
  258. if(a < tools::min_value<value_type>())
  259. a = tools::min_value<value_type>();
  260. //
  261. // Go ahead and find the root:
  262. //
  263. boost::math::pair<value_type, value_type> r = toms748_solve(f, a, b, fa, fb, tol, count, policy_type());
  264. max_iter += count;
  265. if (max_iter >= policies::get_max_root_iterations<policy_type>())
  266. {
  267. return policies::raise_evaluation_error<value_type>(function, "Unable to locate solution in a reasonable time:" // LCOV_EXCL_LINE
  268. " either there is no answer to quantile or the answer is infinite. Current best guess is %1%", r.first, policy_type()); // LCOV_EXCL_LINE
  269. }
  270. BOOST_MATH_INSTRUMENT_CODE("max_iter = " << max_iter << " count = " << count);
  271. return (r.first + r.second) / 2;
  272. }
  273. //
  274. // Some special routine for rounding up and down:
  275. // We want to check and see if we are very close to an integer, and if so test to see if
  276. // that integer is an exact root of the cdf. We do this because our root finder only
  277. // guarantees to find *a root*, and there can sometimes be many consecutive floating
  278. // point values which are all roots. This is especially true if the target probability
  279. // is very close 1.
  280. //
  281. template <class Dist>
  282. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type round_to_floor(const Dist& d, typename Dist::value_type result, typename Dist::value_type p, bool c)
  283. {
  284. BOOST_MATH_STD_USING
  285. typename Dist::value_type cc = ceil(result);
  286. typename Dist::value_type pp = cc <= support(d).second ? c ? cdf(complement(d, cc)) : cdf(d, cc) : 1;
  287. if(pp == p)
  288. result = cc;
  289. else
  290. result = floor(result);
  291. //
  292. // Now find the smallest integer <= result for which we get an exact root:
  293. //
  294. while(result != 0)
  295. {
  296. #ifdef BOOST_MATH_HAS_GPU_SUPPORT
  297. cc = floor(::nextafter(result, -tools::max_value<typename Dist::value_type>()));
  298. #else
  299. cc = floor(float_prior(result));
  300. #endif
  301. if(cc < support(d).first)
  302. break;
  303. pp = c ? cdf(complement(d, cc)) : cdf(d, cc);
  304. if(c ? pp > p : pp < p)
  305. break;
  306. result = cc;
  307. }
  308. return result;
  309. }
  310. #ifdef _MSC_VER
  311. #pragma warning(push)
  312. #pragma warning(disable:4127)
  313. #endif
  314. template <class Dist>
  315. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type round_to_ceil(const Dist& d, typename Dist::value_type result, typename Dist::value_type p, bool c)
  316. {
  317. BOOST_MATH_STD_USING
  318. typename Dist::value_type cc = floor(result);
  319. typename Dist::value_type pp = cc >= support(d).first ? c ? cdf(complement(d, cc)) : cdf(d, cc) : 0;
  320. if(pp == p)
  321. result = cc;
  322. else
  323. result = ceil(result);
  324. //
  325. // Now find the largest integer >= result for which we get an exact root:
  326. //
  327. while(true)
  328. {
  329. #ifdef BOOST_MATH_HAS_GPU_SUPPORT
  330. cc = ceil(::nextafter(result, tools::max_value<typename Dist::value_type>()));
  331. #else
  332. cc = ceil(float_next(result));
  333. #endif
  334. if(cc > support(d).second)
  335. break;
  336. pp = c ? cdf(complement(d, cc)) : cdf(d, cc);
  337. if(c ? pp < p : pp > p)
  338. break;
  339. result = cc;
  340. }
  341. return result;
  342. }
  343. #ifdef _MSC_VER
  344. #pragma warning(pop)
  345. #endif
  346. //
  347. // Now finally are the public API functions.
  348. // There is one overload for each policy,
  349. // each one is responsible for selecting the correct
  350. // termination condition, and rounding the result
  351. // to an int where required.
  352. //
  353. template <class Dist>
  354. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type
  355. inverse_discrete_quantile(
  356. const Dist& dist,
  357. typename Dist::value_type p,
  358. bool c,
  359. const typename Dist::value_type& guess,
  360. const typename Dist::value_type& multiplier,
  361. const typename Dist::value_type& adder,
  362. const policies::discrete_quantile<policies::real>&,
  363. boost::math::uintmax_t& max_iter)
  364. {
  365. if(p > 0.5)
  366. {
  367. p = 1 - p;
  368. c = !c;
  369. }
  370. typename Dist::value_type pp = c ? 1 - p : p;
  371. if(pp <= pdf(dist, 0))
  372. return 0;
  373. return do_inverse_discrete_quantile(
  374. dist,
  375. p,
  376. c,
  377. guess,
  378. multiplier,
  379. adder,
  380. tools::eps_tolerance<typename Dist::value_type>(policies::digits<typename Dist::value_type, typename Dist::policy_type>()),
  381. max_iter);
  382. }
  383. template <class Dist>
  384. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type
  385. inverse_discrete_quantile(
  386. const Dist& dist,
  387. const typename Dist::value_type& p,
  388. bool c,
  389. const typename Dist::value_type& guess,
  390. const typename Dist::value_type& multiplier,
  391. const typename Dist::value_type& adder,
  392. const policies::discrete_quantile<policies::integer_round_outwards>&,
  393. boost::math::uintmax_t& max_iter)
  394. {
  395. typedef typename Dist::value_type value_type;
  396. BOOST_MATH_STD_USING
  397. typename Dist::value_type pp = c ? 1 - p : p;
  398. if(pp <= pdf(dist, 0))
  399. return 0;
  400. //
  401. // What happens next depends on whether we're looking for an
  402. // upper or lower quantile:
  403. //
  404. if(pp < 0.5f)
  405. return round_to_floor(dist, do_inverse_discrete_quantile(
  406. dist,
  407. p,
  408. c,
  409. (guess < 1 ? value_type(1) : (value_type)floor(guess)),
  410. multiplier,
  411. adder,
  412. tools::equal_floor(),
  413. max_iter), p, c);
  414. // else:
  415. return round_to_ceil(dist, do_inverse_discrete_quantile(
  416. dist,
  417. p,
  418. c,
  419. (value_type)ceil(guess),
  420. multiplier,
  421. adder,
  422. tools::equal_ceil(),
  423. max_iter), p, c);
  424. }
  425. template <class Dist>
  426. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type
  427. inverse_discrete_quantile(
  428. const Dist& dist,
  429. const typename Dist::value_type& p,
  430. bool c,
  431. const typename Dist::value_type& guess,
  432. const typename Dist::value_type& multiplier,
  433. const typename Dist::value_type& adder,
  434. const policies::discrete_quantile<policies::integer_round_inwards>&,
  435. boost::math::uintmax_t& max_iter)
  436. {
  437. typedef typename Dist::value_type value_type;
  438. BOOST_MATH_STD_USING
  439. typename Dist::value_type pp = c ? 1 - p : p;
  440. if(pp <= pdf(dist, 0))
  441. return 0;
  442. //
  443. // What happens next depends on whether we're looking for an
  444. // upper or lower quantile:
  445. //
  446. if(pp < 0.5f)
  447. return round_to_ceil(dist, do_inverse_discrete_quantile(
  448. dist,
  449. p,
  450. c,
  451. ceil(guess),
  452. multiplier,
  453. adder,
  454. tools::equal_ceil(),
  455. max_iter), p, c);
  456. // else:
  457. return round_to_floor(dist, do_inverse_discrete_quantile(
  458. dist,
  459. p,
  460. c,
  461. (guess < 1 ? value_type(1) : floor(guess)),
  462. multiplier,
  463. adder,
  464. tools::equal_floor(),
  465. max_iter), p, c);
  466. }
  467. template <class Dist>
  468. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type
  469. inverse_discrete_quantile(
  470. const Dist& dist,
  471. const typename Dist::value_type& p,
  472. bool c,
  473. const typename Dist::value_type& guess,
  474. const typename Dist::value_type& multiplier,
  475. const typename Dist::value_type& adder,
  476. const policies::discrete_quantile<policies::integer_round_down>&,
  477. boost::math::uintmax_t& max_iter)
  478. {
  479. typedef typename Dist::value_type value_type;
  480. BOOST_MATH_STD_USING
  481. typename Dist::value_type pp = c ? 1 - p : p;
  482. if(pp <= pdf(dist, 0))
  483. return 0;
  484. return round_to_floor(dist, do_inverse_discrete_quantile(
  485. dist,
  486. p,
  487. c,
  488. (guess < 1 ? value_type(1) : floor(guess)),
  489. multiplier,
  490. adder,
  491. tools::equal_floor(),
  492. max_iter), p, c);
  493. }
  494. template <class Dist>
  495. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type
  496. inverse_discrete_quantile(
  497. const Dist& dist,
  498. const typename Dist::value_type& p,
  499. bool c,
  500. const typename Dist::value_type& guess,
  501. const typename Dist::value_type& multiplier,
  502. const typename Dist::value_type& adder,
  503. const policies::discrete_quantile<policies::integer_round_up>&,
  504. boost::math::uintmax_t& max_iter)
  505. {
  506. BOOST_MATH_STD_USING
  507. typename Dist::value_type pp = c ? 1 - p : p;
  508. if(pp <= pdf(dist, 0))
  509. return 0;
  510. return round_to_ceil(dist, do_inverse_discrete_quantile(
  511. dist,
  512. p,
  513. c,
  514. ceil(guess),
  515. multiplier,
  516. adder,
  517. tools::equal_ceil(),
  518. max_iter), p, c);
  519. }
  520. template <class Dist>
  521. BOOST_MATH_GPU_ENABLED inline typename Dist::value_type
  522. inverse_discrete_quantile(
  523. const Dist& dist,
  524. const typename Dist::value_type& p,
  525. bool c,
  526. const typename Dist::value_type& guess,
  527. const typename Dist::value_type& multiplier,
  528. const typename Dist::value_type& adder,
  529. const policies::discrete_quantile<policies::integer_round_nearest>&,
  530. boost::math::uintmax_t& max_iter)
  531. {
  532. typedef typename Dist::value_type value_type;
  533. BOOST_MATH_STD_USING
  534. typename Dist::value_type pp = c ? 1 - p : p;
  535. if(pp <= pdf(dist, 0))
  536. return 0;
  537. //
  538. // Note that we adjust the guess to the nearest half-integer:
  539. // this increase the chances that we will bracket the root
  540. // with two results that both round to the same integer quickly.
  541. //
  542. return round_to_floor(dist, do_inverse_discrete_quantile(
  543. dist,
  544. p,
  545. c,
  546. (guess < 0.5f ? value_type(1.5f) : floor(guess + 0.5f) + 0.5f),
  547. multiplier,
  548. adder,
  549. tools::equal_nearest_integer(),
  550. max_iter) + 0.5f, p, c);
  551. }
  552. }}} // namespaces
  553. #endif // BOOST_MATH_DISTRIBUTIONS_DETAIL_INV_DISCRETE_QUANTILE