distribution 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.travis.yml +4 -6
- data/.yardopts +5 -0
- data/History.txt +3 -0
- data/README.md +87 -44
- data/benchmark/binomial_coefficient.rb +19 -23
- data/benchmark/binomial_coefficient/experiment.rb +33 -36
- data/benchmark/factorial_hash.rb +7 -8
- data/benchmark/factorial_method.rb +4 -6
- data/benchmark/odd.rb +6 -7
- data/benchmark/power.rb +11 -11
- data/bin/distribution +26 -26
- data/distribution.gemspec +3 -4
- data/lib/distribution.rb +55 -96
- data/lib/distribution/beta/gsl.rb +10 -5
- data/lib/distribution/beta/ruby.rb +3 -1
- data/lib/distribution/binomial/ruby.rb +5 -2
- data/lib/distribution/bivariatenormal.rb +4 -5
- data/lib/distribution/bivariatenormal/gsl.rb +2 -2
- data/lib/distribution/bivariatenormal/java.rb +1 -1
- data/lib/distribution/bivariatenormal/ruby.rb +245 -254
- data/lib/distribution/chisquare.rb +8 -10
- data/lib/distribution/chisquare/gsl.rb +24 -19
- data/lib/distribution/chisquare/java.rb +1 -1
- data/lib/distribution/chisquare/ruby.rb +25 -25
- data/lib/distribution/chisquare/statistics2.rb +16 -13
- data/lib/distribution/distributable.rb +40 -0
- data/lib/distribution/exponential.rb +4 -5
- data/lib/distribution/exponential/gsl.rb +13 -9
- data/lib/distribution/exponential/ruby.rb +14 -9
- data/lib/distribution/f.rb +1 -1
- data/lib/distribution/f/gsl.rb +26 -22
- data/lib/distribution/f/java.rb +1 -1
- data/lib/distribution/f/ruby.rb +16 -19
- data/lib/distribution/f/statistics2.rb +22 -19
- data/lib/distribution/gamma.rb +5 -7
- data/lib/distribution/gamma/gsl.rb +13 -9
- data/lib/distribution/gamma/java.rb +1 -1
- data/lib/distribution/gamma/ruby.rb +5 -11
- data/lib/distribution/hypergeometric.rb +5 -8
- data/lib/distribution/hypergeometric/gsl.rb +4 -5
- data/lib/distribution/hypergeometric/java.rb +1 -1
- data/lib/distribution/hypergeometric/ruby.rb +34 -35
- data/lib/distribution/logistic.rb +5 -8
- data/lib/distribution/logistic/ruby.rb +13 -8
- data/lib/distribution/lognormal.rb +5 -7
- data/lib/distribution/lognormal/gsl.rb +8 -6
- data/lib/distribution/lognormal/ruby.rb +5 -9
- data/lib/distribution/math_extension.rb +6 -15
- data/lib/distribution/math_extension/chebyshev_series.rb +281 -272
- data/lib/distribution/math_extension/erfc.rb +26 -29
- data/lib/distribution/math_extension/exponential_integral.rb +17 -17
- data/lib/distribution/math_extension/gammastar.rb +19 -20
- data/lib/distribution/math_extension/gsl_utilities.rb +12 -12
- data/lib/distribution/math_extension/incomplete_beta.rb +52 -61
- data/lib/distribution/math_extension/incomplete_gamma.rb +166 -168
- data/lib/distribution/math_extension/log_utilities.rb +20 -22
- data/lib/distribution/normal.rb +11 -13
- data/lib/distribution/normal/gsl.rb +13 -10
- data/lib/distribution/normal/java.rb +14 -13
- data/lib/distribution/normal/ruby.rb +68 -58
- data/lib/distribution/normal/statistics2.rb +5 -2
- data/lib/distribution/normalmultivariate.rb +64 -64
- data/lib/distribution/poisson.rb +11 -13
- data/lib/distribution/poisson/gsl.rb +7 -7
- data/lib/distribution/poisson/java.rb +19 -24
- data/lib/distribution/poisson/ruby.rb +38 -9
- data/lib/distribution/shorthand.rb +17 -0
- data/lib/distribution/t.rb +13 -15
- data/lib/distribution/t/gsl.rb +27 -24
- data/lib/distribution/t/java.rb +1 -1
- data/lib/distribution/t/ruby.rb +99 -100
- data/lib/distribution/t/statistics2.rb +19 -19
- data/lib/distribution/uniform.rb +26 -0
- data/lib/distribution/uniform/gsl.rb +36 -0
- data/lib/distribution/uniform/ruby.rb +91 -0
- data/lib/distribution/version.rb +1 -1
- data/lib/distribution/weibull.rb +6 -7
- data/lib/distribution/weibull/gsl.rb +16 -16
- data/lib/distribution/weibull/ruby.rb +30 -23
- data/spec/beta_spec.rb +45 -47
- data/spec/binomial_spec.rb +77 -85
- data/spec/bivariatenormal_spec.rb +28 -35
- data/spec/chisquare_spec.rb +48 -52
- data/spec/distribution_spec.rb +10 -10
- data/spec/exponential_spec.rb +44 -49
- data/spec/f_spec.rb +4 -4
- data/spec/gamma_spec.rb +50 -53
- data/spec/hypergeometric_spec.rb +63 -69
- data/spec/logistic_spec.rb +32 -37
- data/spec/lognormal_spec.rb +25 -31
- data/spec/math_extension_spec.rb +192 -210
- data/spec/normal_spec.rb +80 -73
- data/spec/poisson_spec.rb +63 -41
- data/spec/shorthand_spec.rb +19 -22
- data/spec/spec_helper.rb +8 -9
- data/spec/t_spec.rb +63 -77
- data/spec/uniform_spec.rb +154 -0
- data/spec/weibull_spec.rb +13 -14
- metadata +17 -8
@@ -3,36 +3,35 @@
|
|
3
3
|
|
4
4
|
module Distribution
|
5
5
|
module MathExtension
|
6
|
-
|
7
6
|
# Error function from GSL-1.9, with epsilon information. Access it using Math.erfc_e
|
8
7
|
module Erfc
|
9
|
-
P = [
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Q = [
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
8
|
+
P = [2.97886562639399288862,
|
9
|
+
7.409740605964741794425,
|
10
|
+
6.1602098531096305440906,
|
11
|
+
5.019049726784267463450058,
|
12
|
+
1.275366644729965952479585264,
|
13
|
+
0.5641895835477550741253201704]
|
14
|
+
Q = [3.3690752069827527677,
|
15
|
+
9.608965327192787870698,
|
16
|
+
17.08144074746600431571095,
|
17
|
+
12.0489519278551290360340491,
|
18
|
+
9.396034016235054150430579648,
|
19
|
+
2.260528520767326969591866945,
|
20
|
+
1.0]
|
22
21
|
|
23
22
|
class << self
|
24
23
|
# Estimates erfc(x) valid for 8 < x < 100
|
25
24
|
# erfc8_sum from GSL-1.9
|
26
25
|
def erfc8_sum(x)
|
27
26
|
num = P[5]
|
28
|
-
4.downto(0) { |i| num = x*num + P[i] }
|
27
|
+
4.downto(0) { |i| num = x * num + P[i] }
|
29
28
|
den = Q[6]
|
30
|
-
5.downto(0) { |i| den = x*den + Q[i] }
|
29
|
+
5.downto(0) { |i| den = x * den + Q[i] }
|
31
30
|
num / den
|
32
31
|
end
|
33
32
|
|
34
33
|
def erfc8(x)
|
35
|
-
erfc8_sum(x) * Math.exp(-x*x)
|
34
|
+
erfc8_sum(x) * Math.exp(-x * x)
|
36
35
|
end
|
37
36
|
|
38
37
|
# gsl_sf_erfc_e
|
@@ -41,39 +40,37 @@ module Distribution
|
|
41
40
|
e = nil
|
42
41
|
|
43
42
|
if ax <= 1.0
|
44
|
-
t = 2*ax-1
|
43
|
+
t = 2 * ax - 1
|
45
44
|
e = ChebyshevSeries.evaluate(:erfc_xlt1, t, with_error)
|
46
45
|
elsif ax <= 5.0
|
47
|
-
ex2 = Math.exp(-x*x)
|
48
|
-
t = (ax-3).quo(2)
|
46
|
+
ex2 = Math.exp(-x * x)
|
47
|
+
t = (ax - 3).quo(2)
|
49
48
|
e = ChebyshevSeries.evaluate(:erfc_x15, t, with_error)
|
50
49
|
if with_error
|
51
50
|
e[0] *= ex2
|
52
|
-
e[1] = ex2 * (e[1] + 2*x.abs*Float::EPSILON)
|
51
|
+
e[1] = ex2 * (e[1] + 2 * x.abs * Float::EPSILON)
|
53
52
|
else
|
54
53
|
e *= ex2
|
55
54
|
end
|
56
55
|
elsif ax < 10.0
|
57
|
-
exterm = Math.exp(-x*x) / ax
|
58
|
-
t = (2*ax-15).quo(5)
|
56
|
+
exterm = Math.exp(-x * x) / ax
|
57
|
+
t = (2 * ax - 15).quo(5)
|
59
58
|
e = ChebyshevSeries.evaluate(:erfc_x510, t, with_error)
|
60
59
|
if with_error
|
61
60
|
e[0] *= exterm
|
62
|
-
e[1] = exterm * (e[1] + 2*x.abs*Float::EPSILON + Float::EPSILON)
|
61
|
+
e[1] = exterm * (e[1] + 2 * x.abs * Float::EPSILON + Float::EPSILON)
|
63
62
|
else
|
64
63
|
e *= exterm
|
65
64
|
end
|
66
65
|
else
|
67
66
|
e8 = erfc8(ax)
|
68
|
-
e = with_error ? [e8, (x*x + 1) * Float::EPSILON * e8.abs] : e8
|
67
|
+
e = with_error ? [e8, (x * x + 1) * Float::EPSILON * e8.abs] : e8
|
69
68
|
end
|
70
69
|
|
71
70
|
result = x < 0 ? 2 - (with_error ? e.first : e) : (with_error ? e.first : e)
|
72
|
-
with_error ? [result, e.last + 2*Float::EPSILON*result.abs] : result
|
71
|
+
with_error ? [result, e.last + 2 * Float::EPSILON * result.abs] : result
|
73
72
|
end
|
74
73
|
end
|
75
|
-
|
76
74
|
end
|
77
|
-
|
78
75
|
end
|
79
|
-
end
|
76
|
+
end
|
@@ -3,57 +3,57 @@ module Distribution
|
|
3
3
|
# From GSL-1.9.
|
4
4
|
module ExponentialIntegral
|
5
5
|
class << self
|
6
|
-
def first_order
|
6
|
+
def first_order(x, scale = 0, with_error = false)
|
7
7
|
xmaxt = -Math::LOG_FLOAT_MIN
|
8
8
|
xmax = xmaxt - Math.log(xmaxt)
|
9
9
|
result = nil
|
10
10
|
error = with_error ? nil : 0.0
|
11
11
|
|
12
12
|
if x < -xmax && !scale
|
13
|
-
|
13
|
+
fail('Overflow Error')
|
14
14
|
elsif x <= -10.0
|
15
|
-
s = 1.0 / x * (
|
16
|
-
result_c = ChebyshevSeries.eval(20.0/x+1.0, :ae11, with_error)
|
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
17
|
result_c, result_c_err = result_c if with_error
|
18
18
|
result = s * (1.0 + result_c)
|
19
|
-
error
|
19
|
+
error ||= (s * result_c_err) + 2.0 * Float::EPSILON * (x.abs + 1.0) * result.abs
|
20
20
|
elsif x <= -4.0
|
21
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)
|
22
|
+
result_c = ChebyshevSeries.eval((40.0 / x + 7.0) / 3.0, :ae12, with_error)
|
23
23
|
result_c, result_c_err = result_c if with_error
|
24
24
|
result = s * (1.0 + result_c)
|
25
|
-
error
|
25
|
+
error ||= (s * result_c_err) + 2.0 * Float::EPSILON * result.abs
|
26
26
|
elsif x <= -1.0
|
27
27
|
ln_term = - Math.log(x.abs)
|
28
28
|
scale_factor = scale ? Math.exp(x) : 1.0
|
29
|
-
result_c = ChebyshevSeries.eval((2.0*x+5.0)/3.0, :e11, with_error)
|
29
|
+
result_c = ChebyshevSeries.eval((2.0 * x + 5.0) / 3.0, :e11, with_error)
|
30
30
|
result_c, result_c_err = result_c if with_error
|
31
31
|
result = scale_factor * (ln_term + result_c)
|
32
|
-
error
|
32
|
+
error ||= scale_factor * (result_c_err + Float::EPSILON * ln_term.abs) + 2.0 * Float::EPSILON * result.abs
|
33
33
|
elsif x == 0.0
|
34
|
-
|
34
|
+
fail(ArgumentError, 'Domain Error')
|
35
35
|
elsif x <= 1.0
|
36
36
|
ln_term = - Math.log(x.abs)
|
37
37
|
scale_factor = scale ? Math.exp(x) : 1.0
|
38
38
|
result_c = ChebyshevSeries.eval(x, :e12, with_error)
|
39
39
|
result_c, result_c_err = result_c if with_error
|
40
40
|
result = scale_factor * (ln_term - 0.6875 + x + result_c)
|
41
|
-
error
|
41
|
+
error ||= scale_factor * (result_c_err + Float::EPSILON * ln_term.abs) + 2.0 * Float::EPSILON * result.abs
|
42
42
|
elsif x <= 4.0
|
43
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)
|
44
|
+
result_c = ChebyshevSeries.eval((8.0 / x - 5.0) / 3.0, :ae13, with_error)
|
45
45
|
result_c, result_c_err = result_c if with_error
|
46
46
|
result = s * (1.0 + result_c)
|
47
|
-
error
|
47
|
+
error ||= (s * result_c_err) + 2.0 * Float::EPSILON * result.abs
|
48
48
|
elsif x <= xmax || scale
|
49
49
|
s = 1.0 / x * (scale ? 1.0 : Math.exp(-x))
|
50
|
-
result_c = ChebyshevSeries.eval(8.0/x-1.0, :ae14, with_error)
|
50
|
+
result_c = ChebyshevSeries.eval(8.0 / x - 1.0, :ae14, with_error)
|
51
51
|
result_c, result_c_err = result_c if with_error
|
52
52
|
result = s * (1.0 + result_c)
|
53
|
-
error
|
54
|
-
|
53
|
+
error ||= s * (Float::EPSILON + result_c_err) + 2.0 * (x + 1.0) * Float::EPSILON * result.abs
|
54
|
+
fail('Underflow Error') if result == 0.0
|
55
55
|
else
|
56
|
-
|
56
|
+
fail('Underflow Error')
|
57
57
|
end
|
58
58
|
with_error ? [result, error] : result
|
59
59
|
end
|
@@ -10,52 +10,51 @@ module Distribution
|
|
10
10
|
C2 = 1.quo(1260)
|
11
11
|
C3 = -1.quo(1680)
|
12
12
|
C4 = 1.quo(1188)
|
13
|
-
C5 = -691.quo(
|
13
|
+
C5 = -691.quo(360_360)
|
14
14
|
C6 = 1.quo(156)
|
15
|
-
C7 = -3617.quo(
|
15
|
+
C7 = -3617.quo(122_400)
|
16
16
|
|
17
17
|
class << self
|
18
|
-
|
19
|
-
def series x, with_error = false
|
18
|
+
def series(x, with_error = false)
|
20
19
|
# Use the Stirling series for the correction to Log(Gamma(x)),
|
21
20
|
# which is better behaved and easier to compute than the
|
22
21
|
# 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
|
22
|
+
y = 1.quo(x * x)
|
23
|
+
ser = C0 + y * (C1 + y * (C2 + y * (C3 + y * (C4 + y * (C5 + y * (C6 + y * C7))))))
|
24
|
+
result = Math.exp(ser / x)
|
25
|
+
with_error ? [result, 2.0 * Float::EPSILON * result * [1, ser / x].max] : result
|
27
26
|
end
|
28
27
|
|
29
|
-
def evaluate
|
30
|
-
|
28
|
+
def evaluate(x, with_error = false)
|
29
|
+
fail(ArgumentError, 'x must be positive') if x <= 0
|
31
30
|
if x < 0.5
|
32
31
|
STDERR.puts("Warning: Don't know error on lg_x, error for this function will be incorrect") if with_error
|
33
32
|
lg = Math.lgamma(x).first
|
34
33
|
lg_err = Float::EPSILON # Guess
|
35
34
|
lx = Math.log(x)
|
36
35
|
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)
|
36
|
+
lnr_val = lg - (x - 0.5) * lx + x - c
|
37
|
+
lnr_err = lg_err + 2.0 * Float::EPSILON * ((x + 0.5) * lx.abs + c)
|
39
38
|
with_error ? exp_err(lnr_val, lnr_err) : Math.exp(lnr_val)
|
40
39
|
elsif x < 2.0
|
41
|
-
t = 4.0/3.0*(x-0.5) - 1.0
|
40
|
+
t = 4.0 / 3.0 * (x - 0.5) - 1.0
|
42
41
|
ChebyshevSeries.evaluate(:gstar_a, t, with_error)
|
43
42
|
elsif x < 10.0
|
44
|
-
t = 0.25*(x-2.0) - 1.0
|
43
|
+
t = 0.25 * (x - 2.0) - 1.0
|
45
44
|
c = ChebyshevSeries.evaluate(:gstar_b, t, with_error)
|
46
45
|
c, c_err = c if with_error
|
47
46
|
|
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
|
47
|
+
result = c / (x * x) + 1.0 + 1.0 / (12.0 * x)
|
48
|
+
with_error ? [result, c_err / (x * x) + 2.0 * Float::EPSILON * result.abs] : result
|
49
|
+
elsif x < 1.0 / Math::ROOT4_FLOAT_EPSILON
|
51
50
|
series x, with_error
|
52
51
|
elsif x < 1.0 / Float::EPSILON # Stirling
|
53
52
|
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)))
|
53
|
+
result = 1.0 + xi / 12.0 * (1.0 + xi / 24.0 * (1.0 - xi * (139.0 / 180.0 + 571.0 / 8640.0 * xi)))
|
55
54
|
result_err = 2.0 * Float::EPSILON * result.abs
|
56
|
-
with_error ? [result,result_err] : result
|
55
|
+
with_error ? [result, result_err] : result
|
57
56
|
else
|
58
|
-
with_error ? [1.0,1.0/x] : 1.0
|
57
|
+
with_error ? [1.0, 1.0 / x] : 1.0
|
59
58
|
end
|
60
59
|
end
|
61
60
|
end
|
@@ -8,14 +8,14 @@ module Distribution
|
|
8
8
|
SQRT2 = 1.41421356237309504880168872421
|
9
9
|
SQRTPI = 1.77245385090551602729816748334
|
10
10
|
|
11
|
-
ROOT3_FLOAT_MIN = Float::MIN
|
12
|
-
ROOT3_FLOAT_EPSILON = Float::EPSILON
|
13
|
-
ROOT4_FLOAT_MIN = Float::MIN
|
14
|
-
ROOT4_FLOAT_EPSILON = Float::EPSILON
|
15
|
-
ROOT5_FLOAT_MIN = Float::MIN
|
16
|
-
ROOT5_FLOAT_EPSILON = Float::EPSILON
|
17
|
-
ROOT6_FLOAT_MIN = Float::MIN
|
18
|
-
ROOT6_FLOAT_EPSILON = Float::EPSILON
|
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
19
|
LOG_FLOAT_MIN = Math.log(Float::MIN)
|
20
20
|
EULER = 0.57721566490153286060651209008
|
21
21
|
|
@@ -23,9 +23,9 @@ module Distribution
|
|
23
23
|
# gsl_sf_exp_err_e
|
24
24
|
def exp_err(x, dx)
|
25
25
|
adx = dx.abs
|
26
|
-
|
27
|
-
|
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]
|
26
|
+
fail('Overflow Error in exp_err: x + adx > LOG_FLOAT_MAX') if x + adx > LOG_FLOAT_MAX
|
27
|
+
fail('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
29
|
end
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
@@ -7,37 +7,37 @@ module Distribution
|
|
7
7
|
class << self
|
8
8
|
# Based on gsl_sf_lnbeta_e and gsl_sf_lnbeta_sgn_e
|
9
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)
|
10
|
+
def log_beta(x, y, with_error = false)
|
11
11
|
sign = nil
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
fail(ArgumentError, 'x and y must be nonzero') if x == 0.0 || y == 0.0
|
14
|
+
fail(ArgumentError, 'not defined for negative integers') if [x, y].any? { |v| v < 0 }
|
15
15
|
|
16
16
|
# See if we can handle the positive case with min/max < 0.2
|
17
17
|
if x > 0 && y > 0
|
18
|
-
min, max = [x,y].minmax
|
18
|
+
min, max = [x, y].minmax
|
19
19
|
ratio = min.quo(max)
|
20
20
|
|
21
21
|
if ratio < 0.2
|
22
22
|
gsx = Gammastar.evaluate(x, with_error)
|
23
23
|
gsy = Gammastar.evaluate(y, with_error)
|
24
|
-
gsxy = Gammastar.evaluate(x+y, with_error)
|
25
|
-
lnopr = Log
|
24
|
+
gsxy = Gammastar.evaluate(x + y, with_error)
|
25
|
+
lnopr = Log.log_1plusx(ratio, with_error)
|
26
26
|
|
27
|
-
gsx, gsx_err, gsy, gsy_err, gsxy, gsxy_err, lnopr, lnopr_err = [gsx,gsy,gsxy,lnopr].flatten if with_error
|
27
|
+
gsx, gsx_err, gsy, gsy_err, gsxy, gsxy_err, lnopr, lnopr_err = [gsx, gsy, gsxy, lnopr].flatten if with_error
|
28
28
|
|
29
|
-
lnpre = Math.log((gsx*gsy).quo(gsxy) * Math::SQRT2 * Math::SQRTPI)
|
29
|
+
lnpre = Math.log((gsx * gsy).quo(gsxy) * Math::SQRT2 * Math::SQRTPI)
|
30
30
|
lnpre_err = gsx_err.quo(gsx) + gsy_err(gsy) + gsxy_err.quo(gsxy) if with_error
|
31
31
|
|
32
32
|
t1 = min * Math.log(ratio)
|
33
33
|
t2 = 0.5 * Math.log(min)
|
34
|
-
t3 = (x+y-0.5)*lnopr
|
34
|
+
t3 = (x + y - 0.5) * lnopr
|
35
35
|
|
36
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
|
37
|
+
lnpow_err = Float::EPSILON * (t1.abs + t2.abs + t3.abs) + (x + y - 0.5).abs * lnopr_err if with_error
|
38
38
|
|
39
39
|
result = lnpre + lnpow
|
40
|
-
error = lnpre_err + lnpow_err + 2.0*Float::EPSILON*result.abs if with_error
|
40
|
+
error = lnpre_err + lnpow_err + 2.0 * Float::EPSILON * result.abs if with_error
|
41
41
|
|
42
42
|
return with_error ? [result, 1.0, error] : [result, 1.0]
|
43
43
|
end
|
@@ -46,84 +46,80 @@ module Distribution
|
|
46
46
|
# General case: fallback
|
47
47
|
lgx, sgx = Math.lgamma(x)
|
48
48
|
lgy, sgy = Math.lgamma(y)
|
49
|
-
lgxy, sgxy = Math.lgamma(x+y)
|
49
|
+
lgxy, sgxy = Math.lgamma(x + y)
|
50
50
|
sgn = sgx * sgy * sgxy
|
51
51
|
|
52
|
-
|
52
|
+
fail('Domain error: sign is -') if sgn == -1
|
53
53
|
|
54
54
|
result = lgx + lgy - lgxy
|
55
55
|
if with_error
|
56
56
|
lgx_err, lgy_err, lgxy_err = begin
|
57
|
-
STDERR.puts(
|
57
|
+
STDERR.puts('Warning: Error is unknown for Math::lgamma, guessing.')
|
58
58
|
[Math::EPSILON, Math::EPSILON, Math::EPSILON]
|
59
59
|
end
|
60
60
|
|
61
|
-
error = lgx_err + lgy_err + lgxy_err + Float::EPSILON*(lgx.abs+lgy.abs+lgxy.abs) + 2.0*(Float::EPSILON)*result.abs
|
61
|
+
error = lgx_err + lgy_err + lgxy_err + Float::EPSILON * (lgx.abs + lgy.abs + lgxy.abs) + 2.0 * (Float::EPSILON) * result.abs
|
62
62
|
return [result, sgn, error]
|
63
63
|
else
|
64
64
|
return [result, sgn]
|
65
65
|
end
|
66
|
-
|
67
66
|
end
|
68
67
|
end
|
69
68
|
end
|
70
69
|
# Calculate regularized incomplete beta function
|
71
70
|
module IncompleteBeta
|
72
|
-
|
73
71
|
MAX_ITER = 512
|
74
72
|
CUTOFF = 2.0 * Float::MIN
|
75
73
|
|
76
74
|
class << self
|
77
|
-
|
78
75
|
# Evaluate aa * beta_inc(a,b,x) + yy
|
79
76
|
#
|
80
77
|
# No error mode available.
|
81
78
|
#
|
82
79
|
# 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
|
80
|
+
def axpy(aa, yy, a, b, x)
|
81
|
+
return aa * 0 + yy if x == 0.0
|
82
|
+
return aa * 1 + yy if x == 1.0
|
86
83
|
|
87
84
|
ln_beta = Math.logbeta(a, b)
|
88
|
-
ln_pre = -ln_beta
|
85
|
+
ln_pre = -ln_beta + a * Math.log(x) + b * Math::Log.log1p(-x)
|
89
86
|
prefactor = Math.exp(ln_pre)
|
90
87
|
|
91
|
-
if x < (a+1).quo(a+b+2)
|
88
|
+
if x < (a + 1).quo(a + b + 2)
|
92
89
|
# Apply continued fraction directly
|
93
90
|
epsabs = yy.quo((aa * prefactor).quo(a)).abs * Float::EPSILON
|
94
91
|
cf = continued_fraction(a, b, x, epsabs)
|
95
92
|
return aa * (prefactor * cf).quo(a) + yy
|
96
93
|
else
|
97
94
|
# Apply continued fraction after hypergeometric transformation
|
98
|
-
epsabs = (aa + yy).quo(
|
99
|
-
cf = continued_fraction(b, a, 1-x, epsabs)
|
95
|
+
epsabs = (aa + yy).quo((aa * prefactor).quo(b)) * Float::EPSILON
|
96
|
+
cf = continued_fraction(b, a, 1 - x, epsabs)
|
100
97
|
term = (prefactor * cf).quo(b)
|
101
|
-
return aa == -yy ? -aa*term : aa*(1-term)+yy
|
98
|
+
return aa == -yy ? -aa * term : aa * (1 - term) + yy
|
102
99
|
end
|
103
100
|
end
|
104
101
|
|
105
|
-
|
106
102
|
# Evaluate the incomplete beta function
|
107
103
|
# gsl_sf_beta_inc_e
|
108
|
-
def evaluate(a,b,x,with_error=false)
|
109
|
-
|
104
|
+
def evaluate(a, b, x, with_error = false)
|
105
|
+
fail(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
106
|
if x == 0
|
111
|
-
return with_error ? [0.0,0.0] : 0.0
|
107
|
+
return with_error ? [0.0, 0.0] : 0.0
|
112
108
|
elsif x == 1
|
113
|
-
return with_error ? [1.0,0.0] : 1.0
|
109
|
+
return with_error ? [1.0, 0.0] : 1.0
|
114
110
|
else
|
115
111
|
|
116
|
-
ln_beta = Beta.log_beta(a,b, with_error)
|
112
|
+
ln_beta = Beta.log_beta(a, b, with_error)
|
117
113
|
ln_1mx = Log.log_1plusx(-x, with_error)
|
118
114
|
ln_x = Math.log(x)
|
119
115
|
|
120
116
|
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
|
117
|
+
# STDERR.puts("Warning: Error is unknown for Math::log, guessing.")
|
118
|
+
[ln_beta, ln_1mx, Float::EPSILON].flatten
|
123
119
|
end
|
124
120
|
|
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
|
121
|
+
ln_pre = -ln_beta + a * ln_x + b * ln_1mx
|
122
|
+
ln_pre_err = ln_beta_err + (a * ln_x_err).abs + (b * ln_1mx_err).abs if with_error
|
127
123
|
|
128
124
|
prefactor, prefactor_err = begin
|
129
125
|
if with_error
|
@@ -133,31 +129,30 @@ module Distribution
|
|
133
129
|
end
|
134
130
|
end
|
135
131
|
|
136
|
-
if x < (a+1).quo(a+b+2)
|
132
|
+
if x < (a + 1).quo(a + b + 2)
|
137
133
|
# Apply continued fraction directly
|
138
|
-
|
139
|
-
cf = continued_fraction(a,b,x, nil, with_error)
|
140
|
-
cf,cf_err = cf if with_error
|
134
|
+
|
135
|
+
cf = continued_fraction(a, b, x, nil, with_error)
|
136
|
+
cf, cf_err = cf if with_error
|
141
137
|
result = (prefactor * cf).quo(a)
|
142
|
-
return with_error ? [result, ((prefactor_err*cf).abs + (prefactor*cf_err).abs).quo(a)] : result
|
138
|
+
return with_error ? [result, ((prefactor_err * cf).abs + (prefactor * cf_err).abs).quo(a)] : result
|
143
139
|
else
|
144
140
|
# Apply continued fraction after hypergeometric transformation
|
145
141
|
|
146
|
-
cf = continued_fraction(b, a, 1-x, nil)
|
147
|
-
cf,cf_err = cf if with_error
|
142
|
+
cf = continued_fraction(b, a, 1 - x, nil)
|
143
|
+
cf, cf_err = cf if with_error
|
148
144
|
term = (prefactor * cf).quo(b)
|
149
145
|
result = 1 - term
|
150
146
|
|
151
|
-
return with_error ? [result, (prefactor_err*cf).quo(b) + (prefactor*cf_err).quo(b) + 2.0*Float::EPSILON*(1+term.abs)] : result
|
147
|
+
return with_error ? [result, (prefactor_err * cf).quo(b) + (prefactor * cf_err).quo(b) + 2.0 * Float::EPSILON * (1 + term.abs)] : result
|
152
148
|
end
|
153
149
|
|
154
150
|
end
|
155
151
|
end
|
156
152
|
|
157
|
-
|
158
153
|
def continued_fraction_cutoff(epsabs)
|
159
154
|
return CUTOFF if epsabs.nil?
|
160
|
-
0.0/0 # NaN
|
155
|
+
0.0 / 0 # NaN
|
161
156
|
end
|
162
157
|
|
163
158
|
# Continued fraction calculation of incomplete beta
|
@@ -165,9 +160,9 @@ module Distribution
|
|
165
160
|
#
|
166
161
|
# If epsabs is set, will execute the version of the GSL function in the cdf folder. Otherwise, does the
|
167
162
|
# basic one in specfunc.
|
168
|
-
def continued_fraction(a,b,x,epsabs=nil,with_error=false)
|
163
|
+
def continued_fraction(a, b, x, epsabs = nil, with_error = false)
|
169
164
|
num_term = 1
|
170
|
-
den_term = 1 - (a+b)*x.quo(a+1)
|
165
|
+
den_term = 1 - (a + b) * x.quo(a + 1)
|
171
166
|
k = 0
|
172
167
|
|
173
168
|
den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF
|
@@ -175,11 +170,10 @@ module Distribution
|
|
175
170
|
cf = den_term
|
176
171
|
|
177
172
|
1.upto(MAX_ITER) do |k|
|
178
|
-
coeff = k *(b-k)*x.quo(((a-1)+2*k)*(a+2*k)) # coefficient for step 1
|
173
|
+
coeff = k * (b - k) * x.quo(((a - 1) + 2 * k) * (a + 2 * k)) # coefficient for step 1
|
179
174
|
delta_frac = nil
|
180
175
|
2.times do
|
181
|
-
|
182
|
-
den_term = 1 + coeff*den_term
|
176
|
+
den_term = 1 + coeff * den_term
|
183
177
|
num_term = 1 + coeff.quo(num_term)
|
184
178
|
|
185
179
|
den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF
|
@@ -188,26 +182,23 @@ module Distribution
|
|
188
182
|
den_term = 1.quo(den_term)
|
189
183
|
|
190
184
|
delta_frac = den_term * num_term
|
191
|
-
cf
|
185
|
+
cf *= delta_frac
|
192
186
|
|
193
|
-
coeff = -(a+k)*(a+b+k)*x.quo((a+2*k)*(a+2*k+1)) # coefficient for step 2
|
187
|
+
coeff = -(a + k) * (a + b + k) * x.quo((a + 2 * k) * (a + 2 * k + 1)) # coefficient for step 2
|
194
188
|
end
|
195
189
|
|
196
|
-
break if (delta_frac-1).abs < 2.0*Float::EPSILON
|
197
|
-
break if !epsabs.nil? && (cf * (delta_frac-1).abs < epsabs)
|
190
|
+
break if (delta_frac - 1).abs < 2.0 * Float::EPSILON
|
191
|
+
break if !epsabs.nil? && (cf * (delta_frac - 1).abs < epsabs)
|
198
192
|
end
|
199
193
|
|
200
194
|
if k > MAX_ITER
|
201
|
-
|
202
|
-
return with_error ? [0.0/0, 0] : 0.0/0 # NaN if epsabs is set
|
195
|
+
fail('Exceeded maximum number of iterations') if epsabs.nil?
|
196
|
+
return with_error ? [0.0 / 0, 0] : 0.0 / 0 # NaN if epsabs is set
|
203
197
|
end
|
204
198
|
|
205
199
|
with_error ? [cf, k * 4 * Float::EPSILON * cf.abs] : cf
|
206
200
|
end
|
207
|
-
|
208
|
-
|
209
201
|
end
|
210
|
-
|
211
202
|
end
|
212
203
|
end
|
213
|
-
end
|
204
|
+
end
|