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  }