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
|