More gamma_inc_inv Failures
Deduction42 opened this issue · comments
Hello,
The last patch fixed a lot of the issues I was running into with Distributions.jl. However, I was still able to trigger two more (unique) failure cases. When calling via Distributions.jl, I was able to trigger one case with this
dG = Gamma{Float64}(0.0016546512046778552, 0.21745036229505915)
q = 1-0.7070707070707071
quantile(dG,q)
ERROR: DomainError with -9.5893e-320:
log will only return a complex result if called with a complex argument. Try log(Complex(x)).
Stacktrace:
[1] throw_complex_domainerror(f::Symbol, x::Float64)
@ Base.Math .\math.jl:33
[2] _log(x::Float64, base::Val{:ℯ}, func::Symbol)
@ Base.Math .\special\log.jl:304
[3] log
@ .\special\log.jl:269 [inlined]
[4] __gamma_inc_inv(a::Float64, minpq::Float64, pcase::Bool)
@ SpecialFunctions C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:971
[5] _gamma_inc_inv
@ C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:935 [inlined]
[6] gamma_inc_inv
@ C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:917 [inlined]
[7] gammainvcdf
@ C:\Users\usr\.julia\packages\StatsFuns\dTYga\src\distrs\gamma.jl:98 [inlined]
[8] quantile(d::Gamma{Float64}, q::Float64)
@ Distributions C:\Users\usr\.julia\packages\Distributions\T2SAc\src\univariates.jl:627
[9] top-level scope
@ REPL[4]:1
The second failure case was triggered with this:
dG = Gamma{Float64}(1.0309015068677239, 0.03724188228734454)
q = 1-0.020202020202020204
quantile(dG, q)
ERROR: DomainError with (1.0309015068677239, -380.7187661907244, 0):
`a` and `x` must be greater than 0 ---- Domain : (0, Inf)
Stacktrace:
[1] _gamma_inc(a::Float64, x::Float64, ind::Int64)
@ SpecialFunctions C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:787
[2] gamma_inc
@ C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:781 [inlined]
[3] __gamma_inc_inv(a::Float64, minpq::Float64, pcase::Bool)
@ SpecialFunctions C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:981
[4] _gamma_inc_inv
@ C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:935 [inlined]
[5] gamma_inc_inv
@ C:\Users\usr\.julia\packages\SpecialFunctions\rNt4H\src\gamma_inc.jl:917 [inlined]
[6] gammainvcdf
@ C:\Users\usr\.julia\packages\StatsFuns\dTYga\src\distrs\gamma.jl:98 [inlined]
[7] quantile(d::Gamma{Float64}, q::Float64)
@ Distributions C:\Users\usr\.julia\packages\Distributions\T2SAc\src\univariates.jl:627
[8] top-level scope
@ REPL[10]:1
Thanks for reporting these. It looks gamma_inc_inv
isn't as robust as you could hope for. For easier reference in this package, the two error cases are:
julia> gamma_inc_inv(0.0016546512046778552, 1-0.7070707070707071, 0.7070707070707071)
x = x0 = 3.0e-323
x = x0 = 1.7944e-320
x = x0 = -9.5893e-320
ERROR: DomainError with -9.5893e-320:
log will only return a complex result if called with a complex argument. Try log(Complex(x)).
Stacktrace:
[1] throw_complex_domainerror(f::Symbol, x::Float64)
@ Base.Math ./math.jl:33
[2] _log(x::Float64, base::Val{:ℯ}, func::Symbol)
@ Base.Math ./special/log.jl:304
[3] log
@ ./special/log.jl:269 [inlined]
[4] __gamma_inc_inv(a::Float64, minpq::Float64, pcase::Bool)
@ SpecialFunctions ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:971
[5] _gamma_inc_inv
@ ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:935 [inlined]
[6] gamma_inc_inv(a::Float64, p::Float64, q::Float64)
@ SpecialFunctions ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:917
[7] top-level scope
@ REPL[54]:1
and
julia> gamma_inc_inv(1.0309015068677239, 1-0.020202020202020204, 0.020202020202020204)
x = x0 = 4.997466292519298
x = x0 = 2.905035646142073
x = x0 = 19.7726175242108
x = x0 = -380.71876619072816
ERROR: DomainError with (1.0309015068677239, -380.71876619072816, 0):
`a` and `x` must be greater than 0 ---- Domain : (0, Inf)
Stacktrace:
[1] _gamma_inc(a::Float64, x::Float64, ind::Int64)
@ SpecialFunctions ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:787
[2] gamma_inc
@ ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:781 [inlined]
[3] __gamma_inc_inv(a::Float64, minpq::Float64, pcase::Bool)
@ SpecialFunctions ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:981
[4] _gamma_inc_inv
@ ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:935 [inlined]
[5] gamma_inc_inv(a::Float64, p::Float64, q::Float64)
@ SpecialFunctions ~/.julia/dev/SpecialFunctions/src/gamma_inc.jl:917
[6] top-level scope
@ REPL[55]:1
I've printed the value of x
during the Newton iterations to make it visible how it fails. I'm wondering if something is wrong in
SpecialFunctions.jl/src/gamma_inc.jl
Line 751 in 5bcad28
After a lot of testing (more than a million runs) I think I can find the two areas where things tend to fail. One case is where alpha is small and p is close to 0.5, and in the other area alpha is close to one as well as the p value. If these cases are taken care of, I'm pretty sure gamma_inc_inv will be quite robust.
for ii in 1:100000
a = rand(Gamma(0.5,20))
th = 1.0
dG = Gamma(a,th)
p = rand()
try
quantile(dG,p)
catch
display("Failure occurred with dG=$(dG), p = $(p)")
end
end
"Failure occurred with dG=Gamma{Float64}(α=0.0012092790074495242, θ=1.0), p = 0.4082421355705157"
"Failure occurred with dG=Gamma{Float64}(α=1.0159294646390604, θ=1.0), p = 0.979133588311139"
"Failure occurred with dG=Gamma{Float64}(α=0.0008500643209799257, θ=1.0), p = 0.5328646109345743"
"Failure occurred with dG=Gamma{Float64}(α=1.0067186164460882, θ=1.0), p = 0.9783064756058487"
"Failure occurred with dG=Gamma{Float64}(α=1.0197172128374965, θ=1.0), p = 0.9782999473835832"
"Failure occurred with dG=Gamma{Float64}(α=1.0021613258145359, θ=1.0), p = 0.9796635761410678"
"Failure occurred with dG=Gamma{Float64}(α=0.0015145166954209457, θ=1.0), p = 0.32601660850450453"
At the moment, I have to wrap my quantile calls with a Try-Catch statement, return NaN, and try to interpolate out any NaNs that occur.
At the moment, I have to wrap my quantile calls with a Try-Catch statement, return NaN, and try to interpolate out any NaNs that occur.
A possibly easier solution until gamma_inc_inv
is more robust is to pin StatsFuns to 0.9.15 and to not use 0.9.16. Then Rmath will be used instead of SpecialFunctions for computing the quantiles.
Oh, thanks! I didn't even have StatsFuns installed, I basically just had Distributions.jl and SpecialFuns.jl. I didn't know that installing a different version of StatsFuns even affected Distributions.jl.
Oh, thanks! I didn't even have StatsFuns installed, I basically just had Distributions.jl and SpecialFuns.jl.
If you install Distributions, StatsFuns is installed automatically as an indirect dependency. You can see the explicitly installed packages if you run ] st
in the Julia REPL, and all, also automatically installed, packages with ] st --manifest
.
So the second example here was a simple bug, see #391. The first example is more tricky. When evaluating the incomplete gamma function during the Newton iteration,
SpecialFunctions.jl/src/gamma_inc.jl
Line 981 in 5bcad28
the first time, the input values are a=0.0016546512046778552
and x=3.0e-323
. Our incomplete gamma function has this branch
SpecialFunctions.jl/src/gamma_inc.jl
Lines 790 to 795 in 5bcad28
so the result ends up as (0.0, 1.0)
which eventually leads to a Newton step that makes x
slightly negative. In contrast, https://arxiv.org/pdf/1306.1754.pdf is able to evaluate the first inputs to (0.29291549720279125, 0.70708450279720880)
. We could consider changing the implementation of the incomplete gamma function but I suspect it's just not reliable to compute results for subnormal inputs. Alternatively, we should figure out a way to modify the Newton iteration to stop if the step causes a negative x
.
I reached out to Amparo Gil, and she gave us permission to MIT license Julia translations of their Fortran code so we should look into translating those.