Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:35:33

0001 // boost\math\distributions\poisson.hpp
0002 
0003 // Copyright John Maddock 2006.
0004 // Copyright Paul A. Bristow 2007.
0005 // Copyright Matt Borland 2024.
0006 
0007 // Use, modification and distribution are subject to the
0008 // Boost Software License, Version 1.0.
0009 // (See accompanying file LICENSE_1_0.txt
0010 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0011 
0012 // Poisson distribution is a discrete probability distribution.
0013 // It expresses the probability of a number (k) of
0014 // events, occurrences, failures or arrivals occurring in a fixed time,
0015 // assuming these events occur with a known average or mean rate (lambda)
0016 // and are independent of the time since the last event.
0017 // The distribution was discovered by Simeon-Denis Poisson (1781-1840).
0018 
0019 // Parameter lambda is the mean number of events in the given time interval.
0020 // The random variate k is the number of events, occurrences or arrivals.
0021 // k argument may be integral, signed, or unsigned, or floating point.
0022 // If necessary, it has already been promoted from an integral type.
0023 
0024 // Note that the Poisson distribution
0025 // (like others including the binomial, negative binomial & Bernoulli)
0026 // is strictly defined as a discrete function:
0027 // only integral values of k are envisaged.
0028 // However because the method of calculation uses a continuous gamma function,
0029 // it is convenient to treat it as if a continuous function,
0030 // and permit non-integral values of k.
0031 // To enforce the strict mathematical model, users should use floor or ceil functions
0032 // on k outside this function to ensure that k is integral.
0033 
0034 // See http://en.wikipedia.org/wiki/Poisson_distribution
0035 // http://documents.wolfram.com/v5/Add-onsLinks/StandardPackages/Statistics/DiscreteDistributions.html
0036 
0037 #ifndef BOOST_MATH_SPECIAL_POISSON_HPP
0038 #define BOOST_MATH_SPECIAL_POISSON_HPP
0039 
0040 #include <boost/math/tools/config.hpp>
0041 #include <boost/math/tools/tuple.hpp>
0042 #include <boost/math/tools/cstdint.hpp>
0043 #include <boost/math/tools/numeric_limits.hpp>
0044 #include <boost/math/distributions/fwd.hpp>
0045 #include <boost/math/special_functions/gamma.hpp> // for incomplete gamma. gamma_q
0046 #include <boost/math/special_functions/trunc.hpp> // for incomplete gamma. gamma_q
0047 #include <boost/math/distributions/complement.hpp> // complements
0048 #include <boost/math/distributions/detail/common_error_handling.hpp> // error checks
0049 #include <boost/math/special_functions/fpclassify.hpp> // isnan.
0050 #include <boost/math/special_functions/factorials.hpp> // factorials.
0051 #include <boost/math/tools/roots.hpp> // for root finding.
0052 #include <boost/math/distributions/detail/inv_discrete_quantile.hpp>
0053 
0054 namespace boost
0055 {
0056   namespace math
0057   {
0058     namespace poisson_detail
0059     {
0060       // Common error checking routines for Poisson distribution functions.
0061       // These are convoluted, & apparently redundant, to try to ensure that
0062       // checks are always performed, even if exceptions are not enabled.
0063 
0064       template <class RealType, class Policy>
0065       BOOST_MATH_GPU_ENABLED inline bool check_mean(const char* function, const RealType& mean, RealType* result, const Policy& pol)
0066       {
0067         if(!(boost::math::isfinite)(mean) || (mean < 0))
0068         {
0069           *result = policies::raise_domain_error<RealType>(
0070             function,
0071             "Mean argument is %1%, but must be >= 0 !", mean, pol);
0072           return false;
0073         }
0074         return true;
0075       } // bool check_mean
0076 
0077       template <class RealType, class Policy>
0078       BOOST_MATH_GPU_ENABLED inline bool check_mean_NZ(const char* function, const RealType& mean, RealType* result, const Policy& pol)
0079       { // mean == 0 is considered an error.
0080         if( !(boost::math::isfinite)(mean) || (mean <= 0))
0081         {
0082           *result = policies::raise_domain_error<RealType>(
0083             function,
0084             "Mean argument is %1%, but must be > 0 !", mean, pol);
0085           return false;
0086         }
0087         return true;
0088       } // bool check_mean_NZ
0089 
0090       template <class RealType, class Policy>
0091       BOOST_MATH_GPU_ENABLED inline bool check_dist(const char* function, const RealType& mean, RealType* result, const Policy& pol)
0092       { // Only one check, so this is redundant really but should be optimized away.
0093         return check_mean_NZ(function, mean, result, pol);
0094       } // bool check_dist
0095 
0096       template <class RealType, class Policy>
0097       BOOST_MATH_GPU_ENABLED inline bool check_k(const char* function, const RealType& k, RealType* result, const Policy& pol)
0098       {
0099         if((k < 0) || !(boost::math::isfinite)(k))
0100         {
0101           *result = policies::raise_domain_error<RealType>(
0102             function,
0103             "Number of events k argument is %1%, but must be >= 0 !", k, pol);
0104           return false;
0105         }
0106         return true;
0107       } // bool check_k
0108 
0109       template <class RealType, class Policy>
0110       BOOST_MATH_GPU_ENABLED inline bool check_dist_and_k(const char* function, RealType mean, RealType k, RealType* result, const Policy& pol)
0111       {
0112         if((check_dist(function, mean, result, pol) == false) ||
0113           (check_k(function, k, result, pol) == false))
0114         {
0115           return false;
0116         }
0117         return true;
0118       } // bool check_dist_and_k
0119 
0120       template <class RealType, class Policy>
0121       BOOST_MATH_GPU_ENABLED inline bool check_prob(const char* function, const RealType& p, RealType* result, const Policy& pol)
0122       { // Check 0 <= p <= 1
0123         if(!(boost::math::isfinite)(p) || (p < 0) || (p > 1))
0124         {
0125           *result = policies::raise_domain_error<RealType>(
0126             function,
0127             "Probability argument is %1%, but must be >= 0 and <= 1 !", p, pol);
0128           return false;
0129         }
0130         return true;
0131       } // bool check_prob
0132 
0133       template <class RealType, class Policy>
0134       BOOST_MATH_GPU_ENABLED inline bool check_dist_and_prob(const char* function, RealType mean,  RealType p, RealType* result, const Policy& pol)
0135       {
0136         if((check_dist(function, mean, result, pol) == false) ||
0137           (check_prob(function, p, result, pol) == false))
0138         {
0139           return false;
0140         }
0141         return true;
0142       } // bool check_dist_and_prob
0143 
0144     } // namespace poisson_detail
0145 
0146     template <class RealType = double, class Policy = policies::policy<> >
0147     class poisson_distribution
0148     {
0149     public:
0150       using value_type = RealType;
0151       using policy_type = Policy;
0152 
0153       BOOST_MATH_GPU_ENABLED explicit poisson_distribution(RealType l_mean = 1) : m_l(l_mean) // mean (lambda).
0154       { // Expected mean number of events that occur during the given interval.
0155         RealType r;
0156         poisson_detail::check_dist(
0157            "boost::math::poisson_distribution<%1%>::poisson_distribution",
0158           m_l,
0159           &r, Policy());
0160       } // poisson_distribution constructor.
0161 
0162       BOOST_MATH_GPU_ENABLED RealType mean() const
0163       { // Private data getter function.
0164         return m_l;
0165       }
0166     private:
0167       // Data member, initialized by constructor.
0168       RealType m_l; // mean number of occurrences.
0169     }; // template <class RealType, class Policy> class poisson_distribution
0170 
0171     using poisson = poisson_distribution<double>; // Reserved name of type double.
0172 
0173     #ifdef __cpp_deduction_guides
0174     template <class RealType>
0175     poisson_distribution(RealType)->poisson_distribution<typename boost::math::tools::promote_args<RealType>::type>;
0176     #endif
0177 
0178     // Non-member functions to give properties of the distribution.
0179 
0180     template <class RealType, class Policy>
0181     BOOST_MATH_GPU_ENABLED inline boost::math::pair<RealType, RealType> range(const poisson_distribution<RealType, Policy>& /* dist */)
0182     { // Range of permissible values for random variable k.
0183        using boost::math::tools::max_value;
0184        return boost::math::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // Max integer?
0185     }
0186 
0187     template <class RealType, class Policy>
0188     BOOST_MATH_GPU_ENABLED inline boost::math::pair<RealType, RealType> support(const poisson_distribution<RealType, Policy>& /* dist */)
0189     { // Range of supported values for random variable k.
0190        // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
0191        using boost::math::tools::max_value;
0192        return boost::math::pair<RealType, RealType>(static_cast<RealType>(0),  max_value<RealType>());
0193     }
0194 
0195     template <class RealType, class Policy>
0196     BOOST_MATH_GPU_ENABLED inline RealType mean(const poisson_distribution<RealType, Policy>& dist)
0197     { // Mean of poisson distribution = lambda.
0198       return dist.mean();
0199     } // mean
0200 
0201     template <class RealType, class Policy>
0202     BOOST_MATH_GPU_ENABLED inline RealType mode(const poisson_distribution<RealType, Policy>& dist)
0203     { // mode.
0204       BOOST_MATH_STD_USING // ADL of std functions.
0205       return floor(dist.mean());
0206     }
0207 
0208     // Median now implemented via quantile(half) in derived accessors.
0209 
0210     template <class RealType, class Policy>
0211     BOOST_MATH_GPU_ENABLED inline RealType variance(const poisson_distribution<RealType, Policy>& dist)
0212     { // variance.
0213       return dist.mean();
0214     }
0215 
0216     // standard_deviation provided by derived accessors.
0217 
0218     template <class RealType, class Policy>
0219     BOOST_MATH_GPU_ENABLED inline RealType skewness(const poisson_distribution<RealType, Policy>& dist)
0220     { // skewness = sqrt(l).
0221       BOOST_MATH_STD_USING // ADL of std functions.
0222       return 1 / sqrt(dist.mean());
0223     }
0224 
0225     template <class RealType, class Policy>
0226     BOOST_MATH_GPU_ENABLED inline RealType kurtosis_excess(const poisson_distribution<RealType, Policy>& dist)
0227     { // skewness = sqrt(l).
0228       return 1 / dist.mean(); // kurtosis_excess 1/mean from Wiki & MathWorld eq 31.
0229       // http://mathworld.wolfram.com/Kurtosis.html explains that the kurtosis excess
0230       // is more convenient because the kurtosis excess of a normal distribution is zero
0231       // whereas the true kurtosis is 3.
0232     } // RealType kurtosis_excess
0233 
0234     template <class RealType, class Policy>
0235     BOOST_MATH_GPU_ENABLED inline RealType kurtosis(const poisson_distribution<RealType, Policy>& dist)
0236     { // kurtosis is 4th moment about the mean = u4 / sd ^ 4
0237       // http://en.wikipedia.org/wiki/Kurtosis
0238       // kurtosis can range from -2 (flat top) to +infinity (sharp peak & heavy tails).
0239       // http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
0240       return 3 + 1 / dist.mean(); // NIST.
0241       // http://mathworld.wolfram.com/Kurtosis.html explains that the kurtosis excess
0242       // is more convenient because the kurtosis excess of a normal distribution is zero
0243       // whereas the true kurtosis is 3.
0244     } // RealType kurtosis
0245 
0246     template <class RealType, class Policy>
0247     BOOST_MATH_GPU_ENABLED RealType pdf(const poisson_distribution<RealType, Policy>& dist, const RealType& k)
0248     { // Probability Density/Mass Function.
0249       // Probability that there are EXACTLY k occurrences (or arrivals).
0250       BOOST_FPU_EXCEPTION_GUARD
0251 
0252       BOOST_MATH_STD_USING // for ADL of std functions.
0253 
0254       RealType mean = dist.mean();
0255       // Error check:
0256       RealType result = 0;
0257       if(false == poisson_detail::check_dist_and_k(
0258         "boost::math::pdf(const poisson_distribution<%1%>&, %1%)",
0259         mean,
0260         k,
0261         &result, Policy()))
0262       {
0263         return result;
0264       }
0265 
0266       // Special case of mean zero, regardless of the number of events k.
0267       if (mean == 0)
0268       { // Probability for any k is zero.
0269         return 0;
0270       }
0271       if (k == 0)
0272       { // mean ^ k = 1, and k! = 1, so can simplify.
0273         return exp(-mean);
0274       }
0275       return boost::math::gamma_p_derivative(k+1, mean, Policy());
0276     } // pdf
0277 
0278     template <class RealType, class Policy>
0279     BOOST_MATH_GPU_ENABLED RealType logpdf(const poisson_distribution<RealType, Policy>& dist, const RealType& k)
0280     {
0281       BOOST_FPU_EXCEPTION_GUARD
0282 
0283       BOOST_MATH_STD_USING // for ADL of std functions.
0284       using boost::math::lgamma;
0285 
0286       RealType mean = dist.mean();
0287       // Error check:
0288       RealType result = -boost::math::numeric_limits<RealType>::infinity();
0289       if(false == poisson_detail::check_dist_and_k(
0290         "boost::math::pdf(const poisson_distribution<%1%>&, %1%)",
0291         mean,
0292         k,
0293         &result, Policy()))
0294       {
0295         return result;
0296       }
0297 
0298       // Special case of mean zero, regardless of the number of events k.
0299       if (mean == 0)
0300       { // Probability for any k is zero.
0301         return boost::math::numeric_limits<RealType>::quiet_NaN();
0302       }
0303       
0304       // Special case where k and lambda are both positive
0305       if(k > 0 && mean > 0)
0306       {
0307         return -lgamma(k+1) + k*log(mean) - mean;
0308       }
0309 
0310       result = log(pdf(dist, k));
0311       return result;
0312     }
0313 
0314     template <class RealType, class Policy>
0315     BOOST_MATH_GPU_ENABLED RealType cdf(const poisson_distribution<RealType, Policy>& dist, const RealType& k)
0316     { // Cumulative Distribution Function Poisson.
0317       // The random variate k is the number of occurrences(or arrivals)
0318       // k argument may be integral, signed, or unsigned, or floating point.
0319       // If necessary, it has already been promoted from an integral type.
0320       // Returns the sum of the terms 0 through k of the Poisson Probability Density or Mass (pdf).
0321 
0322       // But note that the Poisson distribution
0323       // (like others including the binomial, negative binomial & Bernoulli)
0324       // is strictly defined as a discrete function: only integral values of k are envisaged.
0325       // However because of the method of calculation using a continuous gamma function,
0326       // it is convenient to treat it as if it is a continuous function
0327       // and permit non-integral values of k.
0328       // To enforce the strict mathematical model, users should use floor or ceil functions
0329       // outside this function to ensure that k is integral.
0330 
0331       // The terms are not summed directly (at least for larger k)
0332       // instead the incomplete gamma integral is employed,
0333 
0334       BOOST_MATH_STD_USING // for ADL of std function exp.
0335 
0336       RealType mean = dist.mean();
0337       // Error checks:
0338       RealType result = 0;
0339       if(false == poisson_detail::check_dist_and_k(
0340         "boost::math::cdf(const poisson_distribution<%1%>&, %1%)",
0341         mean,
0342         k,
0343         &result, Policy()))
0344       {
0345         return result;
0346       }
0347       // Special cases:
0348       if (mean == 0)
0349       { // Probability for any k is zero.
0350         return 0;
0351       }
0352       if (k == 0)
0353       {
0354         // mean (and k) have already been checked,
0355         // so this avoids unnecessary repeated checks.
0356        return exp(-mean);
0357       }
0358       // For small integral k could use a finite sum -
0359       // it's cheaper than the gamma function.
0360       // BUT this is now done efficiently by gamma_q function.
0361       // Calculate poisson cdf using the gamma_q function.
0362       return gamma_q(k+1, mean, Policy());
0363     } // binomial cdf
0364 
0365     template <class RealType, class Policy>
0366     BOOST_MATH_GPU_ENABLED RealType cdf(const complemented2_type<poisson_distribution<RealType, Policy>, RealType>& c)
0367     { // Complemented Cumulative Distribution Function Poisson
0368       // The random variate k is the number of events, occurrences or arrivals.
0369       // k argument may be integral, signed, or unsigned, or floating point.
0370       // If necessary, it has already been promoted from an integral type.
0371       // But note that the Poisson distribution
0372       // (like others including the binomial, negative binomial & Bernoulli)
0373       // is strictly defined as a discrete function: only integral values of k are envisaged.
0374       // However because of the method of calculation using a continuous gamma function,
0375       // it is convenient to treat it as is it is a continuous function
0376       // and permit non-integral values of k.
0377       // To enforce the strict mathematical model, users should use floor or ceil functions
0378       // outside this function to ensure that k is integral.
0379 
0380       // Returns the sum of the terms k+1 through inf of the Poisson Probability Density/Mass (pdf).
0381       // The terms are not summed directly (at least for larger k)
0382       // instead the incomplete gamma integral is employed,
0383 
0384       RealType const& k = c.param;
0385       poisson_distribution<RealType, Policy> const& dist = c.dist;
0386 
0387       RealType mean = dist.mean();
0388 
0389       // Error checks:
0390       RealType result = 0;
0391       if(false == poisson_detail::check_dist_and_k(
0392         "boost::math::cdf(const poisson_distribution<%1%>&, %1%)",
0393         mean,
0394         k,
0395         &result, Policy()))
0396       {
0397         return result;
0398       }
0399       // Special case of mean, regardless of the number of events k.
0400       if (mean == 0)
0401       { // Probability for any k is unity, complement of zero.
0402         return 1;
0403       }
0404       if (k == 0)
0405       { // Avoid repeated checks on k and mean in gamma_p.
0406          return -boost::math::expm1(-mean, Policy());
0407       }
0408       // Unlike un-complemented cdf (sum from 0 to k),
0409       // can't use finite sum from k+1 to infinity for small integral k,
0410       // anyway it is now done efficiently by gamma_p.
0411       return gamma_p(k + 1, mean, Policy()); // Calculate Poisson cdf using the gamma_p function.
0412       // CCDF = gamma_p(k+1, lambda)
0413     } // poisson ccdf
0414 
0415     template <class RealType, class Policy>
0416     BOOST_MATH_GPU_ENABLED inline RealType quantile(const poisson_distribution<RealType, Policy>& dist, const RealType& p)
0417     { // Quantile (or Percent Point) Poisson function.
0418       // Return the number of expected events k for a given probability p.
0419       constexpr auto function = "boost::math::quantile(const poisson_distribution<%1%>&, %1%)";
0420       RealType result = 0; // of Argument checks:
0421       if(false == poisson_detail::check_prob(
0422         function,
0423         p,
0424         &result, Policy()))
0425       {
0426         return result;
0427       }
0428       // Special case:
0429       if (dist.mean() == 0)
0430       { // if mean = 0 then p = 0, so k can be anything?
0431          if (false == poisson_detail::check_mean_NZ(
0432          function,
0433          dist.mean(),
0434          &result, Policy()))
0435         {
0436           return result;
0437         }
0438       }
0439       if(p == 0)
0440       {
0441          return 0; // Exact result regardless of discrete-quantile Policy
0442       }
0443       if(p == 1)
0444       {
0445          return policies::raise_overflow_error<RealType>(function, 0, Policy());
0446       }
0447       using discrete_type = typename Policy::discrete_quantile_type;
0448       boost::math::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0449       RealType guess;
0450       RealType factor = 8;
0451       RealType z = dist.mean();
0452       if(z < 1)
0453          guess = z;
0454       else
0455          guess = boost::math::detail::inverse_poisson_cornish_fisher(z, p, RealType(1-p), Policy());
0456       if(z > 5)
0457       {
0458          if(z > 1000)
0459             factor = 1.01f;
0460          else if(z > 50)
0461             factor = 1.1f;
0462          else if(guess > 10)
0463             factor = 1.25f;
0464          else
0465             factor = 2;
0466          if(guess < 1.1)
0467             factor = 8;
0468       }
0469 
0470       return detail::inverse_discrete_quantile(
0471          dist,
0472          p,
0473          false,
0474          guess,
0475          factor,
0476          RealType(1),
0477          discrete_type(),
0478          max_iter);
0479    } // quantile
0480 
0481     template <class RealType, class Policy>
0482     BOOST_MATH_GPU_ENABLED inline RealType quantile(const complemented2_type<poisson_distribution<RealType, Policy>, RealType>& c)
0483     { // Quantile (or Percent Point) of Poisson function.
0484       // Return the number of expected events k for a given
0485       // complement of the probability q.
0486       //
0487       // Error checks:
0488       constexpr auto function = "boost::math::quantile(complement(const poisson_distribution<%1%>&, %1%))";
0489       RealType q = c.param;
0490       const poisson_distribution<RealType, Policy>& dist = c.dist;
0491       RealType result = 0;  // of argument checks.
0492       if(false == poisson_detail::check_prob(
0493         function,
0494         q,
0495         &result, Policy()))
0496       {
0497         return result;
0498       }
0499       // Special case:
0500       if (dist.mean() == 0)
0501       { // if mean = 0 then p = 0, so k can be anything?
0502          if (false == poisson_detail::check_mean_NZ(
0503          function,
0504          dist.mean(),
0505          &result, Policy()))
0506         {
0507           return result;
0508         }
0509       }
0510       if(q == 0)
0511       {
0512          return policies::raise_overflow_error<RealType>(function, 0, Policy());
0513       }
0514       if(q == 1)
0515       {
0516          return 0;  // Exact result regardless of discrete-quantile Policy
0517       }
0518       using discrete_type = typename Policy::discrete_quantile_type;
0519       boost::math::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0520       RealType guess;
0521       RealType factor = 8;
0522       RealType z = dist.mean();
0523       if(z < 1)
0524          guess = z;
0525       else
0526          guess = boost::math::detail::inverse_poisson_cornish_fisher(z, RealType(1-q), q, Policy());
0527       if(z > 5)
0528       {
0529          if(z > 1000)
0530             factor = 1.01f;
0531          else if(z > 50)
0532             factor = 1.1f;
0533          else if(guess > 10)
0534             factor = 1.25f;
0535          else
0536             factor = 2;
0537          if(guess < 1.1)
0538             factor = 8;
0539       }
0540 
0541       return detail::inverse_discrete_quantile(
0542          dist,
0543          q,
0544          true,
0545          guess,
0546          factor,
0547          RealType(1),
0548          discrete_type(),
0549          max_iter);
0550    } // quantile complement.
0551 
0552   } // namespace math
0553 } // namespace boost
0554 
0555 // This include must be at the end, *after* the accessors
0556 // for this distribution have been defined, in order to
0557 // keep compilers that support two-phase lookup happy.
0558 #include <boost/math/distributions/detail/derived_accessors.hpp>
0559 
0560 #endif // BOOST_MATH_SPECIAL_POISSON_HPP
0561 
0562 
0563