Skip to content

Commit 1f026ed

Browse files
devmotionandreasnoack
authored andcommitted
Refactor gamma_inc_inv
(cherry picked from commit a02c080)
1 parent 9cc7a80 commit 1f026ed

File tree

2 files changed

+40
-50
lines changed

2 files changed

+40
-50
lines changed

src/gamma_inc.jl

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ function gamma_inc_fsum(a::Float64, x::Float64)
665665
end
666666

667667
"""
668-
gamma_inc_inv_psmall(a,p)
668+
gamma_inc_inv_psmall(a, logr)
669669
670670
Compute `x0` - initial approximation when `p` is small.
671671
Here we invert the series in Eqn (2.20) in the paper and write the inversion problem as:
@@ -675,8 +675,7 @@ x = r\\left[1 + a\\sum_{k=1}^{\\infty}\\frac{(-x)^{n}}{(a+n)n!}\\right]^{-1/a},
675675
where ``r = (p\\Gamma(1+a))^{1/a}``
676676
Inverting this relation we obtain ``x = r + \\sum_{k=2}^{\\infty}c_{k}r^{k}``.
677677
"""
678-
function gamma_inc_inv_psmall(a::Float64, p::Float64)
679-
logr = (1.0/a)*(log(p) + logabsgamma(a + 1.0)[1])
678+
function gamma_inc_inv_psmall(a::Float64, logr::Float64)
680679
r = exp(logr)
681680
ap1 = a + 1.0
682681
ap1² = ap1*ap1
@@ -726,19 +725,19 @@ function gamma_inc_inv_qsmall(a::Float64, q::Float64)
726725
end
727726

728727
"""
729-
gamma_inc_inv_asmall(a,p,q,pcase)
730-
731-
Compute `x0` - initial approximation when `a` is small.
732-
Here the solution `x` of ``P(a,x)=p`` satisfies ``x_{l} < x < x_{u}``
733-
where ``x_{l} = (p\\Gamma(a+1))^{1/a}`` and ``x_{u} = -\\log{(1 - p\\Gamma(a+1))}``, and is used as starting value for Newton iteration.
734-
"""
735-
function gamma_inc_inv_asmall(a::Float64, p::Float64, q::Float64, pcase::Bool)
736-
logp = (pcase) ? log(p) : log1p(-q)
737-
return exp((1.0/a)*(logp +loggamma1p(a)))
728+
gamma_inc_inv_asmall(a, minpq, pcase)
729+
730+
Compute `x0` - initial approximation when `a` is small.
731+
Here the solution `x` of ``P(a,x)=p`` satisfies ``x_{l} < x < x_{u}``
732+
where ``x_{l} = (p\\Gamma(a+1))^{1/a}`` and ``x_{u} = -\\log{(1 - p\\Gamma(a+1))}``, and is used as starting value for Newton iteration.
733+
"""
734+
function gamma_inc_inv_asmall(a::Float64, minpq::Float64, pcase::Bool)
735+
logp = pcase ? log(minpq) : log1p(-minpq)
736+
return exp((logp + loggamma1p(a)) / a)
738737
end
739738

740739
"""
741-
gamma_inc_inv_alarge(a,porq,s)
740+
gamma_inc_inv_alarge(a, minpq, pcase)
742741
743742
Compute `x0` - initial approximation when `a` is large.
744743
The inversion problem is rewritten as :
@@ -753,9 +752,10 @@ and it is possible to expand:
753752
which is calculated by coeff1, coeff2 and coeff3 functions below.
754753
This returns a tuple `(x0,fp)`, where `fp` is computed since it's an approximation for the coefficient after inverting the original power series.
755754
"""
756-
function gamma_inc_inv_alarge(a::Float64, porq::Float64, s::Integer)
757-
r = erfcinv(2*porq)
758-
eta = s*r/sqrt(a*0.5)
755+
function gamma_inc_inv_alarge(a::Float64, minpq::Float64, pcase::Bool)
756+
r = erfcinv(2*minpq)
757+
s = r/sqrt(a*0.5)
758+
eta = pcase ? -s : s
759759
eta += (coeff1(eta) + (coeff2(eta) + coeff3(eta)/a)/a)/a
760760
x0 = a*lambdaeta(eta)
761761
fp = -sqrt(inv2π*a)*exp(-0.5*a*eta*eta)/gammax(a)
@@ -925,43 +925,41 @@ See also: [`gamma_inc(a,x,ind)`](@ref SpecialFunctions.gamma_inc).
925925
"""
926926
gamma_inc_inv(a::Real, p::Real, q::Real) = _gamma_inc_inv(promote(float(a), float(p), float(q))...)
927927

928-
function _gamma_inc_inv(a::Float64, p::Float64, q::Float64)
929-
928+
function _gamma_inc_inv(a::T, p::T, q::T) where {T<:Real}
930929
if p + q != 1
931930
throw(ArgumentError("p + q must equal one but is $(p + q)"))
932931
end
933932

934933
if iszero(p)
935-
return 0.0
934+
return zero(T)
936935
elseif iszero(q)
937-
return Inf
936+
return T(Inf)
938937
end
939938

940-
if p < 0.5
941-
pcase = true
942-
porq = p
943-
s = -1
944-
else
945-
pcase = false
946-
porq = q
947-
s = 1
948-
end
939+
pcase = p < 0.5
940+
minpq = pcase ? p : q
941+
942+
return __gamma_inc_inv(a, minpq, pcase)
943+
end
944+
945+
function __gamma_inc_inv(a::Float64, minpq::Float64, pcase::Bool)
949946
haseta = false
950947

951-
logr = (1.0/a)*(log(p) + logabsgamma(a + 1.0)[1])
948+
logp = pcase ? log(minpq) : log1p(-minpq)
949+
logr = (1.0/a)*(logp + logabsgamma(a + 1.0)[1])
952950
if logr < log(0.2*(1 + a)) #small value of p
953-
x0 = gamma_inc_inv_psmall(a, p)
954-
elseif ((q < min(0.02, exp(-1.5*a)/gamma(a))) && (a < 10)) #small q
955-
x0 = gamma_inc_inv_qsmall(a, q)
956-
elseif abs(porq - 0.5) < 1.0e-05
951+
x0 = gamma_inc_inv_psmall(a, logr)
952+
elseif !pcase && minpq < min(0.02, exp(-1.5*a)/gamma(a)) && a < 10 #small q
953+
x0 = gamma_inc_inv_qsmall(a, minpq)
954+
elseif abs(minpq - 0.5) < 1.0e-05
957955
x0 = a - 1.0/3.0 + (8.0/405.0 + 184.0/25515.0/a)/a
958956
elseif abs(a - 1.0) < 1.0e-4
959-
x0 = pcase ? -log1p(-p) : -log(q)
957+
x0 = pcase ? -log1p(-minpq) : -log(minpq)
960958
elseif a < 1.0 # small value of a
961-
x0 = gamma_inc_inv_asmall(a, p, q, pcase)
959+
x0 = gamma_inc_inv_asmall(a, minpq, pcase)
962960
else #large a
963961
haseta = true
964-
x0, fp = gamma_inc_inv_alarge(a, porq, s)
962+
x0, fp = gamma_inc_inv_alarge(a, minpq, pcase)
965963
end
966964

967965
t = 1
@@ -985,7 +983,7 @@ function _gamma_inc_inv(a::Float64, p::Float64, q::Float64)
985983

986984
px, qx = gamma_inc(a, x, 0)
987985

988-
ck1 = pcase ? -r*(px - p) : r*(qx - q)
986+
ck1 = pcase ? -r*(px - minpq) : r*(qx - minpq)
989987
if a > 0.05
990988
ck2 = (x - a + 1.0)/(2.0*x)
991989

@@ -1018,16 +1016,8 @@ function _gamma_inc_inv(a::Float64, p::Float64, q::Float64)
10181016
return x
10191017
end
10201018

1021-
function _gamma_inc_inv(a::T, p::T, q::T) where {T <: Union{Float16, Float32}}
1022-
if p + q != one(T)
1023-
throw(ArgumentError("p + q must equal one but was $(p + q)"))
1024-
end
1025-
p64, q64 = if p < q
1026-
(Float64(p), 1 - Float64(p))
1027-
else
1028-
(1 - Float64(q), Float64(q))
1029-
end
1030-
return T(_gamma_inc_inv(Float64(a), p64, q64))
1019+
function __gamma_inc_inv(a::T, minpq::T, pcase::Bool) where {T<:Union{Float16,Float32}}
1020+
return T(__gamma_inc_inv(Float64(a), Float64(minpq), pcase))
10311021
end
10321022

10331023
# like promote(x,y), but don't complexify real values

test/gamma_inc.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ end
177177
@testset "Low precision with Float64(p) + Float64(q) != 1" for T in (Float16, Float32)
178178
@test gamma_inc(T(1.0), gamma_inc_inv(T(1.0), T(0.1), T(0.9)))[1]::T T(0.1)
179179
@test gamma_inc(T(1.0), gamma_inc_inv(T(1.0), T(0.9), T(0.1)))[2]::T T(0.1)
180-
@test_throws ArgumentError("p + q must equal one but was 1.02") gamma_inc_inv(T(1.0), T(0.1), T(0.92))
181-
@test_throws ArgumentError("p + q must equal one but was 1.02") gamma_inc_inv(T(1.0), T(0.92), T(0.1))
180+
@test_throws ArgumentError("p + q must equal one but is 1.02") gamma_inc_inv(T(1.0), T(0.1), T(0.92))
181+
@test_throws ArgumentError("p + q must equal one but is 1.02") gamma_inc_inv(T(1.0), T(0.92), T(0.1))
182182
end
183183
end
184184

0 commit comments

Comments
 (0)