Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:39:40

0001 // boost\math\distributions\geometric.hpp
0002 
0003 // Copyright John Maddock 2010.
0004 // Copyright Paul A. Bristow 2010.
0005 
0006 // Use, modification and distribution are subject to the
0007 // Boost Software License, Version 1.0.
0008 // (See accompanying file LICENSE_1_0.txt
0009 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0010 
0011 // geometric distribution is a discrete probability distribution.
0012 // It expresses the probability distribution of the number (k) of
0013 // events, occurrences, failures or arrivals before the first success.
0014 // supported on the set {0, 1, 2, 3...}
0015 
0016 // Note that the set includes zero (unlike some definitions that start at one).
0017 
0018 // The random variate k is the number of events, occurrences or arrivals.
0019 // k argument may be integral, signed, or unsigned, or floating point.
0020 // If necessary, it has already been promoted from an integral type.
0021 
0022 // Note that the geometric distribution
0023 // (like others including the binomial, geometric & Bernoulli)
0024 // is strictly defined as a discrete function:
0025 // only integral values of k are envisaged.
0026 // However because the method of calculation uses a continuous gamma function,
0027 // it is convenient to treat it as if a continuous function,
0028 // and permit non-integral values of k.
0029 // To enforce the strict mathematical model, users should use floor or ceil functions
0030 // on k outside this function to ensure that k is integral.
0031 
0032 // See http://en.wikipedia.org/wiki/geometric_distribution
0033 // http://documents.wolfram.com/v5/Add-onsLinks/StandardPackages/Statistics/DiscreteDistributions.html
0034 // http://mathworld.wolfram.com/GeometricDistribution.html
0035 
0036 #ifndef BOOST_MATH_SPECIAL_GEOMETRIC_HPP
0037 #define BOOST_MATH_SPECIAL_GEOMETRIC_HPP
0038 
0039 #include <boost/math/distributions/fwd.hpp>
0040 #include <boost/math/special_functions/beta.hpp> // for ibeta(a, b, x) == Ix(a, b).
0041 #include <boost/math/distributions/complement.hpp> // complement.
0042 #include <boost/math/distributions/detail/common_error_handling.hpp> // error checks domain_error & logic_error.
0043 #include <boost/math/special_functions/fpclassify.hpp> // isnan.
0044 #include <boost/math/tools/roots.hpp> // for root finding.
0045 #include <boost/math/distributions/detail/inv_discrete_quantile.hpp>
0046 #include <boost/math/special_functions/log1p.hpp>
0047 
0048 #include <limits> // using std::numeric_limits;
0049 #include <utility>
0050 #include <cmath>
0051 
0052 #if defined (BOOST_MSVC)
0053 #  pragma warning(push)
0054 // This believed not now necessary, so commented out.
0055 //#  pragma warning(disable: 4702) // unreachable code.
0056 // in domain_error_imp in error_handling.
0057 #endif
0058 
0059 namespace boost
0060 {
0061   namespace math
0062   {
0063     namespace geometric_detail
0064     {
0065       // Common error checking routines for geometric distribution function:
0066       template <class RealType, class Policy>
0067       inline bool check_success_fraction(const char* function, const RealType& p, RealType* result, const Policy& pol)
0068       {
0069         if( !(boost::math::isfinite)(p) || (p < 0) || (p > 1) )
0070         {
0071           *result = policies::raise_domain_error<RealType>(
0072             function,
0073             "Success fraction argument is %1%, but must be >= 0 and <= 1 !", p, pol);
0074           return false;
0075         }
0076         return true;
0077       }
0078 
0079       template <class RealType, class Policy>
0080       inline bool check_dist(const char* function, const RealType& p, RealType* result, const Policy& pol)
0081       {
0082         return check_success_fraction(function, p, result, pol);
0083       }
0084 
0085       template <class RealType, class Policy>
0086       inline bool check_dist_and_k(const char* function,  const RealType& p, RealType k, RealType* result, const Policy& pol)
0087       {
0088         if(check_dist(function, p, result, pol) == false)
0089         {
0090           return false;
0091         }
0092         if( !(boost::math::isfinite)(k) || (k < 0) )
0093         { // Check k failures.
0094           *result = policies::raise_domain_error<RealType>(
0095             function,
0096             "Number of failures argument is %1%, but must be >= 0 !", k, pol);
0097           return false;
0098         }
0099         return true;
0100       } // Check_dist_and_k
0101 
0102       template <class RealType, class Policy>
0103       inline bool check_dist_and_prob(const char* function, RealType p, RealType prob, RealType* result, const Policy& pol)
0104       {
0105         if((check_dist(function, p, result, pol) && detail::check_probability(function, prob, result, pol)) == false)
0106         {
0107           return false;
0108         }
0109         return true;
0110       } // check_dist_and_prob
0111     } //  namespace geometric_detail
0112 
0113     template <class RealType = double, class Policy = policies::policy<> >
0114     class geometric_distribution
0115     {
0116     public:
0117       typedef RealType value_type;
0118       typedef Policy policy_type;
0119 
0120       geometric_distribution(RealType p) : m_p(p)
0121       { // Constructor stores success_fraction p.
0122         RealType result;
0123         geometric_detail::check_dist(
0124           "geometric_distribution<%1%>::geometric_distribution",
0125           m_p, // Check success_fraction 0 <= p <= 1.
0126           &result, Policy());
0127       } // geometric_distribution constructor.
0128 
0129       // Private data getter class member functions.
0130       RealType success_fraction() const
0131       { // Probability of success as fraction in range 0 to 1.
0132         return m_p;
0133       }
0134       RealType successes() const
0135       { // Total number of successes r = 1 (for compatibility with negative binomial?).
0136         return 1;
0137       }
0138 
0139       // Parameter estimation.
0140       // (These are copies of negative_binomial distribution with successes = 1).
0141       static RealType find_lower_bound_on_p(
0142         RealType trials,
0143         RealType alpha) // alpha 0.05 equivalent to 95% for one-sided test.
0144       {
0145         static const char* function = "boost::math::geometric<%1%>::find_lower_bound_on_p";
0146         RealType result = 0;  // of error checks.
0147         RealType successes = 1;
0148         RealType failures = trials - successes;
0149         if(false == detail::check_probability(function, alpha, &result, Policy())
0150           && geometric_detail::check_dist_and_k(
0151           function, RealType(0), failures, &result, Policy()))
0152         {
0153           return result;
0154         }
0155         // Use complement ibeta_inv function for lower bound.
0156         // This is adapted from the corresponding binomial formula
0157         // here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc241.htm
0158         // This is a Clopper-Pearson interval, and may be overly conservative,
0159         // see also "A Simple Improved Inferential Method for Some
0160         // Discrete Distributions" Yong CAI and K. KRISHNAMOORTHY
0161         // http://www.ucs.louisiana.edu/~kxk4695/Discrete_new.pdf
0162         //
0163         return ibeta_inv(successes, failures + 1, alpha, static_cast<RealType*>(nullptr), Policy());
0164       } // find_lower_bound_on_p
0165 
0166       static RealType find_upper_bound_on_p(
0167         RealType trials,
0168         RealType alpha) // alpha 0.05 equivalent to 95% for one-sided test.
0169       {
0170         static const char* function = "boost::math::geometric<%1%>::find_upper_bound_on_p";
0171         RealType result = 0;  // of error checks.
0172         RealType successes = 1;
0173         RealType failures = trials - successes;
0174         if(false == geometric_detail::check_dist_and_k(
0175           function, RealType(0), failures, &result, Policy())
0176           && detail::check_probability(function, alpha, &result, Policy()))
0177         {
0178           return result;
0179         }
0180         if(failures == 0)
0181         {
0182            return 1;
0183         }// Use complement ibetac_inv function for upper bound.
0184         // Note adjusted failures value: *not* failures+1 as usual.
0185         // This is adapted from the corresponding binomial formula
0186         // here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc241.htm
0187         // This is a Clopper-Pearson interval, and may be overly conservative,
0188         // see also "A Simple Improved Inferential Method for Some
0189         // Discrete Distributions" Yong CAI and K. Krishnamoorthy
0190         // http://www.ucs.louisiana.edu/~kxk4695/Discrete_new.pdf
0191         //
0192         return ibetac_inv(successes, failures, alpha, static_cast<RealType*>(nullptr), Policy());
0193       } // find_upper_bound_on_p
0194 
0195       // Estimate number of trials :
0196       // "How many trials do I need to be P% sure of seeing k or fewer failures?"
0197 
0198       static RealType find_minimum_number_of_trials(
0199         RealType k,     // number of failures (k >= 0).
0200         RealType p,     // success fraction 0 <= p <= 1.
0201         RealType alpha) // risk level threshold 0 <= alpha <= 1.
0202       {
0203         static const char* function = "boost::math::geometric<%1%>::find_minimum_number_of_trials";
0204         // Error checks:
0205         RealType result = 0;
0206         if(false == geometric_detail::check_dist_and_k(
0207           function, p, k, &result, Policy())
0208           && detail::check_probability(function, alpha, &result, Policy()))
0209         {
0210           return result;
0211         }
0212         result = ibeta_inva(k + 1, p, alpha, Policy());  // returns n - k
0213         return result + k;
0214       } // RealType find_number_of_failures
0215 
0216       static RealType find_maximum_number_of_trials(
0217         RealType k,     // number of failures (k >= 0).
0218         RealType p,     // success fraction 0 <= p <= 1.
0219         RealType alpha) // risk level threshold 0 <= alpha <= 1.
0220       {
0221         static const char* function = "boost::math::geometric<%1%>::find_maximum_number_of_trials";
0222         // Error checks:
0223         RealType result = 0;
0224         if(false == geometric_detail::check_dist_and_k(
0225           function, p, k, &result, Policy())
0226           &&  detail::check_probability(function, alpha, &result, Policy()))
0227         {
0228           return result;
0229         }
0230         result = ibetac_inva(k + 1, p, alpha, Policy());  // returns n - k
0231         return result + k;
0232       } // RealType find_number_of_trials complemented
0233 
0234     private:
0235       //RealType m_r; // successes fixed at unity.
0236       RealType m_p; // success_fraction
0237     }; // template <class RealType, class Policy> class geometric_distribution
0238 
0239     typedef geometric_distribution<double> geometric; // Reserved name of type double.
0240 
0241     #ifdef __cpp_deduction_guides
0242     template <class RealType>
0243     geometric_distribution(RealType)->geometric_distribution<typename boost::math::tools::promote_args<RealType>::type>;
0244     #endif
0245 
0246     template <class RealType, class Policy>
0247     inline const std::pair<RealType, RealType> range(const geometric_distribution<RealType, Policy>& /* dist */)
0248     { // Range of permissible values for random variable k.
0249        using boost::math::tools::max_value;
0250        return std::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // max_integer?
0251     }
0252 
0253     template <class RealType, class Policy>
0254     inline const std::pair<RealType, RealType> support(const geometric_distribution<RealType, Policy>& /* dist */)
0255     { // Range of supported values for random variable k.
0256        // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
0257        using boost::math::tools::max_value;
0258        return std::pair<RealType, RealType>(static_cast<RealType>(0),  max_value<RealType>()); // max_integer?
0259     }
0260 
0261     template <class RealType, class Policy>
0262     inline RealType mean(const geometric_distribution<RealType, Policy>& dist)
0263     { // Mean of geometric distribution = (1-p)/p.
0264       return (1 - dist.success_fraction() ) / dist.success_fraction();
0265     } // mean
0266 
0267     // median implemented via quantile(half) in derived accessors.
0268 
0269     template <class RealType, class Policy>
0270     inline RealType mode(const geometric_distribution<RealType, Policy>&)
0271     { // Mode of geometric distribution = zero.
0272       BOOST_MATH_STD_USING // ADL of std functions.
0273       return 0;
0274     } // mode
0275 
0276     template <class RealType, class Policy>
0277     inline RealType variance(const geometric_distribution<RealType, Policy>& dist)
0278     { // Variance of Binomial distribution = (1-p) / p^2.
0279       return  (1 - dist.success_fraction())
0280         / (dist.success_fraction() * dist.success_fraction());
0281     } // variance
0282 
0283     template <class RealType, class Policy>
0284     inline RealType skewness(const geometric_distribution<RealType, Policy>& dist)
0285     { // skewness of geometric distribution = 2-p / (sqrt(r(1-p))
0286       BOOST_MATH_STD_USING // ADL of std functions.
0287       RealType p = dist.success_fraction();
0288       return (2 - p) / sqrt(1 - p);
0289     } // skewness
0290 
0291     template <class RealType, class Policy>
0292     inline RealType kurtosis(const geometric_distribution<RealType, Policy>& dist)
0293     { // kurtosis of geometric distribution
0294       // http://en.wikipedia.org/wiki/geometric is kurtosis_excess so add 3
0295       RealType p = dist.success_fraction();
0296       return 3 + (p*p - 6*p + 6) / (1 - p);
0297     } // kurtosis
0298 
0299      template <class RealType, class Policy>
0300     inline RealType kurtosis_excess(const geometric_distribution<RealType, Policy>& dist)
0301     { // kurtosis excess of geometric distribution
0302       // http://mathworld.wolfram.com/Kurtosis.html table of kurtosis_excess
0303       RealType p = dist.success_fraction();
0304       return (p*p - 6*p + 6) / (1 - p);
0305     } // kurtosis_excess
0306 
0307     // RealType standard_deviation(const geometric_distribution<RealType, Policy>& dist)
0308     // standard_deviation provided by derived accessors.
0309     // RealType hazard(const geometric_distribution<RealType, Policy>& dist)
0310     // hazard of geometric distribution provided by derived accessors.
0311     // RealType chf(const geometric_distribution<RealType, Policy>& dist)
0312     // chf of geometric distribution provided by derived accessors.
0313 
0314     template <class RealType, class Policy>
0315     inline RealType pdf(const geometric_distribution<RealType, Policy>& dist, const RealType& k)
0316     { // Probability Density/Mass Function.
0317       BOOST_FPU_EXCEPTION_GUARD
0318       BOOST_MATH_STD_USING  // For ADL of math functions.
0319       static const char* function = "boost::math::pdf(const geometric_distribution<%1%>&, %1%)";
0320 
0321       RealType p = dist.success_fraction();
0322       RealType result = 0;
0323       if(false == geometric_detail::check_dist_and_k(
0324         function,
0325         p,
0326         k,
0327         &result, Policy()))
0328       {
0329         return result;
0330       }
0331       if (k == 0)
0332       {
0333         return p; // success_fraction
0334       }
0335       RealType q = 1 - p;  // Inaccurate for small p?
0336       // So try to avoid inaccuracy for large or small p.
0337       // but has little effect > last significant bit.
0338       //cout << "p *  pow(q, k) " << result << endl; // seems best whatever p
0339       //cout << "exp(p * k * log1p(-p)) " << p * exp(k * log1p(-p)) << endl;
0340       //if (p < 0.5)
0341       //{
0342       //  result = p *  pow(q, k);
0343       //}
0344       //else
0345       //{
0346       //  result = p * exp(k * log1p(-p));
0347       //}
0348       result = p * pow(q, k);
0349       return result;
0350     } // geometric_pdf
0351 
0352     template <class RealType, class Policy>
0353     inline RealType cdf(const geometric_distribution<RealType, Policy>& dist, const RealType& k)
0354     { // Cumulative Distribution Function of geometric.
0355       static const char* function = "boost::math::cdf(const geometric_distribution<%1%>&, %1%)";
0356 
0357       // k argument may be integral, signed, or unsigned, or floating point.
0358       // If necessary, it has already been promoted from an integral type.
0359       RealType p = dist.success_fraction();
0360       // Error check:
0361       RealType result = 0;
0362       if(false == geometric_detail::check_dist_and_k(
0363         function,
0364         p,
0365         k,
0366         &result, Policy()))
0367       {
0368         return result;
0369       }
0370       if(k == 0)
0371       {
0372         return p; // success_fraction
0373       }
0374       //RealType q = 1 - p;  // Bad for small p
0375       //RealType probability = 1 - std::pow(q, k+1);
0376 
0377       RealType z = boost::math::log1p(-p, Policy()) * (k + 1);
0378       RealType probability = -boost::math::expm1(z, Policy());
0379 
0380       return probability;
0381     } // cdf Cumulative Distribution Function geometric.
0382 
0383     template <class RealType, class Policy>
0384     inline RealType logcdf(const geometric_distribution<RealType, Policy>& dist, const RealType& k)
0385     { // Cumulative Distribution Function of geometric.
0386       using std::pow;
0387       static const char* function = "boost::math::logcdf(const geometric_distribution<%1%>&, %1%)";
0388 
0389       // k argument may be integral, signed, or unsigned, or floating point.
0390       // If necessary, it has already been promoted from an integral type.
0391       RealType p = dist.success_fraction();
0392       // Error check:
0393       RealType result = 0;
0394       if(false == geometric_detail::check_dist_and_k(
0395         function,
0396         p,
0397         k,
0398         &result, Policy()))
0399       {
0400         return -std::numeric_limits<RealType>::infinity();
0401       }
0402       if(k == 0)
0403       {
0404         return log(p); // success_fraction
0405       }
0406       //RealType q = 1 - p;  // Bad for small p
0407       //RealType probability = 1 - std::pow(q, k+1);
0408 
0409       RealType z = boost::math::log1p(-p, Policy()) * (k + 1);
0410       return log1p(-exp(z), Policy());
0411     } // logcdf Cumulative Distribution Function geometric.
0412 
0413     template <class RealType, class Policy>
0414     inline RealType cdf(const complemented2_type<geometric_distribution<RealType, Policy>, RealType>& c)
0415     { // Complemented Cumulative Distribution Function geometric.
0416       BOOST_MATH_STD_USING
0417       static const char* function = "boost::math::cdf(const geometric_distribution<%1%>&, %1%)";
0418       // k argument may be integral, signed, or unsigned, or floating point.
0419       // If necessary, it has already been promoted from an integral type.
0420       RealType const& k = c.param;
0421       geometric_distribution<RealType, Policy> const& dist = c.dist;
0422       RealType p = dist.success_fraction();
0423       // Error check:
0424       RealType result = 0;
0425       if(false == geometric_detail::check_dist_and_k(
0426         function,
0427         p,
0428         k,
0429         &result, Policy()))
0430       {
0431         return result;
0432       }
0433       RealType z = boost::math::log1p(-p, Policy()) * (k+1);
0434       RealType probability = exp(z);
0435       return probability;
0436     } // cdf Complemented Cumulative Distribution Function geometric.
0437 
0438     template <class RealType, class Policy>
0439     inline RealType logcdf(const complemented2_type<geometric_distribution<RealType, Policy>, RealType>& c)
0440     { // Complemented Cumulative Distribution Function geometric.
0441       BOOST_MATH_STD_USING
0442       static const char* function = "boost::math::logcdf(const geometric_distribution<%1%>&, %1%)";
0443       // k argument may be integral, signed, or unsigned, or floating point.
0444       // If necessary, it has already been promoted from an integral type.
0445       RealType const& k = c.param;
0446       geometric_distribution<RealType, Policy> const& dist = c.dist;
0447       RealType p = dist.success_fraction();
0448       // Error check:
0449       RealType result = 0;
0450       if(false == geometric_detail::check_dist_and_k(
0451         function,
0452         p,
0453         k,
0454         &result, Policy()))
0455       {
0456         return -std::numeric_limits<RealType>::infinity();
0457       }
0458 
0459       return boost::math::log1p(-p, Policy()) * (k+1);
0460     } // logcdf Complemented Cumulative Distribution Function geometric.
0461 
0462     template <class RealType, class Policy>
0463     inline RealType quantile(const geometric_distribution<RealType, Policy>& dist, const RealType& x)
0464     { // Quantile, percentile/100 or Percent Point geometric function.
0465       // Return the number of expected failures k for a given probability p.
0466 
0467       // Inverse cumulative Distribution Function or Quantile (percentile / 100) of geometric Probability.
0468       // k argument may be integral, signed, or unsigned, or floating point.
0469 
0470       static const char* function = "boost::math::quantile(const geometric_distribution<%1%>&, %1%)";
0471       BOOST_MATH_STD_USING // ADL of std functions.
0472 
0473       RealType success_fraction = dist.success_fraction();
0474       // Check dist and x.
0475       RealType result = 0;
0476       if(false == geometric_detail::check_dist_and_prob
0477         (function, success_fraction, x, &result, Policy()))
0478       {
0479         return result;
0480       }
0481 
0482       // Special cases.
0483       if (x == 1)
0484       {  // Would need +infinity failures for total confidence.
0485         result = policies::raise_overflow_error<RealType>(
0486             function,
0487             "Probability argument is 1, which implies infinite failures !", Policy());
0488         return result;
0489        // usually means return +std::numeric_limits<RealType>::infinity();
0490        // unless #define BOOST_MATH_THROW_ON_OVERFLOW_ERROR
0491       }
0492       if (x == 0)
0493       { // No failures are expected if P = 0.
0494         return 0; // Total trials will be just dist.successes.
0495       }
0496       // if (P <= pow(dist.success_fraction(), 1))
0497       if (x <= success_fraction)
0498       { // p <= pdf(dist, 0) == cdf(dist, 0)
0499         return 0;
0500       }
0501       if (x == 1)
0502       {
0503         return 0;
0504       }
0505 
0506       // log(1-x) /log(1-success_fraction) -1; but use log1p in case success_fraction is small
0507       result = boost::math::log1p(-x, Policy()) / boost::math::log1p(-success_fraction, Policy()) - 1;
0508       // Subtract a few epsilons here too?
0509       // to make sure it doesn't slip over, so ceil would be one too many.
0510       return result;
0511     } // RealType quantile(const geometric_distribution dist, p)
0512 
0513     template <class RealType, class Policy>
0514     inline RealType quantile(const complemented2_type<geometric_distribution<RealType, Policy>, RealType>& c)
0515     {  // Quantile or Percent Point Binomial function.
0516        // Return the number of expected failures k for a given
0517        // complement of the probability Q = 1 - P.
0518        static const char* function = "boost::math::quantile(const geometric_distribution<%1%>&, %1%)";
0519        BOOST_MATH_STD_USING
0520        // Error checks:
0521        RealType x = c.param;
0522        const geometric_distribution<RealType, Policy>& dist = c.dist;
0523        RealType success_fraction = dist.success_fraction();
0524        RealType result = 0;
0525        if(false == geometric_detail::check_dist_and_prob(
0526           function,
0527           success_fraction,
0528           x,
0529           &result, Policy()))
0530        {
0531           return result;
0532        }
0533 
0534        // Special cases:
0535        if(x == 1)
0536        {  // There may actually be no answer to this question,
0537           // since the probability of zero failures may be non-zero,
0538           return 0; // but zero is the best we can do:
0539        }
0540        if (-x <= boost::math::powm1(dist.success_fraction(), dist.successes(), Policy()))
0541        {  // q <= cdf(complement(dist, 0)) == pdf(dist, 0)
0542           return 0; //
0543        }
0544        if(x == 0)
0545        {  // Probability 1 - Q  == 1 so infinite failures to achieve certainty.
0546           // Would need +infinity failures for total confidence.
0547           result = policies::raise_overflow_error<RealType>(
0548              function,
0549              "Probability argument complement is 0, which implies infinite failures !", Policy());
0550           return result;
0551           // usually means return +std::numeric_limits<RealType>::infinity();
0552           // unless #define BOOST_MATH_THROW_ON_OVERFLOW_ERROR
0553        }
0554        // log(x) /log(1-success_fraction) -1; but use log1p in case success_fraction is small
0555        result = log(x) / boost::math::log1p(-success_fraction, Policy()) - 1;
0556       return result;
0557 
0558     } // quantile complement
0559 
0560  } // namespace math
0561 } // namespace boost
0562 
0563 // This include must be at the end, *after* the accessors
0564 // for this distribution have been defined, in order to
0565 // keep compilers that support two-phase lookup happy.
0566 #include <boost/math/distributions/detail/derived_accessors.hpp>
0567 
0568 #if defined (BOOST_MSVC)
0569 # pragma warning(pop)
0570 #endif
0571 
0572 #endif // BOOST_MATH_SPECIAL_GEOMETRIC_HPP