Module: Distribution::MathExtension::IncompleteBeta
- Defined in:
- lib/distribution/math_extension/incomplete_beta.rb
Overview
Calculate regularized incomplete beta function
Constant Summary collapse
- MAX_ITER =
512
- CUTOFF =
2.0 * Float::MIN
Class Method Summary collapse
-
.axpy(aa, yy, a, b, x) ⇒ Object
Evaluate aa * beta_inc(a,b,x) + yy.
-
.continued_fraction(a, b, x, epsabs = nil, with_error = false) ⇒ Object
Continued fraction calculation of incomplete beta beta_cont_frac from GSL-1.9.
- .continued_fraction_cutoff(epsabs) ⇒ Object
-
.evaluate(a, b, x, with_error = false) ⇒ Object
Evaluate the incomplete beta function gsl_sf_beta_inc_e.
Class Method Details
.axpy(aa, yy, a, b, x) ⇒ Object
Evaluate aa * beta_inc(a,b,x) + yy
No error mode available.
From GSL-1.9: cdf/beta_inc.c, beta_inc_AXPY
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/distribution/math_extension/incomplete_beta.rb', line 80 def axpy(aa, yy, a, b, x) return aa * 0 + yy if x == 0.0 return aa * 1 + yy if x == 1.0 ln_beta = Math.logbeta(a, b) ln_pre = -ln_beta + a * Math.log(x) + b * Math::Log.log1p(-x) prefactor = Math.exp(ln_pre) if x < (a + 1).quo(a + b + 2) # Apply continued fraction directly epsabs = yy.quo((aa * prefactor).quo(a)).abs * Float::EPSILON cf = continued_fraction(a, b, x, epsabs) return aa * (prefactor * cf).quo(a) + yy else # Apply continued fraction after hypergeometric transformation epsabs = (aa + yy).quo((aa * prefactor).quo(b)) * Float::EPSILON cf = continued_fraction(b, a, 1 - x, epsabs) term = (prefactor * cf).quo(b) return aa == -yy ? -aa * term : aa * (1 - term) + yy end end |
.continued_fraction(a, b, x, epsabs = nil, with_error = false) ⇒ Object
Continued fraction calculation of incomplete beta beta_cont_frac from GSL-1.9
If epsabs is set, will execute the version of the GSL function in the cdf folder. Otherwise, does the basic one in specfunc.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/distribution/math_extension/incomplete_beta.rb', line 163 def continued_fraction(a, b, x, epsabs = nil, with_error = false) num_term = 1 den_term = 1 - (a + b) * x.quo(a + 1) k = 0 den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF den_term = 1.quo(den_term) cf = den_term 1.upto(MAX_ITER) do |k| coeff = k * (b - k) * x.quo(((a - 1) + 2 * k) * (a + 2 * k)) # coefficient for step 1 delta_frac = nil 2.times do den_term = 1 + coeff * den_term num_term = 1 + coeff.quo(num_term) den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF num_term = continued_fraction_cutoff(epsabs) if num_term.abs < CUTOFF den_term = 1.quo(den_term) delta_frac = den_term * num_term cf *= delta_frac coeff = -(a + k) * (a + b + k) * x.quo((a + 2 * k) * (a + 2 * k + 1)) # coefficient for step 2 end break if (delta_frac - 1).abs < 2.0 * Float::EPSILON break if !epsabs.nil? && (cf * (delta_frac - 1).abs < epsabs) end if k > MAX_ITER fail('Exceeded maximum number of iterations') if epsabs.nil? return with_error ? [0.0 / 0, 0] : 0.0 / 0 # NaN if epsabs is set end with_error ? [cf, k * 4 * Float::EPSILON * cf.abs] : cf end |
.continued_fraction_cutoff(epsabs) ⇒ Object
153 154 155 156 |
# File 'lib/distribution/math_extension/incomplete_beta.rb', line 153 def continued_fraction_cutoff(epsabs) return CUTOFF if epsabs.nil? 0.0 / 0 # NaN end |
.evaluate(a, b, x, with_error = false) ⇒ Object
Evaluate the incomplete beta function gsl_sf_beta_inc_e
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/distribution/math_extension/incomplete_beta.rb', line 104 def evaluate(a, b, x, with_error = false) 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 if x == 0 return with_error ? [0.0, 0.0] : 0.0 elsif x == 1 return with_error ? [1.0, 0.0] : 1.0 else ln_beta = Beta.log_beta(a, b, with_error) ln_1mx = Log.log_1plusx(-x, with_error) ln_x = Math.log(x) ln_beta, ln_beta_err, ln_1mx, ln_1mx_err, ln_x_err = begin # STDERR.puts("Warning: Error is unknown for Math::log, guessing.") [ln_beta, ln_1mx, Float::EPSILON].flatten end ln_pre = -ln_beta + a * ln_x + b * ln_1mx ln_pre_err = ln_beta_err + (a * ln_x_err).abs + (b * ln_1mx_err).abs if with_error prefactor, prefactor_err = begin if with_error exp_err(ln_pre, ln_pre_err) else [Math.exp(ln_pre), nil] end end if x < (a + 1).quo(a + b + 2) # Apply continued fraction directly cf = continued_fraction(a, b, x, nil, with_error) cf, cf_err = cf if with_error result = (prefactor * cf).quo(a) return with_error ? [result, ((prefactor_err * cf).abs + (prefactor * cf_err).abs).quo(a)] : result else # Apply continued fraction after hypergeometric transformation cf = continued_fraction(b, a, 1 - x, nil) cf, cf_err = cf if with_error term = (prefactor * cf).quo(b) result = 1 - term return with_error ? [result, (prefactor_err * cf).quo(b) + (prefactor * cf_err).quo(b) + 2.0 * Float::EPSILON * (1 + term.abs)] : result end end end |