Skip to content

Fix hangs in beta_inc_inv (alternative/extension of #396) #399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 17, 2022

Conversation

devmotion
Copy link
Member

I noticed that #396 is not merged yet, due to some minor comments and failing tests (I assume).

This PR builds on #396 and in addition to some minor changes (removal of line for debugging, clamp instead of explicit if statements) most importantly it uses a different approach for fixing the original issue: Lower-bounding the tolerance at 10 * floatmin(Float64) instead of floatmin(Float64) seems to fix the pathological example.

Of course, as the maximum number of iterations in #396 this is also an arbitrary choice. However, in contrast to the other PR tests still pass since harder examples that require a larger number of iterations don't fail.

@@ -934,7 +934,6 @@ function _beta_inc_inv(a::Float64, b::Float64, p::Float64, q::Float64=1-p)
r = sqrt(-2*log(p))
p_approx = r - @horner(r, 2.30753e+00, 0.27061e+00) / @horner(r, 1.0, .99229e+00, .04481e+00)
fpu = floatmin(Float64)
sae = log10(fpu)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sae is only used in the computation of acu = exp10(max(..., sae)) below. I rewrote this as acu = max(exp10(...), fpu) (or rather 10 * fpu).

@codecov
Copy link

codecov bot commented May 11, 2022

Codecov Report

Merging #399 (39d0f98) into master (ce58a13) will increase coverage by 1.58%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master     #399      +/-   ##
==========================================
+ Coverage   91.99%   93.57%   +1.58%     
==========================================
  Files          12       12              
  Lines        2809     2769      -40     
==========================================
+ Hits         2584     2591       +7     
+ Misses        225      178      -47     
Flag Coverage Δ
unittests 93.57% <100.00%> (+1.58%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/beta_inc.jl 92.36% <100.00%> (-0.24%) ⬇️
src/gamma.jl 95.14% <0.00%> (+2.13%) ⬆️
src/gamma_inc.jl 93.32% <0.00%> (+3.30%) ⬆️
src/sincosint.jl 100.00% <0.00%> (+34.54%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ce58a13...39d0f98. Read the comment docs.

Comment on lines -1015 to -1025
g = 1.0

tx = x - g*p_approx
while true
adj = g*p_approx
sq = adj^2
adj = p_approx
tx = x - adj
while prev <= (sq = adj^2) || tx < 0.0 || tx > 1.0
adj /= 3.0
tx = x - adj
if (prev > sq && tx >= 0.0 && tx <= 1.0)
break
end
g /= 3.0
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I simplified this loop. Currently,

  • the initial adj = g*p_approx and tx is computed twice
  • adj is recomputed in every step as g*p_approx where g is divided by 3 in every iteration

In this PR, instead

  • values in the initial iteration are only computed once
  • adj is scaled directly, without variable g and additional multiplication

@devmotion devmotion requested a review from andreasnoack May 11, 2022 12:48
iex = max(-5.0/a^2 - 1.0/p^0.2 - 34.0, sae)
acu = exp10(iex)
iex = -5.0/a^2 - 1.0/p^0.2 - 34.0
acu = max(exp10(iex), 10 * fpu) # 10 * fpu instead of fpu avoids hangs for small values
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay. Generally, I believe this calculation is pretty ad hoc anyway.

@devmotion devmotion merged commit a615557 into master May 17, 2022
@devmotion devmotion deleted the dw/nohanginbetainv branch May 17, 2022 20:11
devmotion added a commit that referenced this pull request May 17, 2022
* Limit number of Newton iterations in beta_inc_inv and allow much
smaller starting values.

* Adjust tolerance instead of number of iterations

Co-authored-by: Andreas Noack <andreas@noack.dk>
devmotion added a commit that referenced this pull request May 24, 2022
* Fix hangs in `beta_inc_inv` (alternative/extension of #396) (#399)

* Limit number of Newton iterations in beta_inc_inv and allow much
smaller starting values.

* Adjust tolerance instead of number of iterations

Co-authored-by: Andreas Noack <andreas@noack.dk>

* Release 1.8.5

Co-authored-by: Andreas Noack <andreas@noack.dk>
if x > .9999
x = .9999
end
x = clamp(x, fpu, prevfloat(1.0))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change, or rather the lower bound fpu, break examples such as JuliaStats/StatsFuns.jl#145. Unfortunately, the alternative 1e-200 proposed in #396 breaks that example as well.

devmotion added a commit that referenced this pull request Jun 1, 2022
stevengj pushed a commit that referenced this pull request Jun 1, 2022
devmotion added a commit that referenced this pull request Jun 1, 2022
stevengj pushed a commit that referenced this pull request Jun 2, 2022
* Fix `beta_inc_inv` bug introduced in #399 (#401)

* Release 1.8.6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants