1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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 152 153 154 155
|
// Derived from SciPy's special/cephes/igami.c
// https://github.com/scipy/scipy/blob/master/scipy/special/cephes/igami.c
// Made freely available by Stephen L. Moshier without support or guarantee.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright ©1984, ©1987, ©1995 by Stephen L. Moshier
// Portions Copyright ©2017 The Gonum Authors. All rights reserved.
package cephes
import "math"
// IgamI computes the inverse of the incomplete Gamma function. That is, it
// returns the x such that:
//
// IgamC(a, x) = p
//
// The input argument a must be positive and p must be between 0 and 1
// inclusive or IgamI will panic. IgamI should return a positive number, but
// can return 0 even with non-zero y due to underflow.
func IgamI(a, p float64) float64 {
// Bound the solution
x0 := math.MaxFloat64
yl := 0.0
x1 := 0.0
yh := 1.0
dithresh := 5.0 * machEp
if p < 0 || p > 1 || a <= 0 {
panic(paramOutOfBounds)
}
if p == 0 {
return math.Inf(1)
}
if p == 1 {
return 0.0
}
// Starting with the approximate value
// x = a y^3
// where
// y = 1 - d - ndtri(p) sqrt(d)
// and
// d = 1/9a
// the routine performs up to 10 Newton iterations to find the root of
// IgamC(a, x) - p = 0
d := 1.0 / (9.0 * a)
y := 1.0 - d - Ndtri(p)*math.Sqrt(d)
x := a * y * y * y
lgm := lgam(a)
for i := 0; i < 10; i++ {
if x > x0 || x < x1 {
break
}
y = IgamC(a, x)
if y < yl || y > yh {
break
}
if y < p {
x0 = x
yl = y
} else {
x1 = x
yh = y
}
// Compute the derivative of the function at this point
d = (a-1)*math.Log(x) - x - lgm
if d < -maxLog {
break
}
d = -math.Exp(d)
// Compute the step to the next approximation of x
d = (y - p) / d
if math.Abs(d/x) < machEp {
return x
}
x = x - d
}
d = 0.0625
if x0 == math.MaxFloat64 {
if x <= 0 {
x = 1
}
for x0 == math.MaxFloat64 {
x = (1 + d) * x
y = IgamC(a, x)
if y < p {
x0 = x
yl = y
break
}
d = d + d
}
}
d = 0.5
dir := 0
for i := 0; i < 400; i++ {
x = x1 + d*(x0-x1)
y = IgamC(a, x)
lgm = (x0 - x1) / (x1 + x0)
if math.Abs(lgm) < dithresh {
break
}
lgm = (y - p) / p
if math.Abs(lgm) < dithresh {
break
}
if x <= 0 {
break
}
if y >= p {
x1 = x
yh = y
if dir < 0 {
dir = 0
d = 0.5
} else if dir > 1 {
d = 0.5*d + 0.5
} else {
d = (p - yl) / (yh - yl)
}
dir++
} else {
x0 = x
yl = y
if dir > 0 {
dir = 0
d = 0.5
} else if dir < -1 {
d = 0.5 * d
} else {
d = (p - yl) / (yh - yl)
}
dir--
}
}
return x
}
|