poisson.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. // boost\math\distributions\poisson.hpp
  2. // Copyright John Maddock 2006.
  3. // Copyright Paul A. Bristow 2007.
  4. // Copyright Matt Borland 2024.
  5. // Use, modification and distribution are subject to the
  6. // Boost Software License, Version 1.0.
  7. // (See accompanying file LICENSE_1_0.txt
  8. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. // Poisson distribution is a discrete probability distribution.
  10. // It expresses the probability of a number (k) of
  11. // events, occurrences, failures or arrivals occurring in a fixed time,
  12. // assuming these events occur with a known average or mean rate (lambda)
  13. // and are independent of the time since the last event.
  14. // The distribution was discovered by Simeon-Denis Poisson (1781-1840).
  15. // Parameter lambda is the mean number of events in the given time interval.
  16. // The random variate k is the number of events, occurrences or arrivals.
  17. // k argument may be integral, signed, or unsigned, or floating point.
  18. // If necessary, it has already been promoted from an integral type.
  19. // Note that the Poisson distribution
  20. // (like others including the binomial, negative binomial & Bernoulli)
  21. // is strictly defined as a discrete function:
  22. // only integral values of k are envisaged.
  23. // However because the method of calculation uses a continuous gamma function,
  24. // it is convenient to treat it as if a continuous function,
  25. // and permit non-integral values of k.
  26. // To enforce the strict mathematical model, users should use floor or ceil functions
  27. // on k outside this function to ensure that k is integral.
  28. // See http://en.wikipedia.org/wiki/Poisson_distribution
  29. // http://documents.wolfram.com/v5/Add-onsLinks/StandardPackages/Statistics/DiscreteDistributions.html
  30. #ifndef BOOST_MATH_SPECIAL_POISSON_HPP
  31. #define BOOST_MATH_SPECIAL_POISSON_HPP
  32. #include <boost/math/tools/config.hpp>
  33. #include <boost/math/tools/tuple.hpp>
  34. #include <boost/math/tools/cstdint.hpp>
  35. #include <boost/math/tools/numeric_limits.hpp>
  36. #include <boost/math/distributions/fwd.hpp>
  37. #include <boost/math/special_functions/gamma.hpp> // for incomplete gamma. gamma_q
  38. #include <boost/math/special_functions/trunc.hpp> // for incomplete gamma. gamma_q
  39. #include <boost/math/distributions/complement.hpp> // complements
  40. #include <boost/math/distributions/detail/common_error_handling.hpp> // error checks
  41. #include <boost/math/special_functions/fpclassify.hpp> // isnan.
  42. #include <boost/math/special_functions/factorials.hpp> // factorials.
  43. #include <boost/math/tools/roots.hpp> // for root finding.
  44. #include <boost/math/distributions/detail/inv_discrete_quantile.hpp>
  45. namespace boost
  46. {
  47. namespace math
  48. {
  49. namespace poisson_detail
  50. {
  51. // Common error checking routines for Poisson distribution functions.
  52. // These are convoluted, & apparently redundant, to try to ensure that
  53. // checks are always performed, even if exceptions are not enabled.
  54. template <class RealType, class Policy>
  55. BOOST_MATH_GPU_ENABLED inline bool check_mean(const char* function, const RealType& mean, RealType* result, const Policy& pol)
  56. {
  57. if(!(boost::math::isfinite)(mean) || (mean < 0))
  58. {
  59. *result = policies::raise_domain_error<RealType>(
  60. function,
  61. "Mean argument is %1%, but must be >= 0 !", mean, pol);
  62. return false;
  63. }
  64. return true;
  65. } // bool check_mean
  66. template <class RealType, class Policy>
  67. BOOST_MATH_GPU_ENABLED inline bool check_mean_NZ(const char* function, const RealType& mean, RealType* result, const Policy& pol)
  68. { // mean == 0 is considered an error.
  69. if( !(boost::math::isfinite)(mean) || (mean <= 0))
  70. {
  71. *result = policies::raise_domain_error<RealType>(
  72. function,
  73. "Mean argument is %1%, but must be > 0 !", mean, pol);
  74. return false;
  75. }
  76. return true;
  77. } // bool check_mean_NZ
  78. template <class RealType, class Policy>
  79. BOOST_MATH_GPU_ENABLED inline bool check_dist(const char* function, const RealType& mean, RealType* result, const Policy& pol)
  80. { // Only one check, so this is redundant really but should be optimized away.
  81. return check_mean_NZ(function, mean, result, pol);
  82. } // bool check_dist
  83. template <class RealType, class Policy>
  84. BOOST_MATH_GPU_ENABLED inline bool check_k(const char* function, const RealType& k, RealType* result, const Policy& pol)
  85. {
  86. if((k < 0) || !(boost::math::isfinite)(k))
  87. {
  88. *result = policies::raise_domain_error<RealType>(
  89. function,
  90. "Number of events k argument is %1%, but must be >= 0 !", k, pol);
  91. return false;
  92. }
  93. return true;
  94. } // bool check_k
  95. template <class RealType, class Policy>
  96. BOOST_MATH_GPU_ENABLED inline bool check_dist_and_k(const char* function, RealType mean, RealType k, RealType* result, const Policy& pol)
  97. {
  98. if((check_dist(function, mean, result, pol) == false) ||
  99. (check_k(function, k, result, pol) == false))
  100. {
  101. return false;
  102. }
  103. return true;
  104. } // bool check_dist_and_k
  105. template <class RealType, class Policy>
  106. BOOST_MATH_GPU_ENABLED inline bool check_prob(const char* function, const RealType& p, RealType* result, const Policy& pol)
  107. { // Check 0 <= p <= 1
  108. if(!(boost::math::isfinite)(p) || (p < 0) || (p > 1))
  109. {
  110. *result = policies::raise_domain_error<RealType>(
  111. function,
  112. "Probability argument is %1%, but must be >= 0 and <= 1 !", p, pol);
  113. return false;
  114. }
  115. return true;
  116. } // bool check_prob
  117. template <class RealType, class Policy>
  118. BOOST_MATH_GPU_ENABLED inline bool check_dist_and_prob(const char* function, RealType mean, RealType p, RealType* result, const Policy& pol)
  119. {
  120. if((check_dist(function, mean, result, pol) == false) ||
  121. (check_prob(function, p, result, pol) == false))
  122. {
  123. return false;
  124. }
  125. return true;
  126. } // bool check_dist_and_prob
  127. } // namespace poisson_detail
  128. template <class RealType = double, class Policy = policies::policy<> >
  129. class poisson_distribution
  130. {
  131. public:
  132. using value_type = RealType;
  133. using policy_type = Policy;
  134. BOOST_MATH_GPU_ENABLED explicit poisson_distribution(RealType l_mean = 1) : m_l(l_mean) // mean (lambda).
  135. { // Expected mean number of events that occur during the given interval.
  136. RealType r;
  137. poisson_detail::check_dist(
  138. "boost::math::poisson_distribution<%1%>::poisson_distribution",
  139. m_l,
  140. &r, Policy());
  141. } // poisson_distribution constructor.
  142. BOOST_MATH_GPU_ENABLED RealType mean() const
  143. { // Private data getter function.
  144. return m_l;
  145. }
  146. private:
  147. // Data member, initialized by constructor.
  148. RealType m_l; // mean number of occurrences.
  149. }; // template <class RealType, class Policy> class poisson_distribution
  150. using poisson = poisson_distribution<double>; // Reserved name of type double.
  151. #ifdef __cpp_deduction_guides
  152. template <class RealType>
  153. poisson_distribution(RealType)->poisson_distribution<typename boost::math::tools::promote_args<RealType>::type>;
  154. #endif
  155. // Non-member functions to give properties of the distribution.
  156. template <class RealType, class Policy>
  157. BOOST_MATH_GPU_ENABLED inline boost::math::pair<RealType, RealType> range(const poisson_distribution<RealType, Policy>& /* dist */)
  158. { // Range of permissible values for random variable k.
  159. using boost::math::tools::max_value;
  160. return boost::math::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // Max integer?
  161. }
  162. template <class RealType, class Policy>
  163. BOOST_MATH_GPU_ENABLED inline boost::math::pair<RealType, RealType> support(const poisson_distribution<RealType, Policy>& /* dist */)
  164. { // Range of supported values for random variable k.
  165. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
  166. using boost::math::tools::max_value;
  167. return boost::math::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>());
  168. }
  169. template <class RealType, class Policy>
  170. BOOST_MATH_GPU_ENABLED inline RealType mean(const poisson_distribution<RealType, Policy>& dist)
  171. { // Mean of poisson distribution = lambda.
  172. return dist.mean();
  173. } // mean
  174. template <class RealType, class Policy>
  175. BOOST_MATH_GPU_ENABLED inline RealType mode(const poisson_distribution<RealType, Policy>& dist)
  176. { // mode.
  177. BOOST_MATH_STD_USING // ADL of std functions.
  178. return floor(dist.mean());
  179. }
  180. // Median now implemented via quantile(half) in derived accessors.
  181. template <class RealType, class Policy>
  182. BOOST_MATH_GPU_ENABLED inline RealType variance(const poisson_distribution<RealType, Policy>& dist)
  183. { // variance.
  184. return dist.mean();
  185. }
  186. // standard_deviation provided by derived accessors.
  187. template <class RealType, class Policy>
  188. BOOST_MATH_GPU_ENABLED inline RealType skewness(const poisson_distribution<RealType, Policy>& dist)
  189. { // skewness = sqrt(l).
  190. BOOST_MATH_STD_USING // ADL of std functions.
  191. return 1 / sqrt(dist.mean());
  192. }
  193. template <class RealType, class Policy>
  194. BOOST_MATH_GPU_ENABLED inline RealType kurtosis_excess(const poisson_distribution<RealType, Policy>& dist)
  195. { // skewness = sqrt(l).
  196. return 1 / dist.mean(); // kurtosis_excess 1/mean from Wiki & MathWorld eq 31.
  197. // http://mathworld.wolfram.com/Kurtosis.html explains that the kurtosis excess
  198. // is more convenient because the kurtosis excess of a normal distribution is zero
  199. // whereas the true kurtosis is 3.
  200. } // RealType kurtosis_excess
  201. template <class RealType, class Policy>
  202. BOOST_MATH_GPU_ENABLED inline RealType kurtosis(const poisson_distribution<RealType, Policy>& dist)
  203. { // kurtosis is 4th moment about the mean = u4 / sd ^ 4
  204. // http://en.wikipedia.org/wiki/Kurtosis
  205. // kurtosis can range from -2 (flat top) to +infinity (sharp peak & heavy tails).
  206. // http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
  207. return 3 + 1 / dist.mean(); // NIST.
  208. // http://mathworld.wolfram.com/Kurtosis.html explains that the kurtosis excess
  209. // is more convenient because the kurtosis excess of a normal distribution is zero
  210. // whereas the true kurtosis is 3.
  211. } // RealType kurtosis
  212. template <class RealType, class Policy>
  213. BOOST_MATH_GPU_ENABLED RealType pdf(const poisson_distribution<RealType, Policy>& dist, const RealType& k)
  214. { // Probability Density/Mass Function.
  215. // Probability that there are EXACTLY k occurrences (or arrivals).
  216. BOOST_FPU_EXCEPTION_GUARD
  217. BOOST_MATH_STD_USING // for ADL of std functions.
  218. RealType mean = dist.mean();
  219. // Error check:
  220. RealType result = 0;
  221. if(false == poisson_detail::check_dist_and_k(
  222. "boost::math::pdf(const poisson_distribution<%1%>&, %1%)",
  223. mean,
  224. k,
  225. &result, Policy()))
  226. {
  227. return result;
  228. }
  229. // Special case of mean zero, regardless of the number of events k.
  230. if (mean == 0)
  231. { // Probability for any k is zero.
  232. return 0;
  233. }
  234. if (k == 0)
  235. { // mean ^ k = 1, and k! = 1, so can simplify.
  236. return exp(-mean);
  237. }
  238. return boost::math::gamma_p_derivative(k+1, mean, Policy());
  239. } // pdf
  240. template <class RealType, class Policy>
  241. BOOST_MATH_GPU_ENABLED RealType logpdf(const poisson_distribution<RealType, Policy>& dist, const RealType& k)
  242. {
  243. BOOST_FPU_EXCEPTION_GUARD
  244. BOOST_MATH_STD_USING // for ADL of std functions.
  245. using boost::math::lgamma;
  246. RealType mean = dist.mean();
  247. // Error check:
  248. RealType result = -boost::math::numeric_limits<RealType>::infinity();
  249. if(false == poisson_detail::check_dist_and_k(
  250. "boost::math::pdf(const poisson_distribution<%1%>&, %1%)",
  251. mean,
  252. k,
  253. &result, Policy()))
  254. {
  255. return result;
  256. }
  257. // Special case of mean zero, regardless of the number of events k.
  258. if (mean == 0)
  259. { // Probability for any k is zero.
  260. return boost::math::numeric_limits<RealType>::quiet_NaN();
  261. }
  262. // Special case where k and lambda are both positive
  263. if(k > 0 && mean > 0)
  264. {
  265. return -lgamma(k+1) + k*log(mean) - mean;
  266. }
  267. result = log(pdf(dist, k));
  268. return result;
  269. }
  270. template <class RealType, class Policy>
  271. BOOST_MATH_GPU_ENABLED RealType cdf(const poisson_distribution<RealType, Policy>& dist, const RealType& k)
  272. { // Cumulative Distribution Function Poisson.
  273. // The random variate k is the number of occurrences(or arrivals)
  274. // k argument may be integral, signed, or unsigned, or floating point.
  275. // If necessary, it has already been promoted from an integral type.
  276. // Returns the sum of the terms 0 through k of the Poisson Probability Density or Mass (pdf).
  277. // But note that the Poisson distribution
  278. // (like others including the binomial, negative binomial & Bernoulli)
  279. // is strictly defined as a discrete function: only integral values of k are envisaged.
  280. // However because of the method of calculation using a continuous gamma function,
  281. // it is convenient to treat it as if it is a continuous function
  282. // and permit non-integral values of k.
  283. // To enforce the strict mathematical model, users should use floor or ceil functions
  284. // outside this function to ensure that k is integral.
  285. // The terms are not summed directly (at least for larger k)
  286. // instead the incomplete gamma integral is employed,
  287. BOOST_MATH_STD_USING // for ADL of std function exp.
  288. RealType mean = dist.mean();
  289. // Error checks:
  290. RealType result = 0;
  291. if(false == poisson_detail::check_dist_and_k(
  292. "boost::math::cdf(const poisson_distribution<%1%>&, %1%)",
  293. mean,
  294. k,
  295. &result, Policy()))
  296. {
  297. return result;
  298. }
  299. // Special cases:
  300. if (mean == 0)
  301. { // Probability for any k is zero.
  302. return 0;
  303. }
  304. if (k == 0)
  305. {
  306. // mean (and k) have already been checked,
  307. // so this avoids unnecessary repeated checks.
  308. return exp(-mean);
  309. }
  310. // For small integral k could use a finite sum -
  311. // it's cheaper than the gamma function.
  312. // BUT this is now done efficiently by gamma_q function.
  313. // Calculate poisson cdf using the gamma_q function.
  314. return gamma_q(k+1, mean, Policy());
  315. } // binomial cdf
  316. template <class RealType, class Policy>
  317. BOOST_MATH_GPU_ENABLED RealType cdf(const complemented2_type<poisson_distribution<RealType, Policy>, RealType>& c)
  318. { // Complemented Cumulative Distribution Function Poisson
  319. // The random variate k is the number of events, occurrences or arrivals.
  320. // k argument may be integral, signed, or unsigned, or floating point.
  321. // If necessary, it has already been promoted from an integral type.
  322. // But note that the Poisson distribution
  323. // (like others including the binomial, negative binomial & Bernoulli)
  324. // is strictly defined as a discrete function: only integral values of k are envisaged.
  325. // However because of the method of calculation using a continuous gamma function,
  326. // it is convenient to treat it as is it is a continuous function
  327. // and permit non-integral values of k.
  328. // To enforce the strict mathematical model, users should use floor or ceil functions
  329. // outside this function to ensure that k is integral.
  330. // Returns the sum of the terms k+1 through inf of the Poisson Probability Density/Mass (pdf).
  331. // The terms are not summed directly (at least for larger k)
  332. // instead the incomplete gamma integral is employed,
  333. RealType const& k = c.param;
  334. poisson_distribution<RealType, Policy> const& dist = c.dist;
  335. RealType mean = dist.mean();
  336. // Error checks:
  337. RealType result = 0;
  338. if(false == poisson_detail::check_dist_and_k(
  339. "boost::math::cdf(const poisson_distribution<%1%>&, %1%)",
  340. mean,
  341. k,
  342. &result, Policy()))
  343. {
  344. return result;
  345. }
  346. // Special case of mean, regardless of the number of events k.
  347. if (mean == 0)
  348. { // Probability for any k is unity, complement of zero.
  349. return 1;
  350. }
  351. if (k == 0)
  352. { // Avoid repeated checks on k and mean in gamma_p.
  353. return -boost::math::expm1(-mean, Policy());
  354. }
  355. // Unlike un-complemented cdf (sum from 0 to k),
  356. // can't use finite sum from k+1 to infinity for small integral k,
  357. // anyway it is now done efficiently by gamma_p.
  358. return gamma_p(k + 1, mean, Policy()); // Calculate Poisson cdf using the gamma_p function.
  359. // CCDF = gamma_p(k+1, lambda)
  360. } // poisson ccdf
  361. template <class RealType, class Policy>
  362. BOOST_MATH_GPU_ENABLED inline RealType quantile(const poisson_distribution<RealType, Policy>& dist, const RealType& p)
  363. { // Quantile (or Percent Point) Poisson function.
  364. // Return the number of expected events k for a given probability p.
  365. constexpr auto function = "boost::math::quantile(const poisson_distribution<%1%>&, %1%)";
  366. RealType result = 0; // of Argument checks:
  367. if(false == poisson_detail::check_prob(
  368. function,
  369. p,
  370. &result, Policy()))
  371. {
  372. return result;
  373. }
  374. // Special case:
  375. if (dist.mean() == 0)
  376. { // if mean = 0 then p = 0, so k can be anything?
  377. if (false == poisson_detail::check_mean_NZ(
  378. function,
  379. dist.mean(),
  380. &result, Policy()))
  381. {
  382. return result;
  383. }
  384. }
  385. if(p == 0)
  386. {
  387. return 0; // Exact result regardless of discrete-quantile Policy
  388. }
  389. if(p == 1)
  390. {
  391. return policies::raise_overflow_error<RealType>(function, 0, Policy());
  392. }
  393. using discrete_type = typename Policy::discrete_quantile_type;
  394. boost::math::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
  395. RealType guess;
  396. RealType factor = 8;
  397. RealType z = dist.mean();
  398. if(z < 1)
  399. guess = z;
  400. else
  401. guess = boost::math::detail::inverse_poisson_cornish_fisher(z, p, RealType(1-p), Policy());
  402. if(z > 5)
  403. {
  404. if(z > 1000)
  405. factor = 1.01f;
  406. else if(z > 50)
  407. factor = 1.1f;
  408. else if(guess > 10)
  409. factor = 1.25f;
  410. else
  411. factor = 2;
  412. if(guess < 1.1)
  413. factor = 8;
  414. }
  415. return detail::inverse_discrete_quantile(
  416. dist,
  417. p,
  418. false,
  419. guess,
  420. factor,
  421. RealType(1),
  422. discrete_type(),
  423. max_iter);
  424. } // quantile
  425. template <class RealType, class Policy>
  426. BOOST_MATH_GPU_ENABLED inline RealType quantile(const complemented2_type<poisson_distribution<RealType, Policy>, RealType>& c)
  427. { // Quantile (or Percent Point) of Poisson function.
  428. // Return the number of expected events k for a given
  429. // complement of the probability q.
  430. //
  431. // Error checks:
  432. constexpr auto function = "boost::math::quantile(complement(const poisson_distribution<%1%>&, %1%))";
  433. RealType q = c.param;
  434. const poisson_distribution<RealType, Policy>& dist = c.dist;
  435. RealType result = 0; // of argument checks.
  436. if(false == poisson_detail::check_prob(
  437. function,
  438. q,
  439. &result, Policy()))
  440. {
  441. return result;
  442. }
  443. // Special case:
  444. if (dist.mean() == 0)
  445. { // if mean = 0 then p = 0, so k can be anything?
  446. if (false == poisson_detail::check_mean_NZ(
  447. function,
  448. dist.mean(),
  449. &result, Policy()))
  450. {
  451. return result;
  452. }
  453. }
  454. if(q == 0)
  455. {
  456. return policies::raise_overflow_error<RealType>(function, 0, Policy());
  457. }
  458. if(q == 1)
  459. {
  460. return 0; // Exact result regardless of discrete-quantile Policy
  461. }
  462. using discrete_type = typename Policy::discrete_quantile_type;
  463. boost::math::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
  464. RealType guess;
  465. RealType factor = 8;
  466. RealType z = dist.mean();
  467. if(z < 1)
  468. guess = z;
  469. else
  470. guess = boost::math::detail::inverse_poisson_cornish_fisher(z, RealType(1-q), q, Policy());
  471. if(z > 5)
  472. {
  473. if(z > 1000)
  474. factor = 1.01f;
  475. else if(z > 50)
  476. factor = 1.1f;
  477. else if(guess > 10)
  478. factor = 1.25f;
  479. else
  480. factor = 2;
  481. if(guess < 1.1)
  482. factor = 8;
  483. }
  484. return detail::inverse_discrete_quantile(
  485. dist,
  486. q,
  487. true,
  488. guess,
  489. factor,
  490. RealType(1),
  491. discrete_type(),
  492. max_iter);
  493. } // quantile complement.
  494. } // namespace math
  495. } // namespace boost
  496. // This include must be at the end, *after* the accessors
  497. // for this distribution have been defined, in order to
  498. // keep compilers that support two-phase lookup happy.
  499. #include <boost/math/distributions/detail/derived_accessors.hpp>
  500. #endif // BOOST_MATH_SPECIAL_POISSON_HPP