Skip to content

Commit a02c080

Browse files
committed
Refactor gamma_inc_inv
1 parent 9dd5bf8 commit a02c080

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)
@@ -921,43 +921,41 @@ See also: [`gamma_inc(a,x,ind)`](@ref SpecialFunctions.gamma_inc).
921921
"""
922922
gamma_inc_inv(a::Real, p::Real, q::Real) = _gamma_inc_inv(promote(float(a), float(p), float(q))...)
923923

924-
function _gamma_inc_inv(a::Float64, p::Float64, q::Float64)
925-
924+
function _gamma_inc_inv(a::T, p::T, q::T) where {T<:Real}
926925
if p + q != 1
927926
throw(ArgumentError("p + q must equal one but is $(p + q)"))
928927
end
929928

930929
if iszero(p)
931-
return 0.0
930+
return zero(T)
932931
elseif iszero(q)
933-
return Inf
932+
return T(Inf)
934933
end
935934

936-
if p < 0.5
937-
pcase = true
938-
porq = p
939-
s = -1
940-
else
941-
pcase = false
942-
porq = q
943-
s = 1
944-
end
935+
pcase = p < 0.5
936+
minpq = pcase ? p : q
937+
938+
return __gamma_inc_inv(a, minpq, pcase)
939+
end
940+
941+
function __gamma_inc_inv(a::Float64, minpq::Float64, pcase::Bool)
945942
haseta = false
946943

947-
logr = (1.0/a)*(log(p) + logabsgamma(a + 1.0)[1])
944+
logp = pcase ? log(minpq) : log1p(-minpq)
945+
logr = (1.0/a)*(logp + logabsgamma(a + 1.0)[1])
948946
if logr < log(0.2*(1 + a)) #small value of p
949-
x0 = gamma_inc_inv_psmall(a, p)
950-
elseif ((q < min(0.02, exp(-1.5*a)/gamma(a))) && (a < 10)) #small q
951-
x0 = gamma_inc_inv_qsmall(a, q)
952-
elseif abs(porq - 0.5) < 1.0e-05
947+
x0 = gamma_inc_inv_psmall(a, logr)
948+
elseif !pcase && minpq < min(0.02, exp(-1.5*a)/gamma(a)) && a < 10 #small q
949+
x0 = gamma_inc_inv_qsmall(a, minpq)
950+
elseif abs(minpq - 0.5) < 1.0e-05
953951
x0 = a - 1.0/3.0 + (8.0/405.0 + 184.0/25515.0/a)/a
954952
elseif abs(a - 1.0) < 1.0e-4
955-
x0 = pcase ? -log1p(-p) : -log(q)
953+
x0 = pcase ? -log1p(-minpq) : -log(minpq)
956954
elseif a < 1.0 # small value of a
957-
x0 = gamma_inc_inv_asmall(a, p, q, pcase)
955+
x0 = gamma_inc_inv_asmall(a, minpq, pcase)
958956
else #large a
959957
haseta = true
960-
x0, fp = gamma_inc_inv_alarge(a, porq, s)
958+
x0, fp = gamma_inc_inv_alarge(a, minpq, pcase)
961959
end
962960

963961
t = 1
@@ -981,7 +979,7 @@ function _gamma_inc_inv(a::Float64, p::Float64, q::Float64)
981979

982980
px, qx = gamma_inc(a, x, 0)
983981

984-
ck1 = pcase ? -r*(px - p) : r*(qx - q)
982+
ck1 = pcase ? -r*(px - minpq) : r*(qx - minpq)
985983
if a > 0.05
986984
ck2 = (x - a + 1.0)/(2.0*x)
987985

@@ -1014,16 +1012,8 @@ function _gamma_inc_inv(a::Float64, p::Float64, q::Float64)
10141012
return x
10151013
end
10161014

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

10291019
# 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
@@ -170,8 +170,8 @@ end
170170
@testset "Low precision with Float64(p) + Float64(q) != 1" for T in (Float16, Float32)
171171
@test gamma_inc(T(1.0), gamma_inc_inv(T(1.0), T(0.1), T(0.9)))[1]::T T(0.1)
172172
@test gamma_inc(T(1.0), gamma_inc_inv(T(1.0), T(0.9), T(0.1)))[2]::T T(0.1)
173-
@test_throws ArgumentError("p + q must equal one but was 1.02") gamma_inc_inv(T(1.0), T(0.1), T(0.92))
174-
@test_throws ArgumentError("p + q must equal one but was 1.02") gamma_inc_inv(T(1.0), T(0.92), T(0.1))
173+
@test_throws ArgumentError("p + q must equal one but is 1.02") gamma_inc_inv(T(1.0), T(0.1), T(0.92))
174+
@test_throws ArgumentError("p + q must equal one but is 1.02") gamma_inc_inv(T(1.0), T(0.92), T(0.1))
175175
end
176176
end
177177

0 commit comments

Comments
 (0)