distribution 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ module Distribution
2
+ module MathExtension
3
+ # From GSL-1.9.
4
+ module ExponentialIntegral
5
+ class << self
6
+ def first_order x, scale = 0, with_error = false
7
+ xmaxt = -Math::LOG_FLOAT_MIN
8
+ xmax = xmaxt - Math.log(xmaxt)
9
+ result = nil
10
+ error = with_error ? nil : 0.0
11
+
12
+ if x < -xmax && !scale
13
+ raise("Overflow Error")
14
+ elsif x <= -10.0
15
+ s = 1.0 / x * ( scale ? 1.0 : Math.exp(-x))
16
+ result_c = ChebyshevSeries.eval(20.0/x+1.0, :ae11, with_error)
17
+ result_c, result_c_err = result_c if with_error
18
+ result = s * (1.0 + result_c)
19
+ error ||= (s * result_c_err) + 2.0*Float::EPSILON * (x.abs + 1.0) * result.abs
20
+ elsif x <= -4.0
21
+ s = 1.0 / x * (scale ? 1.0 : Math.exp(-x))
22
+ result_c = ChebyshevSeries.eval((40.0/x+7.0)/3.0, :ae12, with_error)
23
+ result_c, result_c_err = result_c if with_error
24
+ result = s * (1.0 + result_c)
25
+ error ||= (s * result_c_err) + 2.0*Float::EPSILON * result.abs
26
+ elsif x <= -1.0
27
+ ln_term = - Math.log(x.abs)
28
+ scale_factor = scale ? Math.exp(x) : 1.0
29
+ result_c = ChebyshevSeries.eval((2.0*x+5.0)/3.0, :e11, with_error)
30
+ result_c, result_c_err = result_c if with_error
31
+ result = scale_factor * (ln_term + result_c)
32
+ error ||= scale_factor * (result_c_err + Float::EPSILON * ln_term.abs) + 2.0*Float::EPSILON*result.abs
33
+ elsif x == 0.0
34
+ raise(ArgumentError, "Domain Error")
35
+ elsif x <= 1.0
36
+ ln_term = - Math.log(x.abs)
37
+ scale_factor = scale ? Math.exp(x) : 1.0
38
+ result_c = ChebyshevSeries.eval(x, :e12, with_error)
39
+ result_c, result_c_err = result_c if with_error
40
+ result = scale_factor * (ln_term - 0.6875 + x + result_c)
41
+ error ||= scale_factor * (result_c_err + Float::EPSILON * ln_term.abs) + 2.0*Float::EPSILON*result.abs
42
+ elsif x <= 4.0
43
+ s = 1.0 / x * (scale ? 1.0 : Math.exp(-x))
44
+ result_c = ChebyshevSeries.eval((8.0/x-5.0)/3.0, :ae13, with_error)
45
+ result_c, result_c_err = result_c if with_error
46
+ result = s * (1.0 + result_c)
47
+ error ||= (s * result_c_err) + 2.0*Float::EPSILON * result.abs
48
+ elsif x <= xmax || scale
49
+ s = 1.0 / x * (scale ? 1.0 : Math.exp(-x))
50
+ result_c = ChebyshevSeries.eval(8.0/x-1.0, :ae14, with_error)
51
+ result_c, result_c_err = result_c if with_error
52
+ result = s * (1.0 + result_c)
53
+ error ||= s * (Float::EPSILON + result_c_err) + 2.0*(x+1.0)*Float::EPSILON * result.abs
54
+ raise("Underflow Error") if result == 0.0
55
+ else
56
+ raise("Underflow Error")
57
+ end
58
+ with_error ? [result, error] : result
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,64 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files in the specfunc/ dir.
3
+
4
+ module Distribution
5
+ module MathExtension
6
+ # Derived from GSL-1.9.
7
+ module Gammastar
8
+ C0 = 1.quo(12)
9
+ C1 = -1.quo(360)
10
+ C2 = 1.quo(1260)
11
+ C3 = -1.quo(1680)
12
+ C4 = 1.quo(1188)
13
+ C5 = -691.quo(360360)
14
+ C6 = 1.quo(156)
15
+ C7 = -3617.quo(122400)
16
+
17
+ class << self
18
+
19
+ def series x, with_error = false
20
+ # Use the Stirling series for the correction to Log(Gamma(x)),
21
+ # which is better behaved and easier to compute than the
22
+ # regular Stirling series for Gamma(x).
23
+ y = 1.quo(x*x)
24
+ ser = C0 + y*(C1 + y*(C2 + y*(C3 + y*(C4 + y*(C5 + y*(C6 + y*C7))))))
25
+ result = Math.exp(ser/x)
26
+ with_error ? [result, 2.0 * Float::EPSILON * result * [1, ser/x].max] : result
27
+ end
28
+
29
+ def evaluate x, with_error = false
30
+ raise(ArgumentError, "x must be positive") if x <= 0
31
+ if x < 0.5
32
+ STDERR.puts("Warning: Don't know error on lg_x, error for this function will be incorrect") if with_error
33
+ lg = Math.lgamma(x).first
34
+ lg_err = Float::EPSILON # Guess
35
+ lx = Math.log(x)
36
+ c = 0.5 * (LN2 + LNPI)
37
+ lnr_val = lg - (x-0.5)*lx + x - c
38
+ lnr_err = lg_err + 2.0*Float::EPSILON * ((x+0.5)*lx.abs + c)
39
+ with_error ? exp_err(lnr_val, lnr_err) : Math.exp(lnr_val)
40
+ elsif x < 2.0
41
+ t = 4.0/3.0*(x-0.5) - 1.0
42
+ ChebyshevSeries.evaluate(:gstar_a, t, with_error)
43
+ elsif x < 10.0
44
+ t = 0.25*(x-2.0) - 1.0
45
+ c = ChebyshevSeries.evaluate(:gstar_b, t, with_error)
46
+ c, c_err = c if with_error
47
+
48
+ result = c / (x*x) + 1.0 + 1.0/(12.0*x)
49
+ with_error ? [result, c_err / (x*x) + 2.0*Float::EPSILON*result.abs] : result
50
+ elsif x < 1.0/Math::ROOT4_FLOAT_EPSILON
51
+ series x, with_error
52
+ elsif x < 1.0 / Float::EPSILON # Stirling
53
+ xi = 1.0 / x
54
+ result = 1.0 + xi/12.0*(1.0 + xi/24.0*(1.0 - xi*(139.0/180.0 + 571.0/8640.0*xi)))
55
+ result_err = 2.0 * Float::EPSILON * result.abs
56
+ with_error ? [result,result_err] : result
57
+ else
58
+ with_error ? [1.0,1.0/x] : 1.0
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files, mostly in the specfunc/ dir.
3
+
4
+ module Distribution
5
+ module MathExtension
6
+ LNPI = 1.14472988584940017414342735135
7
+ LN2 = 0.69314718055994530941723212146
8
+ SQRT2 = 1.41421356237309504880168872421
9
+ SQRTPI = 1.77245385090551602729816748334
10
+
11
+ ROOT3_FLOAT_MIN = Float::MIN ** (1/3.0)
12
+ ROOT3_FLOAT_EPSILON = Float::EPSILON ** (1/3.0)
13
+ ROOT4_FLOAT_MIN = Float::MIN ** (1/4.0)
14
+ ROOT4_FLOAT_EPSILON = Float::EPSILON ** (1/4.0)
15
+ ROOT5_FLOAT_MIN = Float::MIN ** (1/5.0)
16
+ ROOT5_FLOAT_EPSILON = Float::EPSILON ** (1/5.0)
17
+ ROOT6_FLOAT_MIN = Float::MIN ** (1/6.0)
18
+ ROOT6_FLOAT_EPSILON = Float::EPSILON ** (1/6.0)
19
+ LOG_FLOAT_MIN = Math.log(Float::MIN)
20
+ EULER = 0.57721566490153286060651209008
21
+
22
+ # e^x taking into account the error thus far (I think)
23
+ # gsl_sf_exp_err_e
24
+ def exp_err(x, dx)
25
+ adx = dx.abs
26
+ raise("Overflow Error in exp_err: x + adx > LOG_FLOAT_MAX") if x + adx > LOG_FLOAT_MAX
27
+ raise("Underflow Error in exp_err: x - adx < LOG_FLOAT_MIN") if x - adx < LOG_FLOAT_MIN
28
+ [Math.exp(x), Math.exp(x) * [Float::EPSILON, Math.exp(adx) - 1.0/Math.exp(adx)] + 2.0 * Float::EPSILON * Math.exp(x).abs]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,213 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files in the specfunc/ dir.
3
+
4
+ module Distribution
5
+ module MathExtension
6
+ module Beta
7
+ class << self
8
+ # Based on gsl_sf_lnbeta_e and gsl_sf_lnbeta_sgn_e
9
+ # Returns result and sign in an array. If with_error is specified, also returns the error.
10
+ def log_beta(x,y, with_error=false)
11
+ sign = nil
12
+
13
+ raise(ArgumentError, "x and y must be nonzero") if x == 0.0 || y == 0.0
14
+ raise(ArgumentError, "not defined for negative integers") if [x,y].any? { |v| (v.is_a?(Fixnum) && v < 0) }
15
+
16
+ # See if we can handle the positive case with min/max < 0.2
17
+ if x > 0 && y > 0
18
+ min, max = [x,y].minmax
19
+ ratio = min.quo(max)
20
+
21
+ if ratio < 0.2
22
+ gsx = Gammastar.evaluate(x, with_error)
23
+ gsy = Gammastar.evaluate(y, with_error)
24
+ gsxy = Gammastar.evaluate(x+y, with_error)
25
+ lnopr = Log::log_1plusx(ratio, with_error)
26
+
27
+ gsx, gsx_err, gsy, gsy_err, gsxy, gsxy_err, lnopr, lnopr_err = [gsx,gsy,gsxy,lnopr].flatten if with_error
28
+
29
+ lnpre = Math.log((gsx*gsy).quo(gsxy) * Math::SQRT2 * Math::SQRTPI)
30
+ lnpre_err = gsx_err.quo(gsx) + gsy_err(gsy) + gsxy_err.quo(gsxy) if with_error
31
+
32
+ t1 = min * Math.log(ratio)
33
+ t2 = 0.5 * Math.log(min)
34
+ t3 = (x+y-0.5)*lnopr
35
+
36
+ lnpow = t1 - t2 - t3
37
+ lnpow_err = Float::EPSILON * (t1.abs + t2.abs + t3.abs) + (x+y-0.5).abs * lnopr_err if with_error
38
+
39
+ result = lnpre + lnpow
40
+ error = lnpre_err + lnpow_err + 2.0*Float::EPSILON*result.abs if with_error
41
+
42
+ return with_error ? [result, 1.0, error] : [result, 1.0]
43
+ end
44
+ end
45
+
46
+ # General case: fallback
47
+ lgx, sgx = Math.lgamma(x)
48
+ lgy, sgy = Math.lgamma(y)
49
+ lgxy, sgxy = Math.lgamma(x+y)
50
+ sgn = sgx * sgy * sgxy
51
+
52
+ raise("Domain error: sign is -") if sgn == -1
53
+
54
+ result = lgx + lgy - lgxy
55
+ if with_error
56
+ lgx_err, lgy_err, lgxy_err = begin
57
+ STDERR.puts("Warning: Error is unknown for Math::lgamma, guessing.")
58
+ [Math::EPSILON, Math::EPSILON, Math::EPSILON]
59
+ end
60
+
61
+ error = lgx_err + lgy_err + lgxy_err + Float::EPSILON*(lgx.abs+lgy.abs+lgxy.abs) + 2.0*(Float::EPSILON)*result.abs
62
+ return [result, sgn, error]
63
+ else
64
+ return [result, sgn]
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ # Calculate regularized incomplete beta function
71
+ module IncompleteBeta
72
+
73
+ MAX_ITER = 512
74
+ CUTOFF = 2.0 * Float::MIN
75
+
76
+ class << self
77
+
78
+ # Evaluate aa * beta_inc(a,b,x) + yy
79
+ #
80
+ # No error mode available.
81
+ #
82
+ # From GSL-1.9: cdf/beta_inc.c, beta_inc_AXPY
83
+ def axpy(aa,yy,a,b,x)
84
+ return aa*0 + yy if x == 0.0
85
+ return aa*1 + yy if x == 1.0
86
+
87
+ ln_beta = Math.logbeta(a, b)
88
+ ln_pre = -ln_beta + a * Math.log(x) + b * Math::Log.log1p(-x)
89
+ prefactor = Math.exp(ln_pre)
90
+
91
+ if x < (a+1).quo(a+b+2)
92
+ # Apply continued fraction directly
93
+ epsabs = yy.quo((aa * prefactor).quo(a)).abs * Float::EPSILON
94
+ cf = continued_fraction(a, b, x, epsabs)
95
+ return aa * (prefactor * cf).quo(a) + yy
96
+ else
97
+ # Apply continued fraction after hypergeometric transformation
98
+ epsabs = (aa + yy).quo( (aa*prefactor).quo(b) ) * Float::EPSILON
99
+ cf = continued_fraction(b, a, 1-x, epsabs)
100
+ term = (prefactor * cf).quo(b)
101
+ return aa == -yy ? -aa*term : aa*(1-term)+yy
102
+ end
103
+ end
104
+
105
+
106
+ # Evaluate the incomplete beta function
107
+ # gsl_sf_beta_inc_e
108
+ def evaluate(a,b,x,with_error=false)
109
+ raise(ArgumentError, "Domain error: a(#{a}), b(#{b}) must be positive; x(#{x}) must be between 0 and 1, inclusive") if a <= 0 || b <= 0 || x < 0 || x > 1
110
+ if x == 0
111
+ return with_error ? [0.0,0.0] : 0.0
112
+ elsif x == 1
113
+ return with_error ? [1.0,0.0] : 1.0
114
+ else
115
+
116
+ ln_beta = Beta.log_beta(a,b, with_error)
117
+ ln_1mx = Log.log_1plusx(-x, with_error)
118
+ ln_x = Math.log(x)
119
+
120
+ ln_beta, ln_beta_err, ln_1mx, ln_1mx_err, ln_x_err = begin
121
+ #STDERR.puts("Warning: Error is unknown for Math::log, guessing.")
122
+ [ln_beta,ln_1mx,Float::EPSILON].flatten
123
+ end
124
+
125
+ ln_pre = -ln_beta + a*ln_x + b*ln_1mx
126
+ ln_pre_err = ln_beta_err + (a*ln_x_err).abs + (b*ln_1mx_err).abs if with_error
127
+
128
+ prefactor, prefactor_err = begin
129
+ if with_error
130
+ exp_err(ln_pre, ln_pre_err)
131
+ else
132
+ [Math.exp(ln_pre), nil]
133
+ end
134
+ end
135
+
136
+ if x < (a+1).quo(a+b+2)
137
+ # Apply continued fraction directly
138
+
139
+ cf = continued_fraction(a,b,x, nil, with_error)
140
+ cf,cf_err = cf if with_error
141
+ result = (prefactor * cf).quo(a)
142
+ return with_error ? [result, ((prefactor_err*cf).abs + (prefactor*cf_err).abs).quo(a)] : result
143
+ else
144
+ # Apply continued fraction after hypergeometric transformation
145
+
146
+ cf = continued_fraction(b, a, 1-x, nil)
147
+ cf,cf_err = cf if with_error
148
+ term = (prefactor * cf).quo(b)
149
+ result = 1 - term
150
+
151
+ return with_error ? [result, (prefactor_err*cf).quo(b) + (prefactor*cf_err).quo(b) + 2.0*Float::EPSILON*(1+term.abs)] : result
152
+ end
153
+
154
+ end
155
+ end
156
+
157
+
158
+ def continued_fraction_cutoff(epsabs)
159
+ return CUTOFF if epsabs.nil?
160
+ 0.0/0 # NaN
161
+ end
162
+
163
+ # Continued fraction calculation of incomplete beta
164
+ # beta_cont_frac from GSL-1.9
165
+ #
166
+ # If epsabs is set, will execute the version of the GSL function in the cdf folder. Otherwise, does the
167
+ # basic one in specfunc.
168
+ def continued_fraction(a,b,x,epsabs=nil,with_error=false)
169
+ num_term = 1
170
+ den_term = 1 - (a+b)*x.quo(a+1)
171
+ k = 0
172
+
173
+ den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF
174
+ den_term = 1.quo(den_term)
175
+ cf = den_term
176
+
177
+ 1.upto(MAX_ITER) do |k|
178
+ coeff = k *(b-k)*x.quo(((a-1)+2*k)*(a+2*k)) # coefficient for step 1
179
+ delta_frac = nil
180
+ 2.times do
181
+
182
+ den_term = 1 + coeff*den_term
183
+ num_term = 1 + coeff.quo(num_term)
184
+
185
+ den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF
186
+ num_term = continued_fraction_cutoff(epsabs) if num_term.abs < CUTOFF
187
+
188
+ den_term = 1.quo(den_term)
189
+
190
+ delta_frac = den_term * num_term
191
+ cf *= delta_frac
192
+
193
+ coeff = -(a+k)*(a+b+k)*x.quo((a+2*k)*(a+2*k+1)) # coefficient for step 2
194
+ end
195
+
196
+ break if (delta_frac-1).abs < 2.0*Float::EPSILON
197
+ break if !epsabs.nil? && (cf * (delta_frac-1).abs < epsabs)
198
+ end
199
+
200
+ if k > MAX_ITER
201
+ raise("Exceeded maximum number of iterations") if epsabs.nil?
202
+ return with_error ? [0.0/0, 0] : 0.0/0 # NaN if epsabs is set
203
+ end
204
+
205
+ with_error ? [cf, k * 4 * Float::EPSILON * cf.abs] : cf
206
+ end
207
+
208
+
209
+ end
210
+
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,424 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files in the specfunc/ dir.
3
+
4
+ # require "statsample"
5
+
6
+ module Distribution
7
+ module MathExtension
8
+ module IncompleteGamma
9
+ NMAX = 5000
10
+ SMALL = Float::EPSILON ** 3
11
+ PG21 = -2.404113806319188570799476 # PolyGamma[2,1]
12
+
13
+ class << self
14
+
15
+ # Helper function for plot
16
+ #def range_to_array r
17
+ # r << (r.last - r.first)/100.0 if r.size == 2 # set dr as Dr/100.0
18
+ # arr = []
19
+ # pos = r[0]
20
+ # while pos <= r[1]
21
+ # arr << pos
22
+ # pos += r[2]
23
+ # end
24
+ # arr
25
+ #end
26
+ #
27
+ #def plot a, x_range, fun = :p
28
+ # x_range = range_to_array(x_range) if x_range.is_a?(Array)
29
+ # y_range = x_range.collect { |x| self.send(fun, a, x) }
30
+ # graph = Statsample::Graph::Scatterplot.new x_range.to_scale, y_range.to_scale
31
+ # f = File.new("test.svg", "w")
32
+ # f.puts(graph.to_svg)
33
+ # f.close
34
+ # `google-chrome test.svg`
35
+ #end
36
+
37
+ # The dominant part, D(a,x) := x^a e^(-x) / Gamma(a+1)
38
+ # gamma_inc_D in GSL-1.9.
39
+ def d(a, x, with_error = false)
40
+ error = nil
41
+ if a < 10.0
42
+ ln_a = Math.lgamma(a+1.0).first
43
+ lnr = a * Math.log(x) - x - ln_a
44
+ result = Math.exp(lnr)
45
+ error = 2.0 * Float::EPSILON * (lnr.abs + 1.0) + result.abs if with_error
46
+ with_error ? [result,error] : result
47
+ else
48
+ ln_term = ln_term_error = nil
49
+ if x < 0.5*a
50
+ u = x/a.to_f
51
+ ln_u = Math.log(u)
52
+ ln_term = ln_u - u + 1.0
53
+ ln_term_error = (ln_u.abs + u.abs + 1.0) * Float::EPSILON if with_error
54
+ else
55
+ mu = (x-a)/a.to_f
56
+ ln_term = Log::log_1plusx_minusx(mu, with_error)
57
+ ln_term, ln_term_error = ln_term if with_error
58
+ end
59
+ gstar = Gammastar.evaluate(a, with_error)
60
+ gstar,gstar_error = gstar if with_error
61
+ term1 = Math.exp(a*ln_term) / Math.sqrt(2.0*Math::PI*a)
62
+ result = term1/gstar
63
+ error = 2.0*Float::EPSILON*((a*ln_term).abs+1.0) * result.abs + gstar_error/gstar.abs * result.abs if with_error
64
+ with_error ? [result,error] : result
65
+ end
66
+ end
67
+
68
+ # gamma_inc_P_series
69
+ def p_series(a,x,with_error=false)
70
+ d = d(a,x,with_error)
71
+ d, d_err = d if with_error
72
+ sum = 1.0
73
+ term = 1.0
74
+ n = 1
75
+ 1.upto(NMAX-1) do |n|
76
+ term *= x / (a+n).to_f
77
+ sum += term
78
+ break if (term/sum).abs < Float::EPSILON
79
+ end
80
+
81
+ result = d * sum
82
+
83
+ if n == NMAX
84
+ STDERR.puts("Error: n reached NMAX in p series")
85
+ else
86
+ return with_error ? [result,d_err * sum.abs + (1.0+n)*Float::EPSILON * result.abs] : result
87
+ end
88
+ end
89
+
90
+ # This function does not exist in GSL, but is nonetheless GSL code. It's for calculating two specific ranges of p.
91
+ def q_asymptotic_uniform_complement a,x,with_error=false
92
+ q = q_asymptotic_uniform(a, x, with_error)
93
+ q,q_err = q if with_error
94
+ result = 1.0 - q
95
+ return with_error ? [result, q_err + 2.0*Float::EPSILON*result.abs] : result
96
+ end
97
+
98
+ def q_continued_fraction_complement a,x,with_error=false
99
+ q = q_continued_fraction(a,x,with_error)
100
+ return with_error ? [1.0 - q.first, q.last + 2.0*Float::EPSILON*(1.0-q.first).abs] : 1.0 - q
101
+ end
102
+
103
+ def q_large_x_complement a,x,with_error=false
104
+ q = q_large_x(a,x,with_error)
105
+ return with_error ? [1.0 - q.first, q.last + 2.0*Float::EPSILON*(1.0-q.first).abs] : 1.0 - q
106
+ end
107
+
108
+ # The incomplete gamma function.
109
+ # gsl_sf_gamma_inc_P_e
110
+ def p a,x,with_error=false
111
+ raise(ArgumentError, "Range Error: a must be positive, x must be non-negative") if a <= 0.0 || x < 0.0
112
+ if x == 0.0
113
+ return with_error ? [0.0, 0.0] : 0.0
114
+ elsif x < 20.0 || x < 0.5*a
115
+ return p_series(a, x, with_error)
116
+ elsif a > 1e6 && (x-a)*(x-a) < a
117
+ return q_asymptotic_uniform_complement a, x, with_error
118
+ elsif a <= x
119
+ if a > 0.2*x
120
+ return q_continued_fraction_complement(a, x, with_error)
121
+ else
122
+ return q_large_x_complement(a, x, with_error)
123
+ end
124
+ elsif (x-a)*(x-a) < a
125
+ return q_asymptotic_uniform_complement a, x, with_error
126
+ else
127
+ return p_series(a, x, with_error)
128
+ end
129
+ end
130
+
131
+ # gamma_inc_Q_e
132
+ def q a,x,with_error=false
133
+ raise(ArgumentError, "Range Error: a and x must be non-negative") if (a < 0.0 || x < 0.0)
134
+ if x == 0.0
135
+ return with_error ? [1.0, 0.0] : 1.0
136
+ elsif a == 0.0
137
+ return with_error ? [0.0, 0.0] : 0.0
138
+ elsif x <= 0.5*a
139
+ # If series is quick, do that.
140
+ p = p_series(a,x, with_error)
141
+ p,p_err = p if with_error
142
+ result = 1.0 - p
143
+ return with_error ? [result, p_err + 2.0*Float::EPSILON*result.abs] : result
144
+ elsif a >= 1.0e+06 && (x-a)*(x-a) < a # difficult asymptotic regime, only way to do this region
145
+ return q_asymptotic_uniform(a, x, with_error)
146
+ elsif a < 0.2 && x < 5.0
147
+ return q_series(a,x, with_error)
148
+ elsif a <= x
149
+ return x <= 1.0e+06 ? q_continued_fraction(a, x, with_error) : q_large_x(a, x, with_error)
150
+ else
151
+ if x > a-Math.sqrt(a)
152
+ return q_continued_fraction(a, x, with_error)
153
+ else
154
+ p = p_series(a, x, with_error)
155
+ p, p_err = p if with_error
156
+ result = 1.0 - p
157
+ return with_error ? [result, p_err + 2.0*Float::EPSILON*result.abs] : result
158
+ end
159
+ end
160
+ end
161
+
162
+ # gamma_inc_Q_CF
163
+ def q_continued_fraction a, x, with_error=false
164
+ d = d(a, x, with_error)
165
+ f = f_continued_fraction(a, x, with_error)
166
+
167
+ if with_error
168
+ [d.first*(a/x).to_f*f.first, d.last * ((a/x).to_f*f.first).abs + (d.first*a/x*f.last).abs]
169
+ else
170
+ d * (a/x).to_f * f
171
+ end
172
+ end
173
+
174
+ # gamma_inc_Q_large_x in GSL-1.9
175
+ def q_large_x a,x,with_error=false
176
+ d = d(a,x,with_error)
177
+ d,d_err = d if with_error
178
+ sum = 1.0
179
+ term = 1.0
180
+ last = 1.0
181
+ n = 1
182
+ 1.upto(NMAX-1).each do |n|
183
+ term *= (a-n)/x
184
+ break if (term/last).abs > 1.0
185
+ break if (term/sum).abs < Float::EPSILON
186
+ sum += term
187
+ last = term
188
+ end
189
+
190
+ result = d*(a/x)*sum
191
+ error = d_err * (a/x).abs * sum if with_error
192
+
193
+ if n == NMAX
194
+ STDERR.puts("Error: n reached NMAX in q_large_x")
195
+ else
196
+ return with_error ? [result,error] : result
197
+ end
198
+ end
199
+
200
+ # Uniform asymptotic for x near a, a and x large
201
+ # gamma_inc_Q_asymp_unif
202
+ def q_asymptotic_uniform(a, x, with_error = false)
203
+ rta = Math.sqrt(a)
204
+ eps = (x-a).quo(a)
205
+
206
+ ln_term = Log::log_1plusx_minusx(eps, with_error)
207
+ ln_term, ln_term_err = ln_term if with_error
208
+
209
+ eta = (eps >= 0 ? 1 : -1) * Math.sqrt(-2*ln_term)
210
+
211
+ erfc = Math.erfc_e(eta*rta/SQRT2, with_error)
212
+ erfc, erfc_err = erfc if with_error
213
+
214
+ c0 = c1 = nil
215
+ if eps.abs < ROOT5_FLOAT_EPSILON
216
+ c0 = -1.quo(3) + eps*(1.quo(12) - eps*(23.quo(540) - eps*(353.quo(12960) - eps*589.quo(30240))))
217
+ c1 = -1.quo(540) - eps.quo(288)
218
+ else
219
+ rt_term = Math.sqrt(-2 * ln_term.quo(eps*eps))
220
+ lam = x.quo(a)
221
+ c0 = (1 - 1/rt_term)/eps
222
+ c1 = -(eta**3 * (lam*lam + 10*lam + 1) - 12*eps**3).quo(12 * eta**3 * eps**3)
223
+ end
224
+
225
+ r = Math.exp(-0.5*a*eta*eta) / (SQRT2*SQRTPI*rta) * (c0 + c1.quo(a))
226
+
227
+ result = 0.5 * erfc + r
228
+ with_error ? [result, Float::EPSILON + (r*0.5*a*eta*eta).abs + 0.5*erfc_err + 2.0*Float::EPSILON + result.abs] : result
229
+ end
230
+
231
+ # gamma_inc_F_CF
232
+ def f_continued_fraction a, x, with_error = false
233
+ hn = 1.0 # convergent
234
+ cn = 1.0 / SMALL
235
+ dn = 1.0
236
+ n = 2
237
+ 2.upto(NMAX-1).each do |n|
238
+ an = n.odd? ? 0.5*(n-1)/x : (0.5*n-a)/x
239
+ dn = 1.0 + an * dn
240
+ dn = SMALL if dn.abs < SMALL
241
+ cn = 1.0 + an / cn
242
+ cn = SMALL if cn.abs < SMALL
243
+ dn = 1.0 / dn
244
+ delta = cn * dn
245
+ hn *= delta
246
+ break if (delta-1.0).abs < Float::EPSILON
247
+ end
248
+
249
+ if n == NMAX
250
+ STDERR.puts("Error: n reached NMAX in f continued fraction")
251
+ else
252
+ with_error ? [hn,2.0*Float::EPSILON * hn.abs + Float::EPSILON*(2.0+0.5*n) * hn.abs] : hn
253
+ end
254
+ end
255
+
256
+ def q_series(a,x,with_error=false)
257
+ term1 = nil
258
+ sum = nil
259
+ term2 = nil
260
+ begin
261
+ lnx = Math.log(x)
262
+ el = EULER + lnx
263
+ c1 = -el
264
+ c2 = Math::PI * Math::PI / 12.0 - 0.5*el*el
265
+ c3 = el*(Math::PI*Math::PI/12.0 - el*el/6.0) + PG21/6.0
266
+ c4 = -0.04166666666666666667 *
267
+ (-1.758243446661483480 + lnx) *
268
+ (-0.764428657272716373 + lnx) *
269
+ ( 0.723980571623507657 + lnx) *
270
+ ( 4.107554191916823640 + lnx)
271
+ c5 = -0.0083333333333333333 *
272
+ (-2.06563396085715900 + lnx) *
273
+ (-1.28459889470864700 + lnx) *
274
+ (-0.27583535756454143 + lnx) *
275
+ ( 1.33677371336239618 + lnx) *
276
+ ( 5.17537282427561550 + lnx)
277
+ c6 = -0.0013888888888888889 *
278
+ (-2.30814336454783200 + lnx) *
279
+ (-1.65846557706987300 + lnx) *
280
+ (-0.88768082560020400 + lnx) *
281
+ ( 0.17043847751371778 + lnx) *
282
+ ( 1.92135970115863890 + lnx) *
283
+ ( 6.22578557795474900 + lnx)
284
+ c7 = -0.00019841269841269841
285
+ (-2.5078657901291800 + lnx) *
286
+ (-1.9478900888958200 + lnx) *
287
+ (-1.3194837322612730 + lnx) *
288
+ (-0.5281322700249279 + lnx) *
289
+ ( 0.5913834939078759 + lnx) *
290
+ ( 2.4876819633378140 + lnx) *
291
+ ( 7.2648160783762400 + lnx)
292
+ c8 = -0.00002480158730158730 *
293
+ (-2.677341544966400 + lnx) *
294
+ (-2.182810448271700 + lnx) *
295
+ (-1.649350342277400 + lnx) *
296
+ (-1.014099048290790 + lnx) *
297
+ (-0.191366955370652 + lnx) *
298
+ ( 0.995403817918724 + lnx) *
299
+ ( 3.041323283529310 + lnx) *
300
+ ( 8.295966556941250 + lnx) *
301
+ c9 = -2.75573192239859e-6 *
302
+ (-2.8243487670469080 + lnx) *
303
+ (-2.3798494322701120 + lnx) *
304
+ (-1.9143674728689960 + lnx) *
305
+ (-1.3814529102920370 + lnx) *
306
+ (-0.7294312810261694 + lnx) *
307
+ ( 0.1299079285269565 + lnx) *
308
+ ( 1.3873333251885240 + lnx) *
309
+ ( 3.5857258865210760 + lnx) *
310
+ ( 9.3214237073814600 + lnx) *
311
+ c10 = -2.75573192239859e-7 *
312
+ (-2.9540329644556910 + lnx) *
313
+ (-2.5491366926991850 + lnx) *
314
+ (-2.1348279229279880 + lnx) *
315
+ (-1.6741881076349450 + lnx) *
316
+ (-1.1325949616098420 + lnx) *
317
+ (-0.4590034650618494 + lnx) *
318
+ ( 0.4399352987435699 + lnx) *
319
+ ( 1.7702236517651670 + lnx) *
320
+ ( 4.1231539047474080 + lnx) *
321
+ ( 10.342627908148680 + lnx)
322
+ term1 = a*(c1+a*(c2+a*(c3+a*(c4+a*(c5+a*(c6+a*(c7+a*(c8+a*(c9+a*c10)))))))))
323
+ end
324
+
325
+ n = 1
326
+ begin
327
+ t = 1.0
328
+ sum = 1.0
329
+ 1.upto(NMAX-1).each do |n|
330
+ t *= -x/(n+1.0)
331
+ sum += (a+1.0) / (a+n+1.0) * t
332
+ break if (t/sum).abs < Float::EPSILON
333
+ end
334
+ end
335
+
336
+ if n == NMAX
337
+ STDERR.puts("Error: n reached NMAX in q_series")
338
+ else
339
+ term2 = (1.0 - term1) * a/(a+1.0) * x * sum
340
+ result = term1+term2
341
+ with_error ? [result, Float::EPSILON*term1.abs + 2.0*term2.abs + 2.0*Float::EPSILON*result.abs] : result
342
+ end
343
+ end
344
+
345
+ # gamma_inc_series
346
+ def series a,x,with_error = false
347
+ q = q_series(a,x,with_error)
348
+ g = Math.gamma(a)
349
+ STDERR.puts("Warning: Don't know error for Math.gamma. Error will be incorrect") if with_error
350
+ # When we get the error from Gamma, switch the comment on the next to lines
351
+ # with_error ? [q.first*g.first, (q.first*g.last).abs + (q.last*g.first).abs + 2.0*Float::EPSILON*(q.first*g.first).abs] : q*g
352
+ with_error ? [q.first*g, (q.first*Float::EPSILON).abs + (q.last*g.first).abs + 2.0*Float::EPSILON(q.first*g).abs] : q*g
353
+ end
354
+
355
+ # gamma_inc_a_gt_0
356
+ def a_greater_than_0 a, x, with_error = false
357
+ q = q(a,x,with_error)
358
+ q,q_err = q if with_error
359
+ g = Math.gamma(a)
360
+ STDERR.puts("Warning: Don't know error for Math.gamma. Error will be incorrect") if with_error
361
+ g_err = Float::EPSILON
362
+ result = g*q
363
+ error = (g*q_err).abs + (g_err*q).abs if with_error
364
+ with_error ? [result,error] : result
365
+ end
366
+
367
+ # gamma_inc_CF
368
+ def continued_fraction a,x, with_error=false
369
+ f = f_continued_fraction(a,x,with_error)
370
+ f,f_error = f if with_error
371
+ pre = Math.exp((a-1.0)*Math.log(x) - x)
372
+ STDERR.puts("Warning: Don't know error for Math.exp. Error will be incorrect") if with_error
373
+ pre_error = Float::EPSILON
374
+ result = f*pre
375
+ if with_error
376
+ error = (f_error*pre).abs + (f*pre_error) + (2.0+a.abs)*Float::EPSILON*result.abs
377
+ [result,error]
378
+ else
379
+ result
380
+ end
381
+ end
382
+
383
+ # Unnormalized incomplete gamma function.
384
+ # gsl_sf_gamma_inc_e
385
+ def unnormalized a,x,with_error = false
386
+ raise(ArgumentError, "x cannot be negative") if x < 0.0
387
+
388
+ if x == 0.0
389
+ result = Math.gamma(a.to_f)
390
+ STDERR.puts("Warning: Don't know error for Math.gamma. Error will be incorrect") if with_error
391
+ return with_error ? [result, Float::EPSILON] : result
392
+ elsif a == 0.0
393
+ return ExponentialIntegral.first_order(x.to_f, with_error)
394
+ elsif a > 0.0
395
+ return a_greater_than_0(a.to_f, x.to_f, with_error)
396
+ elsif x > 0.25
397
+ # continued fraction seems to fail for x too small
398
+ return continued_fraction(a.to_f, x.to_f, with_error)
399
+ elsif a.abs < 0.5
400
+ return series(a.to_f,x.to_f,with_error)
401
+ else
402
+ fa = a.floor.to_f
403
+ da = a - fa
404
+ g_da = da > 0.0 ? a_greater_than_0(da, x.to_f, with_error) : ExponentialIntegral.first_order(x.to_f, with_error)
405
+ g_da, g_da_err = g_da if with_error
406
+ alpha = da
407
+ gax = g_da
408
+
409
+ # Gamma(alpha-1,x) = 1/(alpha-1) (Gamma(a,x) - x^(alpha-1) e^-x)
410
+ begin
411
+ shift = Math.exp(-x + (alpha-1.0)*Math.log(x))
412
+ gax = (gax-shift) / (alpha-1.0)
413
+ alpha -= 1.0
414
+ end while alpha > a
415
+
416
+ result = gax
417
+ return with_error ? [result, 2.0*(1.0 + a.abs) * Float::EPSILON*gax.abs] : result
418
+ end
419
+ end
420
+
421
+ end
422
+ end
423
+ end
424
+ end