gamma(a, x) and loggamma(a, x) throw for every real shape a > 51
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 25/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Domain
- backend
Research direction
The failure comes from _gamma and _loggamma in src/gamma_inc.jl, which route through _expint in src/expint.jl, where the order limit |ν| > 50 is enforced. Start by reproducing loggamma(52, 1e5) and gamma(52, 10.0), then read the existing gamma_inc and gamma_inc_cf paths the issue proposes reusing. Done means both functions return finite results for a > 51 that match a high-precision reference, with existing tests still passing. The author says they plan to open a PR, so check for one before starting.
Written by the indexing model from the issue text.
Description
gamma(a, x) and loggamma(a, x) throw for every real shape a > 51, at every x:
using SpecialFunctions
loggamma(51, 1e5) # fine: -99424.35322663146
loggamma(52, 1e5) # ArgumentError
gamma(52, 10.0) # ArgumentError
loggamma(100, 1e5) # ArgumentError; log Γ(100, 1e5) ≈ -98860.2193884875
On SpecialFunctions 2.9.1 (master at d8ffcc1b456171884112a157b7be1bc4707f77be behaves the same), Julia 1.10.12:
loggamma(51, 100000.0) => -99424.35322663146
loggamma(52, 100000.0) => ArgumentError: Unsupported order |ν| > 50 off the positive real axis
gamma(52, 10.0) => ArgumentError: Unsupported order |ν| > 50 off the positive real axis
loggamma(100, 100000.0) => ArgumentError: Unsupported order |ν| > 50 off the positive real axis
The boundary is exactly a = 51: loggamma(51.0, 10.0) works, loggamma(51.0001, 10.0) throws.
Cause
Both functions are evaluated through the exponential integral for every argument — gamma(a, x) = x^a E_{1-a}(x) in _gamma, and a log x + log(expintx(1 - a, x)) - x in _loggamma (src/gamma_inc.jl). _expint refuses orders |ν| > 50 that are not positive real (src/expint.jl, "Unsupported order |ν| > 50 off the positive real axis"), and ν = 1 - a is a negative real below -50 as soon as a > 51. The docstrings promise "arbitrary real or complex a and x".
The route is also the wrong one for large shapes in principle, independent of the order limit: Γ(a, x) overflows Float64 for small x once a > 171, and underflows for x ≫ a, long before log Γ(a, x) leaves the representable range — Γ(200, 700) ≈ 2.06e262 is representable, but Γ(200) = Inf and Q(200, 700) ≈ 0.0 are not; log Γ(170, 1210) ≈ -10.22 while Q(170, 1210) is below floatmin. expintx cannot carry those magnitudes either.
Where it shows up
StatsFuns.gammalogccdf falls back to loggamma(k, x) - loggamma(k) once the survival probability underflows (src/distrs/gamma.jl), so Distributions.logccdf(Gamma(100.0), 1e5) throws this error instead of returning -99219.35359385707 (the exact integer-shape identity log Q(k, x) = -x + log(sum(x^j / j! for j in 0:k-1)) at 512 bits). The same failure reaches logccdf of Erlang, Chisq (ν > 102) and Chi, and logcdf of InverseGamma and Poisson (x ≥ 51 when the probability underflows) — every log tail of a gamma-family distribution with shape above 51.
Proposed fix
For real a > 0 and finite x ≥ 0, compute both functions from the incomplete gamma ratios that gamma_inc already provides for exactly this region: Γ(a, x) = Q(a, x) Γ(a) and log Γ(a, x) = log Γ(a) + log Q(a, x) (with log1p(-P) when Q is the complement), and when Q(a, x) underflows, log Γ(a, x) = a log x - x + log(CF) with the Legendre continued fraction that gamma_inc_cf already evaluates (DLMF 8.9.2) — it is O(1/x) there, so it never underflows. gamma(a, x) takes Γ(a) Q(a, x) while both are representable and exp(loggamma(a, x)) otherwise. Every other argument (non-positive or complex a, negative or complex x) keeps the expint route. I have this implemented and tested against a 256-bit quadrature of Γ(a, x) (which agrees with MPFR's mpfr_gamma_inc to ~1e-50 where that is fast enough to run) over shapes from 0.1 to 1e8, including both overflow regimes; the relative error stays below 3e-15 everywhere, and for a ≤ 51 it is never worse than the current expint route. I will open a PR against master.
expint(ν, z) itself keeps its order limit; the same identity (E_ν(z) = z^{ν-1} Γ(1 - ν, z)) would lift it for real ν < -50 on the positive real axis, but that is a separate change.
- Dominant language
- Julia
- Stars
- 384
- Forks
- 115
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 2
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from JuliaMath/SpecialFunctions.jl
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
JuliaMath/SpecialFunctions.jl#570 ·
-
Difficulty 5/5 Over a week Newbie friendliness 40/100
JuliaMath/SpecialFunctions.jl#569 · 2 comments ·
-
Difficulty 4/5 1-2 days Newbie friendliness 56/100
JuliaMath/SpecialFunctions.jl#563 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
JuliaMath/SpecialFunctions.jl#560 ·
-
Bug: both `erfcinv(2big"0.8")` and `erfcinv(2big(0.41))` do not terminate.Possibly taken @lrnv claimed this 46 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 64/100
JuliaMath/SpecialFunctions.jl#557 · 3 comments ·
All issues in JuliaMath/SpecialFunctions.jl
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Add project: Typst_jll.jlPossibly taken @github-actions claimed this 1 day ago. Openadd-project
Difficulty 1/5 Under an hour Newbie friendliness 76/100
YDX-2147483647/best-of-typst#590 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100