Skip to content
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

Rewrite gamma_inc_taylor and gamma_inc_asym more concisely #443

Merged
merged 17 commits into from
Oct 29, 2024

Conversation

BioTurboNick
Copy link
Contributor

@BioTurboNick BioTurboNick commented May 4, 2023

Many functions were translated directly from Fortran. This PR rewrites two of them using more concise Julia forms.

Ideally would like to eliminate the array allocation as well. (See #442) And now does!

@codecov
Copy link

codecov bot commented May 4, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 94.11%. Comparing base (7f42b47) to head (1c41a3d).
Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #443   +/-   ##
=======================================
  Coverage   94.10%   94.11%           
=======================================
  Files          14       14           
  Lines        2935     2905   -30     
=======================================
- Hits         2762     2734   -28     
+ Misses        173      171    -2     
Flag Coverage Δ
unittests 94.11% <100.00%> (+<0.01%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

src/gamma_inc.jl Outdated Show resolved Hide resolved
src/gamma_inc.jl Outdated Show resolved Hide resolved
src/gamma_inc.jl Outdated Show resolved Hide resolved
src/gamma_inc.jl Outdated Show resolved Hide resolved
src/gamma_inc.jl Outdated Show resolved Hide resolved
src/gamma_inc.jl Outdated Show resolved Hide resolved
@BioTurboNick
Copy link
Contributor Author

BioTurboNick commented May 4, 2023

Oops, left an nmc off that commit. Apologies to that person.

@heltonmc
Copy link
Member

heltonmc commented May 4, 2023

I know I originally suggested it but it would still be nice to know why it was implemented in this way to begin with as it is quite unusual and we are really just relying on the strength of the original test set here.

My last comment is just about the convergence check. My original suggestion (#442 (comment)) checked for relative tolerance where this just checks for absolute tolerance. It probably doesn't matter but if the final result is much smaller than 1 they could be different. (edit: relative check will be slower so not saying just to do it instead)

@BioTurboNick
Copy link
Contributor Author

BioTurboNick commented May 5, 2023

Alright, thanks! :-) I reverted to the original version, with your smaller suggestions. I kept the other one on another branch, gamma-fix2, just in case.

@heltonmc
Copy link
Member

heltonmc commented May 5, 2023

Sounds good 😊 I’ll let other people comment on what they think of the two versions.

This version will allocate though which might not solve your initial problem. If you want to sum in reverse order your probably best bet is to to use @nexprs to remove the array then can sum in any order you want. I’m not for sure it’s really needed here though but it would be nice since we are putting in the effort to make this allocation free !

Co-authored-by: David Widmann <devmotion@users.noreply.github.com>
@BioTurboNick
Copy link
Contributor Author

Ah, found an allocation-free way! Before I commit it:

# compute first 21 terms
ts = cumprod(ntuple(i -> x / (a + i), Val(21)))
last_t = findfirst(<(1.0e-3), ts)
last_t = last_t === nothing ? 20 : last_t - 1
next_t = last_t + 1

@BioTurboNick
Copy link
Contributor Author

BioTurboNick commented May 9, 2023

Ah, cumprod for tuples doesn't exist in Julia 1.3... any chance of dropping support for Julia before 1.6? (It was introduced in 1.5)

EDIT: Or can just define it here. Nevermind, too much to bring in.

@BioTurboNick
Copy link
Contributor Author

Bumping - any more work needed on this?

@BioTurboNick
Copy link
Contributor Author

What do I need to do to get this in? I have a couple bits of code that would really benefit from this change, and I'd like not to have to disable precompilation.

@ViralBShah
Copy link
Member

@devmotion Is this good to merge?

Project.toml Show resolved Hide resolved
break
end

# compute first 21 terms
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if summation in reverse order is necessary (something like #442 (comment) would be much simpler implementation-wise)? I wonder if there is any argument/comment regarding this choice in the original Fortran code - does anyone know where the current implementation was copied from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In theory better for numerical stability and precision to do it this way, because otherwise you can lose the accumulated effect of the small components when they're very much smaller than the large components, but I don't know in practice if/when it affects results of this function.

Copy link
Contributor Author

@BioTurboNick BioTurboNick Feb 14, 2024

Choose a reason for hiding this comment

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

I've found a couple possible sources, but I think I'm gathering that the impetus was to support arbitrarily low precision. Maybe with Float64 it works pretty much fine, which this package forces. But with lower precision, maybe the errors would add up too much if floating point math wasn't carefully managed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like @Sumegh-git and @simonbyrne were writing/reviewing the PR that introduced them, if they can comment :-)

Copy link
Member

Choose a reason for hiding this comment

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

does anyone know where the current implementation was copied from?

ACM TOMS (FREE ACCESS): https://dl.acm.org/doi/abs/10.1145/22721.23109
Fig. 2 in the paper shows the flowchart of the algorithm as a whole.

  • gamma_inc_taylor: Equ. 15
  • gamma_inc_asym: Equ. 16

The original paper is mentioned in the code comments.
Perhaps those comments should be moved to the function documentation.

# Reference : 'Computation of the incomplete gamma function ratios and their inverse' by Armido R DiDonato, Alfred H Morris.
# Published in Journal: ACM Transactions on Mathematical Software (TOMS)
# Volume 12 Issue 4, Dec. 1986 Pages 377-393
# doi>10.1145/22721.23109

Copy link
Contributor Author

@BioTurboNick BioTurboNick Feb 19, 2024

Choose a reason for hiding this comment

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

Thanks! In that case maybe this is the FORTRAN code itself? https://jblevins.org/mirror/amiller/inc_gam.f90

@BioTurboNick
Copy link
Contributor Author

Just realized I have merge access now. Would anyone object if I merged it as-is? It's a strict improvement on the status quo, even if it could be improved further. All tests green. I'll wait a week if I don't hear anything.

@BioTurboNick
Copy link
Contributor Author

With respect to bumping the minimum version of Julia from 1.3 to 1.5, the CI is no longer even testing against < v1.6

@ViralBShah
Copy link
Member

Please merge.

Copy link
Member

@inkydragon inkydragon left a comment

Choose a reason for hiding this comment

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

LGTM.

src/gamma_inc.jl Show resolved Hide resolved
src/gamma_inc.jl Outdated Show resolved Hide resolved
@devmotion
Copy link
Member

With respect to bumping the minimum version of Julia from 1.3 to 1.5, the CI is no longer even testing against < v1.6

That was fixed in #478.

@BioTurboNick BioTurboNick merged commit cf35c58 into JuliaMath:master Oct 29, 2024
10 checks passed
@devmotion devmotion mentioned this pull request Oct 29, 2024
Comment on lines +472 to +475
ts = cumprod(ntuple(i -> (a - i) / x, Val(21)))

# sum the smaller terms directly
first_small_t = something(findfirst(x -> abs(x) < 1.0e-3, ts), 21)
Copy link
Member

@andreasnoack andreasnoack Nov 22, 2024

Choose a reason for hiding this comment

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

It's good to avoid the heap allocation but this unconditionally computes all 21 elements and then subsequently does a search while the original breaks early while providing the index for free. Would it be possible to keep the benefits of the old implementation and still avoid the heap allocation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not one I was able to think of at the time, but it didn't seem to much matter in practice because the allocation was more expensive than the extra computations. I even tried a version that was a bit more conservative and in practice didn't seem to matter much at all on my system.

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.

6 participants