github.com/gopherd/gonum@v0.0.4/mathext/internal/cephes/incbeta.go (about) 1 // Copyright ©2016 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 * Cephes Math Library, Release 2.3: March, 1995 7 * Copyright 1984, 1995 by Stephen L. Moshier 8 */ 9 10 package cephes 11 12 import ( 13 "math" 14 15 "github.com/gopherd/gonum/mathext/internal/gonum" 16 ) 17 18 const ( 19 maxGam = 171.624376956302725 20 big = 4.503599627370496e15 21 biginv = 2.22044604925031308085e-16 22 ) 23 24 // Incbet computes the regularized incomplete beta function. 25 func Incbet(aa, bb, xx float64) float64 { 26 if aa <= 0 || bb <= 0 { 27 panic(paramOutOfBounds) 28 } 29 if xx <= 0 || xx >= 1 { 30 if xx == 0 { 31 return 0 32 } 33 if xx == 1 { 34 return 1 35 } 36 panic(paramOutOfBounds) 37 } 38 39 var flag int 40 if bb*xx <= 1 && xx <= 0.95 { 41 t := pseries(aa, bb, xx) 42 return transformT(t, flag) 43 } 44 45 w := 1 - xx 46 47 // Reverse a and b if x is greater than the mean. 48 var a, b, xc, x float64 49 if xx > aa/(aa+bb) { 50 flag = 1 51 a = bb 52 b = aa 53 xc = xx 54 x = w 55 } else { 56 a = aa 57 b = bb 58 xc = w 59 x = xx 60 } 61 62 if flag == 1 && (b*x) <= 1.0 && x <= 0.95 { 63 t := pseries(a, b, x) 64 return transformT(t, flag) 65 } 66 67 // Choose expansion for better convergence. 68 y := x*(a+b-2.0) - (a - 1.0) 69 if y < 0.0 { 70 w = incbcf(a, b, x) 71 } else { 72 w = incbd(a, b, x) / xc 73 } 74 75 // Multiply w by the factor 76 // x^a * (1-x)^b * Γ(a+b) / (a*Γ(a)*Γ(b)) 77 var t float64 78 y = a * math.Log(x) 79 t = b * math.Log(xc) 80 if (a+b) < maxGam && math.Abs(y) < maxLog && math.Abs(t) < maxLog { 81 t = math.Pow(xc, b) 82 t *= math.Pow(x, a) 83 t /= a 84 t *= w 85 t *= 1.0 / gonum.Beta(a, b) 86 return transformT(t, flag) 87 } 88 89 // Resort to logarithms. 90 y += t - gonum.Lbeta(a, b) 91 y += math.Log(w / a) 92 if y < minLog { 93 t = 0.0 94 } else { 95 t = math.Exp(y) 96 } 97 98 return transformT(t, flag) 99 } 100 101 func transformT(t float64, flag int) float64 { 102 if flag == 1 { 103 if t <= machEp { 104 t = 1.0 - machEp 105 } else { 106 t = 1.0 - t 107 } 108 } 109 return t 110 } 111 112 // incbcf returns the incomplete beta integral evaluated by a continued fraction 113 // expansion. 114 func incbcf(a, b, x float64) float64 { 115 var xk, pk, pkm1, pkm2, qk, qkm1, qkm2 float64 116 var k1, k2, k3, k4, k5, k6, k7, k8 float64 117 var r, t, ans, thresh float64 118 var n int 119 120 k1 = a 121 k2 = a + b 122 k3 = a 123 k4 = a + 1.0 124 k5 = 1.0 125 k6 = b - 1.0 126 k7 = k4 127 k8 = a + 2.0 128 129 pkm2 = 0.0 130 qkm2 = 1.0 131 pkm1 = 1.0 132 qkm1 = 1.0 133 ans = 1.0 134 r = 1.0 135 thresh = 3.0 * machEp 136 137 for n = 0; n <= 300; n++ { 138 139 xk = -(x * k1 * k2) / (k3 * k4) 140 pk = pkm1 + pkm2*xk 141 qk = qkm1 + qkm2*xk 142 pkm2 = pkm1 143 pkm1 = pk 144 qkm2 = qkm1 145 qkm1 = qk 146 147 xk = (x * k5 * k6) / (k7 * k8) 148 pk = pkm1 + pkm2*xk 149 qk = qkm1 + qkm2*xk 150 pkm2 = pkm1 151 pkm1 = pk 152 qkm2 = qkm1 153 qkm1 = qk 154 155 if qk != 0 { 156 r = pk / qk 157 } 158 if r != 0 { 159 t = math.Abs((ans - r) / r) 160 ans = r 161 } else { 162 t = 1.0 163 } 164 165 if t < thresh { 166 return ans 167 } 168 169 k1 += 1.0 170 k2 += 1.0 171 k3 += 2.0 172 k4 += 2.0 173 k5 += 1.0 174 k6 -= 1.0 175 k7 += 2.0 176 k8 += 2.0 177 178 if (math.Abs(qk) + math.Abs(pk)) > big { 179 pkm2 *= biginv 180 pkm1 *= biginv 181 qkm2 *= biginv 182 qkm1 *= biginv 183 } 184 if (math.Abs(qk) < biginv) || (math.Abs(pk) < biginv) { 185 pkm2 *= big 186 pkm1 *= big 187 qkm2 *= big 188 qkm1 *= big 189 } 190 } 191 192 return ans 193 } 194 195 // incbd returns the incomplete beta integral evaluated by a continued fraction 196 // expansion. 197 func incbd(a, b, x float64) float64 { 198 var xk, pk, pkm1, pkm2, qk, qkm1, qkm2 float64 199 var k1, k2, k3, k4, k5, k6, k7, k8 float64 200 var r, t, ans, z, thresh float64 201 var n int 202 203 k1 = a 204 k2 = b - 1.0 205 k3 = a 206 k4 = a + 1.0 207 k5 = 1.0 208 k6 = a + b 209 k7 = a + 1.0 210 k8 = a + 2.0 211 212 pkm2 = 0.0 213 qkm2 = 1.0 214 pkm1 = 1.0 215 qkm1 = 1.0 216 z = x / (1.0 - x) 217 ans = 1.0 218 r = 1.0 219 thresh = 3.0 * machEp 220 for n = 0; n <= 300; n++ { 221 222 xk = -(z * k1 * k2) / (k3 * k4) 223 pk = pkm1 + pkm2*xk 224 qk = qkm1 + qkm2*xk 225 pkm2 = pkm1 226 pkm1 = pk 227 qkm2 = qkm1 228 qkm1 = qk 229 230 xk = (z * k5 * k6) / (k7 * k8) 231 pk = pkm1 + pkm2*xk 232 qk = qkm1 + qkm2*xk 233 pkm2 = pkm1 234 pkm1 = pk 235 qkm2 = qkm1 236 qkm1 = qk 237 238 if qk != 0 { 239 r = pk / qk 240 } 241 if r != 0 { 242 t = math.Abs((ans - r) / r) 243 ans = r 244 } else { 245 t = 1.0 246 } 247 248 if t < thresh { 249 return ans 250 } 251 252 k1 += 1.0 253 k2 -= 1.0 254 k3 += 2.0 255 k4 += 2.0 256 k5 += 1.0 257 k6 += 1.0 258 k7 += 2.0 259 k8 += 2.0 260 261 if (math.Abs(qk) + math.Abs(pk)) > big { 262 pkm2 *= biginv 263 pkm1 *= biginv 264 qkm2 *= biginv 265 qkm1 *= biginv 266 } 267 if (math.Abs(qk) < biginv) || (math.Abs(pk) < biginv) { 268 pkm2 *= big 269 pkm1 *= big 270 qkm2 *= big 271 qkm1 *= big 272 } 273 } 274 return ans 275 } 276 277 // pseries returns the incomplete beta integral evaluated by a power series. Use 278 // when b*x is small and x not too close to 1. 279 func pseries(a, b, x float64) float64 { 280 var s, t, u, v, n, t1, z, ai float64 281 ai = 1.0 / a 282 u = (1.0 - b) * x 283 v = u / (a + 1.0) 284 t1 = v 285 t = u 286 n = 2.0 287 s = 0.0 288 z = machEp * ai 289 for math.Abs(v) > z { 290 u = (n - b) * x / n 291 t *= u 292 v = t / (a + n) 293 s += v 294 n += 1.0 295 } 296 s += t1 297 s += ai 298 299 u = a * math.Log(x) 300 if (a+b) < maxGam && math.Abs(u) < maxLog { 301 t = 1.0 / gonum.Beta(a, b) 302 s = s * t * math.Pow(x, a) 303 } else { 304 t = -gonum.Lbeta(a, b) + u + math.Log(s) 305 if t < minLog { 306 s = 0.0 307 } else { 308 s = math.Exp(t) 309 } 310 } 311 return (s) 312 }