MCKL
Monte Carlo Kernel Library
gamma.hpp
Go to the documentation of this file.
1 //============================================================================
2 // MCKL/include/mckl/math/gamma.hpp
3 //----------------------------------------------------------------------------
4 // MCKL: Monte Carlo Kernel Library
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2018, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef MCKL_MATH_GAMMA_HPP
33 #define MCKL_MATH_GAMMA_HPP
34 
35 #include <mckl/internal/config.h>
36 #include <mckl/math/constants.hpp>
37 
38 #include <algorithm>
39 #include <cmath>
40 
41 namespace mckl {
42 
43 namespace internal {
44 
45 static constexpr std::size_t gammap_approx_n = 18;
46 
47 static constexpr double gammap_approx_y[] = {0.0021695375159141994,
48  0.011413521097787704, 0.027972308950302116, 0.051727015600492421,
49  0.082502225484340941, 0.12007019910960293, 0.16415283300752470,
50  0.21442376986779355, 0.27051082840644336, 0.33199876341447887,
51  0.39843234186401943, 0.46931971407375483, 0.54413605556657973,
52  0.62232745288031077, 0.70331500465597174, 0.78649910768313447,
53  0.87126389619061517, 0.95698180152629142};
54 
55 static constexpr double gammap_approx_w[] = {0.0055657196642445571,
56  0.012915947284065419, 0.020181515297735382, 0.027298621498568734,
57  0.034213810770299537, 0.040875750923643261, 0.047235083490265582,
58  0.053244713977759692, 0.058860144245324798, 0.064039797355015485,
59  0.068745323835736408, 0.072941885005653087, 0.076598410645870640,
60  0.079687828912071670, 0.082187266704339706, 0.084078218979661945,
61  0.085346685739338721, 0.085983275670394821};
62 
63 inline double gammap_approx(double a, double x, bool psig)
64 {
65  double a1 = a - 1;
66  double lna1 = std::log(a1);
67  double sqrta1 = std::sqrt(a1);
68  double xu = x > a1 ?
69  std::max(a1 + 11.5 * sqrta1, x + 6 * sqrta1) :
70  std::max(0.0, std::min(a1 - 7.5 * sqrta1, x - 5 * sqrta1));
71  double sum = 0;
72  for (std::size_t i = 0; i != gammap_approx_n; ++i) {
73  double t = x + (xu - x) * gammap_approx_y[i];
74  sum += gammap_approx_w[i] *
75  std::exp(-(t - a1) + a1 * (std::log(t) - lna1));
76  }
77  double ans = sum * (xu - x) * std::exp(a1 * (lna1 - 1.) - std::lgamma(a));
78 
79  return psig ? (x > a1 ? 1 - ans : -ans) : (x > a1 ? ans : 1 + ans);
80 }
81 
82 inline double gammap_gser(double a, double x)
83 {
84  constexpr double eps = std::numeric_limits<double>::epsilon();
85 
86  double ap = a;
87  double del = 1 / a;
88  double sum = del;
89  while (true) {
90  ap += 1;
91  del *= x / ap;
92  sum += del;
93  if (std::abs(del) < std::abs(sum) * eps) {
94  return sum * std::exp(-x + a * std::log(x) - std::lgamma(a));
95  }
96  }
97 }
98 
99 inline double gammap_gcf(double a, double x)
100 {
101  constexpr double eps = std::numeric_limits<double>::epsilon();
102  constexpr double fpmin = std::numeric_limits<double>::min() / eps;
103 
104  double b = x + 1 - a;
105  double c = 1.0 / fpmin;
106  double d = 1 / b;
107  double h = d;
108  double n = 0;
109  while (true) {
110  n += 1;
111  double an = -n * (n - a);
112  b += 2;
113  d = an * d + b;
114  if (std::abs(d) < fpmin) {
115  d = fpmin;
116  }
117  c = b + an / c;
118  if (std::abs(c) < fpmin) {
119  c = fpmin;
120  }
121  d = 1 / d;
122  double del = d * c;
123  h *= del;
124  if (std::abs(del - 1) <= eps) {
125  break;
126  }
127  }
128 
129  return std::exp(-x + a * std::log(x) - std::lgamma(a)) * h;
130 }
131 
132 } // namespace internal
133 
136 inline double gammap(double a, double x)
137 {
138  if (x < 0 || a <= 0) {
139  return const_nan<double>();
140  }
141  if (!(x > 0 || x < 0)) {
142  return 0;
143  }
144  if (a > 100) {
145  return internal::gammap_approx(a, x, true);
146  }
147  if (x < a + 1) {
148  return internal::gammap_gser(a, x);
149  }
150  return 1 - internal::gammap_gcf(a, x);
151 }
152 
155 inline double gammapinv(double a, double y)
156 {
157  constexpr double eps = 1e-8;
158 
159  if (a <= 0) {
160  return const_nan<double>();
161  }
162  if (y >= 1) {
163  return const_inf<double>();
164  }
165  if (y <= 0) {
166  return 0;
167  }
168 
169  const double a1 = a - 1;
170  const double gln = std::lgamma(a);
171 
172  double x = 0;
173  double lna1 = 0;
174  double afrac = 0;
175  if (a > 1) {
176  lna1 = std::log(a1);
177  afrac = std::exp(a1 * (lna1 - 1) - gln);
178  double z = y < 0.5 ? y : 1 - y;
179  double t = std::sqrt(-2 * std::log(z));
180  x = (2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t;
181  if (y < 0.5) {
182  x = -x;
183  }
184  x = std::max(
185  1e-3, a * std::pow(1 - 1 / (9 * a) - x / (3 * std::sqrt(a)), 3));
186  } else {
187  double t = 1 - a * (0.253 + a * 0.12);
188  x = y < t ? std::pow(y / t, 1 / a) :
189  1 - std::log(1 - (y - t) / (1 - t));
190  }
191 
192  for (int i = 0; i != 12; ++i) {
193  if (x <= 0) {
194  return 0;
195  }
196 
197  double err = gammap(a, x) - y;
198  double t = a > 1 ?
199  afrac * std::exp(-(x - a1) + a1 * (std::log(x) - lna1)) :
200  std::exp(-x + a1 * std::log(x) - gln);
201  double u = err / t;
202  t = u / (1 - 0.5 * std::min(1.0, u * (a1 / x - 1)));
203  x -= t;
204  if (x <= 0) {
205  x = 0.5 * (x + t);
206  }
207 
208  if (std::abs(t) < eps * x) {
209  return x;
210  }
211  }
212 
213  return x;
214 }
215 
216 } // namespace mckl
217 
218 #endif // MCKL_MATH_GAMMA_HPP
void pow(std::size_t n, const float *a, const float *b, float *y)
Definition: vmf.hpp:184
double gammap_approx(double a, double x, bool psig)
Definition: gamma.hpp:63
static constexpr double gammap_approx_w[]
Definition: gamma.hpp:55
static constexpr std::size_t gammap_approx_n
Definition: gamma.hpp:45
static constexpr double gammap_approx_y[]
Definition: gamma.hpp:47
void lgamma(std::size_t n, const float *a, float *y)
Definition: vmf.hpp:303
double gammapinv(double a, double y)
Inverse regularized lower incomplete Gamma function.
Definition: gamma.hpp:155
void log(std::size_t n, const float *a, float *y)
Definition: vmf.hpp:226
double gammap(double a, double x)
Regularized lower incomplete Gamma function.
Definition: gamma.hpp:136
void abs(std::size_t n, const float *a, float *y)
Definition: vmf.hpp:128
double gammap_gser(double a, double x)
Definition: gamma.hpp:82
double gammap_gcf(double a, double x)
Definition: gamma.hpp:99
void sqrt(std::size_t n, const float *a, float *y)
Definition: vmf.hpp:177
Definition: mcmc.hpp:40
void exp(std::size_t n, const float *a, float *y)
Definition: vmf.hpp:221