annotate DEPENDENCIES/generic/include/boost/math/special_functions/detail/gamma_inva.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents c530137014c0
children
rev   line source
Chris@16 1 // (C) Copyright John Maddock 2006.
Chris@16 2 // Use, modification and distribution are subject to the
Chris@16 3 // Boost Software License, Version 1.0. (See accompanying file
Chris@16 4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Chris@16 5
Chris@16 6 //
Chris@16 7 // This is not a complete header file, it is included by gamma.hpp
Chris@16 8 // after it has defined it's definitions. This inverts the incomplete
Chris@16 9 // gamma functions P and Q on the first parameter "a" using a generic
Chris@16 10 // root finding algorithm (TOMS Algorithm 748).
Chris@16 11 //
Chris@16 12
Chris@16 13 #ifndef BOOST_MATH_SP_DETAIL_GAMMA_INVA
Chris@16 14 #define BOOST_MATH_SP_DETAIL_GAMMA_INVA
Chris@16 15
Chris@16 16 #ifdef _MSC_VER
Chris@16 17 #pragma once
Chris@16 18 #endif
Chris@16 19
Chris@16 20 #include <boost/math/tools/toms748_solve.hpp>
Chris@16 21 #include <boost/cstdint.hpp>
Chris@16 22
Chris@16 23 namespace boost{ namespace math{ namespace detail{
Chris@16 24
Chris@16 25 template <class T, class Policy>
Chris@16 26 struct gamma_inva_t
Chris@16 27 {
Chris@16 28 gamma_inva_t(T z_, T p_, bool invert_) : z(z_), p(p_), invert(invert_) {}
Chris@16 29 T operator()(T a)
Chris@16 30 {
Chris@16 31 return invert ? p - boost::math::gamma_q(a, z, Policy()) : boost::math::gamma_p(a, z, Policy()) - p;
Chris@16 32 }
Chris@16 33 private:
Chris@16 34 T z, p;
Chris@16 35 bool invert;
Chris@16 36 };
Chris@16 37
Chris@16 38 template <class T, class Policy>
Chris@16 39 T inverse_poisson_cornish_fisher(T lambda, T p, T q, const Policy& pol)
Chris@16 40 {
Chris@16 41 BOOST_MATH_STD_USING
Chris@16 42 // mean:
Chris@16 43 T m = lambda;
Chris@16 44 // standard deviation:
Chris@16 45 T sigma = sqrt(lambda);
Chris@16 46 // skewness
Chris@16 47 T sk = 1 / sigma;
Chris@16 48 // kurtosis:
Chris@16 49 // T k = 1/lambda;
Chris@16 50 // Get the inverse of a std normal distribution:
Chris@16 51 T x = boost::math::erfc_inv(p > q ? 2 * q : 2 * p, pol) * constants::root_two<T>();
Chris@16 52 // Set the sign:
Chris@16 53 if(p < 0.5)
Chris@16 54 x = -x;
Chris@16 55 T x2 = x * x;
Chris@16 56 // w is correction term due to skewness
Chris@16 57 T w = x + sk * (x2 - 1) / 6;
Chris@16 58 /*
Chris@16 59 // Add on correction due to kurtosis.
Chris@16 60 // Disabled for now, seems to make things worse?
Chris@16 61 //
Chris@16 62 if(lambda >= 10)
Chris@16 63 w += k * x * (x2 - 3) / 24 + sk * sk * x * (2 * x2 - 5) / -36;
Chris@16 64 */
Chris@16 65 w = m + sigma * w;
Chris@16 66 return w > tools::min_value<T>() ? w : tools::min_value<T>();
Chris@16 67 }
Chris@16 68
Chris@16 69 template <class T, class Policy>
Chris@16 70 T gamma_inva_imp(const T& z, const T& p, const T& q, const Policy& pol)
Chris@16 71 {
Chris@16 72 BOOST_MATH_STD_USING // for ADL of std lib math functions
Chris@16 73 //
Chris@16 74 // Special cases first:
Chris@16 75 //
Chris@16 76 if(p == 0)
Chris@16 77 {
Chris@101 78 return policies::raise_overflow_error<T>("boost::math::gamma_p_inva<%1%>(%1%, %1%)", 0, Policy());
Chris@16 79 }
Chris@16 80 if(q == 0)
Chris@16 81 {
Chris@16 82 return tools::min_value<T>();
Chris@16 83 }
Chris@16 84 //
Chris@16 85 // Function object, this is the functor whose root
Chris@16 86 // we have to solve:
Chris@16 87 //
Chris@16 88 gamma_inva_t<T, Policy> f(z, (p < q) ? p : q, (p < q) ? false : true);
Chris@16 89 //
Chris@16 90 // Tolerance: full precision.
Chris@16 91 //
Chris@16 92 tools::eps_tolerance<T> tol(policies::digits<T, Policy>());
Chris@16 93 //
Chris@16 94 // Now figure out a starting guess for what a may be,
Chris@16 95 // we'll start out with a value that'll put p or q
Chris@16 96 // right bang in the middle of their range, the functions
Chris@16 97 // are quite sensitive so we should need too many steps
Chris@16 98 // to bracket the root from there:
Chris@16 99 //
Chris@16 100 T guess;
Chris@16 101 T factor = 8;
Chris@16 102 if(z >= 1)
Chris@16 103 {
Chris@16 104 //
Chris@16 105 // We can use the relationship between the incomplete
Chris@16 106 // gamma function and the poisson distribution to
Chris@16 107 // calculate an approximate inverse, for large z
Chris@16 108 // this is actually pretty accurate, but it fails badly
Chris@16 109 // when z is very small. Also set our step-factor according
Chris@16 110 // to how accurate we think the result is likely to be:
Chris@16 111 //
Chris@16 112 guess = 1 + inverse_poisson_cornish_fisher(z, q, p, pol);
Chris@16 113 if(z > 5)
Chris@16 114 {
Chris@16 115 if(z > 1000)
Chris@16 116 factor = 1.01f;
Chris@16 117 else if(z > 50)
Chris@16 118 factor = 1.1f;
Chris@16 119 else if(guess > 10)
Chris@16 120 factor = 1.25f;
Chris@16 121 else
Chris@16 122 factor = 2;
Chris@16 123 if(guess < 1.1)
Chris@16 124 factor = 8;
Chris@16 125 }
Chris@16 126 }
Chris@16 127 else if(z > 0.5)
Chris@16 128 {
Chris@16 129 guess = z * 1.2f;
Chris@16 130 }
Chris@16 131 else
Chris@16 132 {
Chris@16 133 guess = -0.4f / log(z);
Chris@16 134 }
Chris@16 135 //
Chris@16 136 // Max iterations permitted:
Chris@16 137 //
Chris@16 138 boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
Chris@16 139 //
Chris@16 140 // Use our generic derivative-free root finding procedure.
Chris@16 141 // We could use Newton steps here, taking the PDF of the
Chris@16 142 // Poisson distribution as our derivative, but that's
Chris@16 143 // even worse performance-wise than the generic method :-(
Chris@16 144 //
Chris@16 145 std::pair<T, T> r = bracket_and_solve_root(f, guess, factor, false, tol, max_iter, pol);
Chris@16 146 if(max_iter >= policies::get_max_root_iterations<Policy>())
Chris@101 147 return policies::raise_evaluation_error<T>("boost::math::gamma_p_inva<%1%>(%1%, %1%)", "Unable to locate the root within a reasonable number of iterations, closest approximation so far was %1%", r.first, pol);
Chris@16 148 return (r.first + r.second) / 2;
Chris@16 149 }
Chris@16 150
Chris@16 151 } // namespace detail
Chris@16 152
Chris@16 153 template <class T1, class T2, class Policy>
Chris@16 154 inline typename tools::promote_args<T1, T2>::type
Chris@16 155 gamma_p_inva(T1 x, T2 p, const Policy& pol)
Chris@16 156 {
Chris@16 157 typedef typename tools::promote_args<T1, T2>::type result_type;
Chris@16 158 typedef typename policies::evaluation<result_type, Policy>::type value_type;
Chris@16 159 typedef typename policies::normalise<
Chris@16 160 Policy,
Chris@16 161 policies::promote_float<false>,
Chris@16 162 policies::promote_double<false>,
Chris@16 163 policies::discrete_quantile<>,
Chris@16 164 policies::assert_undefined<> >::type forwarding_policy;
Chris@16 165
Chris@16 166 if(p == 0)
Chris@16 167 {
Chris@101 168 policies::raise_overflow_error<result_type>("boost::math::gamma_p_inva<%1%>(%1%, %1%)", 0, Policy());
Chris@16 169 }
Chris@16 170 if(p == 1)
Chris@16 171 {
Chris@16 172 return tools::min_value<result_type>();
Chris@16 173 }
Chris@16 174
Chris@16 175 return policies::checked_narrowing_cast<result_type, forwarding_policy>(
Chris@16 176 detail::gamma_inva_imp(
Chris@16 177 static_cast<value_type>(x),
Chris@16 178 static_cast<value_type>(p),
Chris@16 179 static_cast<value_type>(1 - static_cast<value_type>(p)),
Chris@16 180 pol), "boost::math::gamma_p_inva<%1%>(%1%, %1%)");
Chris@16 181 }
Chris@16 182
Chris@16 183 template <class T1, class T2, class Policy>
Chris@16 184 inline typename tools::promote_args<T1, T2>::type
Chris@16 185 gamma_q_inva(T1 x, T2 q, const Policy& pol)
Chris@16 186 {
Chris@16 187 typedef typename tools::promote_args<T1, T2>::type result_type;
Chris@16 188 typedef typename policies::evaluation<result_type, Policy>::type value_type;
Chris@16 189 typedef typename policies::normalise<
Chris@16 190 Policy,
Chris@16 191 policies::promote_float<false>,
Chris@16 192 policies::promote_double<false>,
Chris@16 193 policies::discrete_quantile<>,
Chris@16 194 policies::assert_undefined<> >::type forwarding_policy;
Chris@16 195
Chris@16 196 if(q == 1)
Chris@16 197 {
Chris@101 198 policies::raise_overflow_error<result_type>("boost::math::gamma_q_inva<%1%>(%1%, %1%)", 0, Policy());
Chris@16 199 }
Chris@16 200 if(q == 0)
Chris@16 201 {
Chris@16 202 return tools::min_value<result_type>();
Chris@16 203 }
Chris@16 204
Chris@16 205 return policies::checked_narrowing_cast<result_type, forwarding_policy>(
Chris@16 206 detail::gamma_inva_imp(
Chris@16 207 static_cast<value_type>(x),
Chris@16 208 static_cast<value_type>(1 - static_cast<value_type>(q)),
Chris@16 209 static_cast<value_type>(q),
Chris@16 210 pol), "boost::math::gamma_q_inva<%1%>(%1%, %1%)");
Chris@16 211 }
Chris@16 212
Chris@16 213 template <class T1, class T2>
Chris@16 214 inline typename tools::promote_args<T1, T2>::type
Chris@16 215 gamma_p_inva(T1 x, T2 p)
Chris@16 216 {
Chris@16 217 return boost::math::gamma_p_inva(x, p, policies::policy<>());
Chris@16 218 }
Chris@16 219
Chris@16 220 template <class T1, class T2>
Chris@16 221 inline typename tools::promote_args<T1, T2>::type
Chris@16 222 gamma_q_inva(T1 x, T2 q)
Chris@16 223 {
Chris@16 224 return boost::math::gamma_q_inva(x, q, policies::policy<>());
Chris@16 225 }
Chris@16 226
Chris@16 227 } // namespace math
Chris@16 228 } // namespace boost
Chris@16 229
Chris@16 230 #endif // BOOST_MATH_SP_DETAIL_GAMMA_INVA
Chris@16 231
Chris@16 232
Chris@16 233