Skip to content

Commit a82c4b1

Browse files
Sumegh-gitsimonbyrne
authored andcommitted
Added inverse of incomplete beta Ix(a,b) and Iy(a,b) (#166)
1 parent 1d1d0f9 commit a82c4b1

File tree

3 files changed

+166
-0
lines changed

3 files changed

+166
-0
lines changed

src/SpecialFunctions.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export
4949
trigamma,
5050
gamma_inc,
5151
beta_inc,
52+
beta_inc_inv,
5253
gamma_inc_inv,
5354
hankelh1,
5455
hankelh1x,

src/beta_inc.jl

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,3 +825,120 @@ function beta_inc(a::T, b::T, x::T, y::T) where {T<:Union{Float16, Float32}}
825825
end
826826
beta_inc(a::Real, b::Real, x::Real, y::Real) = beta_inc(promote(float(a), float(b), float(x), float(y))...)
827827
beta_inc(a::T, b::T, x::T, y::T) where {T<:AbstractFloat} = throw(MethodError(beta_inc,(a, b, x, y,"")))
828+
829+
#GW Cran, KJ Martin, GE Thomas, Remark AS R19 and Algorithm AS 109: A Remark on Algorithms AS 63: The Incomplete Beta Integral and AS 64: Inverse of the Incomplete Beta Integeral,
830+
#Applied Statistics,
831+
#Volume 26, Number 1, 1977, pages 111-114.
832+
833+
"""
834+
beta_inc_inv(a,b,p,q,lb=logbeta(a,b)[1])
835+
836+
Computes inverse of incomplete beta function. Given `a`,`b` and ``I_{x}(a,b) = p`` find `x` and return tuple (x,y).
837+
"""
838+
function beta_inc_inv(a::Float64, b::Float64, p::Float64, q::Float64; lb = logbeta(a,b)[1])
839+
fpu = 1e-30
840+
x = p
841+
if p == 0.0
842+
return (0.0, 1.0)
843+
elseif p == 1.0
844+
return (1.0, 0.0)
845+
end
846+
847+
#change tail if necessary
848+
849+
if p > 0.5
850+
pp = q
851+
aa = b
852+
bb = a
853+
indx = true
854+
else
855+
pp = p
856+
aa = a
857+
bb = b
858+
indx = false
859+
end
860+
861+
#Initial approx
862+
863+
r = sqrt(-log(pp^2))
864+
pp_approx = r - @horner(r, 2.30753e+00, 0.27061e+00) / @horner(r, 1.0, .99229e+00, .04481e+00)
865+
866+
if a > 1.0 && b > 1.0
867+
r = (pp_approx^2 - 3.0)/6.0
868+
s = 1.0/(2*aa - 1.0)
869+
t = 1.0/(2*bb - 1.0)
870+
h = 2.0/(s+t)
871+
w = pp_approx*sqrt(h+r)/h - (t-s)*(r + 5.0/6.0 - 2.0/(3.0*h))
872+
x = aa/ (aa+bb*exp(w^2))
873+
else
874+
r = 2.0*bb
875+
t = 1.0/(9.0*bb)
876+
t = r*(1.0-t+pp_approx*sqrt(t))^3
877+
if t <= 0.0
878+
x = -expm1((log((1.0-pp)*bb)+lb)/bb)
879+
else
880+
t = (4.0*aa+r-2.0)/t
881+
if t <= 1.0
882+
x = exp((log(pp*aa)+lb)/aa)
883+
else
884+
x = 1.0 - 2.0/(t+1.0)
885+
end
886+
end
887+
end
888+
889+
#solve x using modified newton-raphson iteration
890+
891+
r = 1.0 - aa
892+
t = 1.0 - bb
893+
pp_approx_prev = 0.0
894+
sq = 1.0
895+
prev = 1.0
896+
897+
if x < 0.0001
898+
x = 0.0001
899+
end
900+
if x > .9999
901+
x = .9999
902+
end
903+
904+
iex = max(-5.0/aa^2 - 1.0/pp^0.2 - 13.0, -30.0)
905+
acu = 10.0^iex
906+
907+
#iterate
908+
while true
909+
pp_approx = beta_inc(aa,bb,x)[1]
910+
xin = x
911+
pp_approx = (pp_approx-pp)*exp(lb+r*log(xin)+t*log1p(-xin))
912+
if pp_approx * pp_approx_prev <= 0.0
913+
prev = max(sq, fpu)
914+
end
915+
g = 1.0
916+
917+
tx = x - g*pp_approx
918+
while true
919+
adj = g*pp_approx
920+
sq = adj^2
921+
tx = x - adj
922+
if (prev > sq && tx >= 0.0 && tx <= 1.0)
923+
break
924+
end
925+
g /= 3.0
926+
end
927+
928+
#check if current estimate is acceptable
929+
930+
if prev <= acu || pp_approx^2 <= acu
931+
x = tx
932+
return indx ? (1.0 - x, x) : (x, 1.0-x)
933+
end
934+
935+
if tx == x
936+
return indx ? (1.0 - x, x) : (x, 1.0-x)
937+
end
938+
939+
x = tx
940+
pp_approx_prev = pp_approx
941+
end
942+
end
943+
944+
beta_inc_inv(a::Float64, b::Float64, p::Float64) = beta_inc_inv(a, b, p, 1.0-p)

test/runtests.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,54 @@ end
166166
@test SpecialFunctions.loggammadiv(13.89, 21.0001) log(gamma(big(21.0001))/gamma(big(21.0001)+big(13.89)))
167167
@test SpecialFunctions.stirling_corr(11.99, 100.1) SpecialFunctions.stirling(11.99) + SpecialFunctions.stirling(100.1) - SpecialFunctions.stirling(11.99 + 100.1)
168168
end
169+
@testset "inverse of incomplete beta" begin
170+
f(a,b,p) = beta_inc_inv(a,b,p)[1]
171+
@test f(.5,.5,0.6376856085851985E-01) 0.01
172+
@test f(.5,.5,0.20483276469913355) 0.1
173+
@test f(.5,.5,1.0000) 1.0000
174+
@test f(1.0,.5,0.0) 0.00
175+
@test f(1.0,.5,0.5012562893380045E-02) 0.01
176+
@test f(1.0,.5,0.5131670194948620E-01) 0.1
177+
@test f(1.0,.5, 0.2928932188134525) 0.5
178+
@test f(1.0,1.0,.5) 0.5
179+
@test f(2.0,2.0,.028) 0.1
180+
@test f(2.0,2.0,0.104) 0.2
181+
@test f(2.0,2.0,.216) 0.3
182+
@test f(2.0,2.0,.352) 0.4
183+
@test f(2.0,2.0,.5) 0.5
184+
@test f(2.0,2.0,0.648) 0.6
185+
@test f(2.0,2.0,0.784) 0.7
186+
@test f(2.0,2.0,0.896) 0.8
187+
@test f(2.0,2.0,.972) 0.9
188+
@test f(5.5,5.0,0.4361908850559777) .5
189+
@test f(10.0,.5,0.1516409096347099) 0.9
190+
@test f(10.0,5.0,0.8978271484375000E-01) 0.5
191+
@test f(10.0,5.0,1.00) 1.00
192+
@test f(10.0,10.0,.5) .5
193+
@test f(20.0,5.0,0.4598773297575791) 0.8
194+
@test f(20.0,10.0,0.2146816102371739) 0.6
195+
@test f(20.0,10.0,0.9507364826957875) 0.8
196+
@test f(20.0,20.0,.5) .5
197+
@test f(20.0,20.0,0.8979413687105918) 0.6
198+
@test f(30.0,10.0,0.2241297491808366) 0.7
199+
@test f(30.0,10.0,0.7586405487192086) 0.8
200+
@test f(40.0,20.0,0.7001783247477069) 0.7
201+
@test f(1.0,0.5,0.5131670194948620E-01) 0.1
202+
@test f(1.0,0.5,0.1055728090000841) 0.2
203+
@test f(1.0,0.5,0.1633399734659245) 0.3
204+
@test f(1.0,0.5,0.2254033307585166) 0.4
205+
@test f(1.0,2.0,.36) 0.2
206+
@test f(1.0,3.0,.488) 0.2
207+
@test f(1.0,4.0,.5904) 0.2
208+
@test f(1.0,5.0,.67232) 0.2
209+
@test f(2.0,2.0,.216) 0.3
210+
@test f(3.0,2.0,0.837e-1) 0.3
211+
@test f(4.0,2.0,0.3078e-1) 0.3
212+
@test f(5.0,2.0,0.10935e-1) 0.3
213+
@test f(1.30625000,11.75620000,0.9188846846205182) 0.225609
214+
@test f(1.30625000,11.75620000,0.21053116418502513) 0.033557
215+
@test f(1.30625000,11.75620000,0.18241165418408148) 0.029522
216+
end
169217
@testset "elliptic integrals" begin
170218
#Computed using Wolframalpha EllipticK and EllipticE functions.
171219
@test ellipk(0) 1.570796326794896619231322 rtol=2*eps()

0 commit comments

Comments
 (0)