cannam@160: // boost\math\distributions\geometric.hpp cannam@160: cannam@160: // Copyright John Maddock 2010. cannam@160: // Copyright Paul A. Bristow 2010. cannam@160: cannam@160: // Use, modification and distribution are subject to the cannam@160: // Boost Software License, Version 1.0. cannam@160: // (See accompanying file LICENSE_1_0.txt cannam@160: // or copy at http://www.boost.org/LICENSE_1_0.txt) cannam@160: cannam@160: // geometric distribution is a discrete probability distribution. cannam@160: // It expresses the probability distribution of the number (k) of cannam@160: // events, occurrences, failures or arrivals before the first success. cannam@160: // supported on the set {0, 1, 2, 3...} cannam@160: cannam@160: // Note that the set includes zero (unlike some definitions that start at one). cannam@160: cannam@160: // The random variate k is the number of events, occurrences or arrivals. cannam@160: // k argument may be integral, signed, or unsigned, or floating point. cannam@160: // If necessary, it has already been promoted from an integral type. cannam@160: cannam@160: // Note that the geometric distribution cannam@160: // (like others including the binomial, geometric & Bernoulli) cannam@160: // is strictly defined as a discrete function: cannam@160: // only integral values of k are envisaged. cannam@160: // However because the method of calculation uses a continuous gamma function, cannam@160: // it is convenient to treat it as if a continous function, cannam@160: // and permit non-integral values of k. cannam@160: // To enforce the strict mathematical model, users should use floor or ceil functions cannam@160: // on k outside this function to ensure that k is integral. cannam@160: cannam@160: // See http://en.wikipedia.org/wiki/geometric_distribution cannam@160: // http://documents.wolfram.com/v5/Add-onsLinks/StandardPackages/Statistics/DiscreteDistributions.html cannam@160: // http://mathworld.wolfram.com/GeometricDistribution.html cannam@160: cannam@160: #ifndef BOOST_MATH_SPECIAL_GEOMETRIC_HPP cannam@160: #define BOOST_MATH_SPECIAL_GEOMETRIC_HPP cannam@160: cannam@160: #include cannam@160: #include // for ibeta(a, b, x) == Ix(a, b). cannam@160: #include // complement. cannam@160: #include // error checks domain_error & logic_error. cannam@160: #include // isnan. cannam@160: #include // for root finding. cannam@160: #include cannam@160: cannam@160: #include cannam@160: #include cannam@160: #include cannam@160: #include cannam@160: cannam@160: #include // using std::numeric_limits; cannam@160: #include cannam@160: cannam@160: #if defined (BOOST_MSVC) cannam@160: # pragma warning(push) cannam@160: // This believed not now necessary, so commented out. cannam@160: //# pragma warning(disable: 4702) // unreachable code. cannam@160: // in domain_error_imp in error_handling. cannam@160: #endif cannam@160: cannam@160: namespace boost cannam@160: { cannam@160: namespace math cannam@160: { cannam@160: namespace geometric_detail cannam@160: { cannam@160: // Common error checking routines for geometric distribution function: cannam@160: template cannam@160: inline bool check_success_fraction(const char* function, const RealType& p, RealType* result, const Policy& pol) cannam@160: { cannam@160: if( !(boost::math::isfinite)(p) || (p < 0) || (p > 1) ) cannam@160: { cannam@160: *result = policies::raise_domain_error( cannam@160: function, cannam@160: "Success fraction argument is %1%, but must be >= 0 and <= 1 !", p, pol); cannam@160: return false; cannam@160: } cannam@160: return true; cannam@160: } cannam@160: cannam@160: template cannam@160: inline bool check_dist(const char* function, const RealType& p, RealType* result, const Policy& pol) cannam@160: { cannam@160: return check_success_fraction(function, p, result, pol); cannam@160: } cannam@160: cannam@160: template cannam@160: inline bool check_dist_and_k(const char* function, const RealType& p, RealType k, RealType* result, const Policy& pol) cannam@160: { cannam@160: if(check_dist(function, p, result, pol) == false) cannam@160: { cannam@160: return false; cannam@160: } cannam@160: if( !(boost::math::isfinite)(k) || (k < 0) ) cannam@160: { // Check k failures. cannam@160: *result = policies::raise_domain_error( cannam@160: function, cannam@160: "Number of failures argument is %1%, but must be >= 0 !", k, pol); cannam@160: return false; cannam@160: } cannam@160: return true; cannam@160: } // Check_dist_and_k cannam@160: cannam@160: template cannam@160: inline bool check_dist_and_prob(const char* function, RealType p, RealType prob, RealType* result, const Policy& pol) cannam@160: { cannam@160: if((check_dist(function, p, result, pol) && detail::check_probability(function, prob, result, pol)) == false) cannam@160: { cannam@160: return false; cannam@160: } cannam@160: return true; cannam@160: } // check_dist_and_prob cannam@160: } // namespace geometric_detail cannam@160: cannam@160: template > cannam@160: class geometric_distribution cannam@160: { cannam@160: public: cannam@160: typedef RealType value_type; cannam@160: typedef Policy policy_type; cannam@160: cannam@160: geometric_distribution(RealType p) : m_p(p) cannam@160: { // Constructor stores success_fraction p. cannam@160: RealType result; cannam@160: geometric_detail::check_dist( cannam@160: "geometric_distribution<%1%>::geometric_distribution", cannam@160: m_p, // Check success_fraction 0 <= p <= 1. cannam@160: &result, Policy()); cannam@160: } // geometric_distribution constructor. cannam@160: cannam@160: // Private data getter class member functions. cannam@160: RealType success_fraction() const cannam@160: { // Probability of success as fraction in range 0 to 1. cannam@160: return m_p; cannam@160: } cannam@160: RealType successes() const cannam@160: { // Total number of successes r = 1 (for compatibility with negative binomial?). cannam@160: return 1; cannam@160: } cannam@160: cannam@160: // Parameter estimation. cannam@160: // (These are copies of negative_binomial distribution with successes = 1). cannam@160: static RealType find_lower_bound_on_p( cannam@160: RealType trials, cannam@160: RealType alpha) // alpha 0.05 equivalent to 95% for one-sided test. cannam@160: { cannam@160: static const char* function = "boost::math::geometric<%1%>::find_lower_bound_on_p"; cannam@160: RealType result = 0; // of error checks. cannam@160: RealType successes = 1; cannam@160: RealType failures = trials - successes; cannam@160: if(false == detail::check_probability(function, alpha, &result, Policy()) cannam@160: && geometric_detail::check_dist_and_k( cannam@160: function, RealType(0), failures, &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: // Use complement ibeta_inv function for lower bound. cannam@160: // This is adapted from the corresponding binomial formula cannam@160: // here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc241.htm cannam@160: // This is a Clopper-Pearson interval, and may be overly conservative, cannam@160: // see also "A Simple Improved Inferential Method for Some cannam@160: // Discrete Distributions" Yong CAI and K. KRISHNAMOORTHY cannam@160: // http://www.ucs.louisiana.edu/~kxk4695/Discrete_new.pdf cannam@160: // cannam@160: return ibeta_inv(successes, failures + 1, alpha, static_cast(0), Policy()); cannam@160: } // find_lower_bound_on_p cannam@160: cannam@160: static RealType find_upper_bound_on_p( cannam@160: RealType trials, cannam@160: RealType alpha) // alpha 0.05 equivalent to 95% for one-sided test. cannam@160: { cannam@160: static const char* function = "boost::math::geometric<%1%>::find_upper_bound_on_p"; cannam@160: RealType result = 0; // of error checks. cannam@160: RealType successes = 1; cannam@160: RealType failures = trials - successes; cannam@160: if(false == geometric_detail::check_dist_and_k( cannam@160: function, RealType(0), failures, &result, Policy()) cannam@160: && detail::check_probability(function, alpha, &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: if(failures == 0) cannam@160: { cannam@160: return 1; cannam@160: }// Use complement ibetac_inv function for upper bound. cannam@160: // Note adjusted failures value: *not* failures+1 as usual. cannam@160: // This is adapted from the corresponding binomial formula cannam@160: // here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc241.htm cannam@160: // This is a Clopper-Pearson interval, and may be overly conservative, cannam@160: // see also "A Simple Improved Inferential Method for Some cannam@160: // Discrete Distributions" Yong CAI and K. Krishnamoorthy cannam@160: // http://www.ucs.louisiana.edu/~kxk4695/Discrete_new.pdf cannam@160: // cannam@160: return ibetac_inv(successes, failures, alpha, static_cast(0), Policy()); cannam@160: } // find_upper_bound_on_p cannam@160: cannam@160: // Estimate number of trials : cannam@160: // "How many trials do I need to be P% sure of seeing k or fewer failures?" cannam@160: cannam@160: static RealType find_minimum_number_of_trials( cannam@160: RealType k, // number of failures (k >= 0). cannam@160: RealType p, // success fraction 0 <= p <= 1. cannam@160: RealType alpha) // risk level threshold 0 <= alpha <= 1. cannam@160: { cannam@160: static const char* function = "boost::math::geometric<%1%>::find_minimum_number_of_trials"; cannam@160: // Error checks: cannam@160: RealType result = 0; cannam@160: if(false == geometric_detail::check_dist_and_k( cannam@160: function, p, k, &result, Policy()) cannam@160: && detail::check_probability(function, alpha, &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: result = ibeta_inva(k + 1, p, alpha, Policy()); // returns n - k cannam@160: return result + k; cannam@160: } // RealType find_number_of_failures cannam@160: cannam@160: static RealType find_maximum_number_of_trials( cannam@160: RealType k, // number of failures (k >= 0). cannam@160: RealType p, // success fraction 0 <= p <= 1. cannam@160: RealType alpha) // risk level threshold 0 <= alpha <= 1. cannam@160: { cannam@160: static const char* function = "boost::math::geometric<%1%>::find_maximum_number_of_trials"; cannam@160: // Error checks: cannam@160: RealType result = 0; cannam@160: if(false == geometric_detail::check_dist_and_k( cannam@160: function, p, k, &result, Policy()) cannam@160: && detail::check_probability(function, alpha, &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: result = ibetac_inva(k + 1, p, alpha, Policy()); // returns n - k cannam@160: return result + k; cannam@160: } // RealType find_number_of_trials complemented cannam@160: cannam@160: private: cannam@160: //RealType m_r; // successes fixed at unity. cannam@160: RealType m_p; // success_fraction cannam@160: }; // template class geometric_distribution cannam@160: cannam@160: typedef geometric_distribution geometric; // Reserved name of type double. cannam@160: cannam@160: template cannam@160: inline const std::pair range(const geometric_distribution& /* dist */) cannam@160: { // Range of permissible values for random variable k. cannam@160: using boost::math::tools::max_value; cannam@160: return std::pair(static_cast(0), max_value()); // max_integer? cannam@160: } cannam@160: cannam@160: template cannam@160: inline const std::pair support(const geometric_distribution& /* dist */) cannam@160: { // Range of supported values for random variable k. cannam@160: // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. cannam@160: using boost::math::tools::max_value; cannam@160: return std::pair(static_cast(0), max_value()); // max_integer? cannam@160: } cannam@160: cannam@160: template cannam@160: inline RealType mean(const geometric_distribution& dist) cannam@160: { // Mean of geometric distribution = (1-p)/p. cannam@160: return (1 - dist.success_fraction() ) / dist.success_fraction(); cannam@160: } // mean cannam@160: cannam@160: // median implemented via quantile(half) in derived accessors. cannam@160: cannam@160: template cannam@160: inline RealType mode(const geometric_distribution&) cannam@160: { // Mode of geometric distribution = zero. cannam@160: BOOST_MATH_STD_USING // ADL of std functions. cannam@160: return 0; cannam@160: } // mode cannam@160: cannam@160: template cannam@160: inline RealType variance(const geometric_distribution& dist) cannam@160: { // Variance of Binomial distribution = (1-p) / p^2. cannam@160: return (1 - dist.success_fraction()) cannam@160: / (dist.success_fraction() * dist.success_fraction()); cannam@160: } // variance cannam@160: cannam@160: template cannam@160: inline RealType skewness(const geometric_distribution& dist) cannam@160: { // skewness of geometric distribution = 2-p / (sqrt(r(1-p)) cannam@160: BOOST_MATH_STD_USING // ADL of std functions. cannam@160: RealType p = dist.success_fraction(); cannam@160: return (2 - p) / sqrt(1 - p); cannam@160: } // skewness cannam@160: cannam@160: template cannam@160: inline RealType kurtosis(const geometric_distribution& dist) cannam@160: { // kurtosis of geometric distribution cannam@160: // http://en.wikipedia.org/wiki/geometric is kurtosis_excess so add 3 cannam@160: RealType p = dist.success_fraction(); cannam@160: return 3 + (p*p - 6*p + 6) / (1 - p); cannam@160: } // kurtosis cannam@160: cannam@160: template cannam@160: inline RealType kurtosis_excess(const geometric_distribution& dist) cannam@160: { // kurtosis excess of geometric distribution cannam@160: // http://mathworld.wolfram.com/Kurtosis.html table of kurtosis_excess cannam@160: RealType p = dist.success_fraction(); cannam@160: return (p*p - 6*p + 6) / (1 - p); cannam@160: } // kurtosis_excess cannam@160: cannam@160: // RealType standard_deviation(const geometric_distribution& dist) cannam@160: // standard_deviation provided by derived accessors. cannam@160: // RealType hazard(const geometric_distribution& dist) cannam@160: // hazard of geometric distribution provided by derived accessors. cannam@160: // RealType chf(const geometric_distribution& dist) cannam@160: // chf of geometric distribution provided by derived accessors. cannam@160: cannam@160: template cannam@160: inline RealType pdf(const geometric_distribution& dist, const RealType& k) cannam@160: { // Probability Density/Mass Function. cannam@160: BOOST_FPU_EXCEPTION_GUARD cannam@160: BOOST_MATH_STD_USING // For ADL of math functions. cannam@160: static const char* function = "boost::math::pdf(const geometric_distribution<%1%>&, %1%)"; cannam@160: cannam@160: RealType p = dist.success_fraction(); cannam@160: RealType result = 0; cannam@160: if(false == geometric_detail::check_dist_and_k( cannam@160: function, cannam@160: p, cannam@160: k, cannam@160: &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: if (k == 0) cannam@160: { cannam@160: return p; // success_fraction cannam@160: } cannam@160: RealType q = 1 - p; // Inaccurate for small p? cannam@160: // So try to avoid inaccuracy for large or small p. cannam@160: // but has little effect > last significant bit. cannam@160: //cout << "p * pow(q, k) " << result << endl; // seems best whatever p cannam@160: //cout << "exp(p * k * log1p(-p)) " << p * exp(k * log1p(-p)) << endl; cannam@160: //if (p < 0.5) cannam@160: //{ cannam@160: // result = p * pow(q, k); cannam@160: //} cannam@160: //else cannam@160: //{ cannam@160: // result = p * exp(k * log1p(-p)); cannam@160: //} cannam@160: result = p * pow(q, k); cannam@160: return result; cannam@160: } // geometric_pdf cannam@160: cannam@160: template cannam@160: inline RealType cdf(const geometric_distribution& dist, const RealType& k) cannam@160: { // Cumulative Distribution Function of geometric. cannam@160: static const char* function = "boost::math::cdf(const geometric_distribution<%1%>&, %1%)"; cannam@160: cannam@160: // k argument may be integral, signed, or unsigned, or floating point. cannam@160: // If necessary, it has already been promoted from an integral type. cannam@160: RealType p = dist.success_fraction(); cannam@160: // Error check: cannam@160: RealType result = 0; cannam@160: if(false == geometric_detail::check_dist_and_k( cannam@160: function, cannam@160: p, cannam@160: k, cannam@160: &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: if(k == 0) cannam@160: { cannam@160: return p; // success_fraction cannam@160: } cannam@160: //RealType q = 1 - p; // Bad for small p cannam@160: //RealType probability = 1 - std::pow(q, k+1); cannam@160: cannam@160: RealType z = boost::math::log1p(-p, Policy()) * (k + 1); cannam@160: RealType probability = -boost::math::expm1(z, Policy()); cannam@160: cannam@160: return probability; cannam@160: } // cdf Cumulative Distribution Function geometric. cannam@160: cannam@160: template cannam@160: inline RealType cdf(const complemented2_type, RealType>& c) cannam@160: { // Complemented Cumulative Distribution Function geometric. cannam@160: BOOST_MATH_STD_USING cannam@160: static const char* function = "boost::math::cdf(const geometric_distribution<%1%>&, %1%)"; cannam@160: // k argument may be integral, signed, or unsigned, or floating point. cannam@160: // If necessary, it has already been promoted from an integral type. cannam@160: RealType const& k = c.param; cannam@160: geometric_distribution const& dist = c.dist; cannam@160: RealType p = dist.success_fraction(); cannam@160: // Error check: cannam@160: RealType result = 0; cannam@160: if(false == geometric_detail::check_dist_and_k( cannam@160: function, cannam@160: p, cannam@160: k, cannam@160: &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: RealType z = boost::math::log1p(-p, Policy()) * (k+1); cannam@160: RealType probability = exp(z); cannam@160: return probability; cannam@160: } // cdf Complemented Cumulative Distribution Function geometric. cannam@160: cannam@160: template cannam@160: inline RealType quantile(const geometric_distribution& dist, const RealType& x) cannam@160: { // Quantile, percentile/100 or Percent Point geometric function. cannam@160: // Return the number of expected failures k for a given probability p. cannam@160: cannam@160: // Inverse cumulative Distribution Function or Quantile (percentile / 100) of geometric Probability. cannam@160: // k argument may be integral, signed, or unsigned, or floating point. cannam@160: cannam@160: static const char* function = "boost::math::quantile(const geometric_distribution<%1%>&, %1%)"; cannam@160: BOOST_MATH_STD_USING // ADL of std functions. cannam@160: cannam@160: RealType success_fraction = dist.success_fraction(); cannam@160: // Check dist and x. cannam@160: RealType result = 0; cannam@160: if(false == geometric_detail::check_dist_and_prob cannam@160: (function, success_fraction, x, &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: cannam@160: // Special cases. cannam@160: if (x == 1) cannam@160: { // Would need +infinity failures for total confidence. cannam@160: result = policies::raise_overflow_error( cannam@160: function, cannam@160: "Probability argument is 1, which implies infinite failures !", Policy()); cannam@160: return result; cannam@160: // usually means return +std::numeric_limits::infinity(); cannam@160: // unless #define BOOST_MATH_THROW_ON_OVERFLOW_ERROR cannam@160: } cannam@160: if (x == 0) cannam@160: { // No failures are expected if P = 0. cannam@160: return 0; // Total trials will be just dist.successes. cannam@160: } cannam@160: // if (P <= pow(dist.success_fraction(), 1)) cannam@160: if (x <= success_fraction) cannam@160: { // p <= pdf(dist, 0) == cdf(dist, 0) cannam@160: return 0; cannam@160: } cannam@160: if (x == 1) cannam@160: { cannam@160: return 0; cannam@160: } cannam@160: cannam@160: // log(1-x) /log(1-success_fraction) -1; but use log1p in case success_fraction is small cannam@160: result = boost::math::log1p(-x, Policy()) / boost::math::log1p(-success_fraction, Policy()) - 1; cannam@160: // Subtract a few epsilons here too? cannam@160: // to make sure it doesn't slip over, so ceil would be one too many. cannam@160: return result; cannam@160: } // RealType quantile(const geometric_distribution dist, p) cannam@160: cannam@160: template cannam@160: inline RealType quantile(const complemented2_type, RealType>& c) cannam@160: { // Quantile or Percent Point Binomial function. cannam@160: // Return the number of expected failures k for a given cannam@160: // complement of the probability Q = 1 - P. cannam@160: static const char* function = "boost::math::quantile(const geometric_distribution<%1%>&, %1%)"; cannam@160: BOOST_MATH_STD_USING cannam@160: // Error checks: cannam@160: RealType x = c.param; cannam@160: const geometric_distribution& dist = c.dist; cannam@160: RealType success_fraction = dist.success_fraction(); cannam@160: RealType result = 0; cannam@160: if(false == geometric_detail::check_dist_and_prob( cannam@160: function, cannam@160: success_fraction, cannam@160: x, cannam@160: &result, Policy())) cannam@160: { cannam@160: return result; cannam@160: } cannam@160: cannam@160: // Special cases: cannam@160: if(x == 1) cannam@160: { // There may actually be no answer to this question, cannam@160: // since the probability of zero failures may be non-zero, cannam@160: return 0; // but zero is the best we can do: cannam@160: } cannam@160: if (-x <= boost::math::powm1(dist.success_fraction(), dist.successes(), Policy())) cannam@160: { // q <= cdf(complement(dist, 0)) == pdf(dist, 0) cannam@160: return 0; // cannam@160: } cannam@160: if(x == 0) cannam@160: { // Probability 1 - Q == 1 so infinite failures to achieve certainty. cannam@160: // Would need +infinity failures for total confidence. cannam@160: result = policies::raise_overflow_error( cannam@160: function, cannam@160: "Probability argument complement is 0, which implies infinite failures !", Policy()); cannam@160: return result; cannam@160: // usually means return +std::numeric_limits::infinity(); cannam@160: // unless #define BOOST_MATH_THROW_ON_OVERFLOW_ERROR cannam@160: } cannam@160: // log(x) /log(1-success_fraction) -1; but use log1p in case success_fraction is small cannam@160: result = log(x) / boost::math::log1p(-success_fraction, Policy()) - 1; cannam@160: return result; cannam@160: cannam@160: } // quantile complement cannam@160: cannam@160: } // namespace math cannam@160: } // namespace boost cannam@160: cannam@160: // This include must be at the end, *after* the accessors cannam@160: // for this distribution have been defined, in order to cannam@160: // keep compilers that support two-phase lookup happy. cannam@160: #include cannam@160: cannam@160: #if defined (BOOST_MSVC) cannam@160: # pragma warning(pop) cannam@160: #endif cannam@160: cannam@160: #endif // BOOST_MATH_SPECIAL_GEOMETRIC_HPP