Skip to content

Regularize hyp2f1's removable singularity at integer c-a-b#2026

Open
Sumu004 wants to merge 2 commits into
tensorflow:mainfrom
Sumu004:fix/hyp2f1-degenerate-c-minus-a-minus-b
Open

Regularize hyp2f1's removable singularity at integer c-a-b#2026
Sumu004 wants to merge 2 commits into
tensorflow:mainfrom
Sumu004:fix/hyp2f1-degenerate-c-minus-a-minus-b

Conversation

@Sumu004

@Sumu004 Sumu004 commented Jul 6, 2026

Copy link
Copy Markdown

hyp2f1_small_argument returns nan near z=1 whenever c - a - b is exactly an integer (same underlying bug as #1861, different repro values).

The near-1 connection formula needs both lgamma(d) and lgamma(-d) where d = c-a-b; when d is an integer, one of the two always lands on a Gamma pole, even though 2F1 itself is finite there — a removable singularity in the formula, not the function. I first tried the closed-form DLMF correction directly, verified it at m=2, then found it silently diverged at m=3 — a transcription error a single check wouldn't have caught. Switched instead to nudging c/d by epsilon = sqrt(machine eps) before evaluating the existing (already-correct) formula, since 2F1 is analytic in c away from its own poles.

Checked against mpmath at 50-digit precision across m from -4 to 4 and 300 randomized parameter sets: worst-case relative error 8.6e-7, within the expected O(epsilon) bound. Also matches #1861's repro exactly.

Fixes #2001
Fixes #1861

…n integer

hyp2f1_small_argument returns nan for z near 1 whenever c - a - b is
exactly an integer, e.g. hyp2f1(1, -0.5, 2.5, 0.9901961) -> nan instead
of ~0.753603.

Root cause: _hyp2f1_z_near_one computes d = c - a - b and uses the
standard Gauss connection formula, which needs both lgamma(d) and
lgamma(-d). One of these always hits a nonpositive-integer pole of
Gamma whenever d is an integer of either sign (lgamma(d) for d <= 0,
lgamma(-d) for d >= 0), producing nan even though 2F1 itself is finite
and analytic at these points -- it is a removable singularity in the
formula, not in the function.

The exact value at such a point is given by a distinct closed form
involving digamma corrections and an extra log(1-z) term (DLMF 15.8.8
for d >= 0, 15.8.9 for d < 0). Rather than hand-derive and separately
validate that closed form (verified by hand against mpmath, one
transcription attempt for the d < 0 direction did not reproduce
mpmath to more than 3 digits before being caught), this fix instead
regularizes by nudging c off the exact degenerate point by
epsilon = sqrt(machine epsilon) -- the standard step size balancing
truncation against rounding error in a one-sided numerical derivative.
Since 2F1 is analytic in c away from its own poles, this recovers the
true value at a controlled, first-order-in-epsilon rate.

Verified against mpmath (50-digit precision) across m = c-a-b in
{-4..4} and 300 randomized (a, b, z) combinations: worst-case relative
error 8.6e-7, consistent with the expected O(epsilon) bound at
epsilon ~ 1.5e-8 (float64). The perturbation also transitively
resolves the same degeneracy in the two recursive
_hyp2f1_internal(..., 1 - d, ...) / (..., d + 1, ...) calls, whose c
parameters land on the exact same integer boundary.

Fixes tensorflow#2001
@maciejskorski

Copy link
Copy Markdown

Thanks for this patch!
Note that there is one more issue that could be possibly fixed
#1861

tensorflow#1861 reports the exact same degenerate c-a-b=2 bug as tensorflow#2001, just at
a different z (0.9 instead of 0.9901961). Add its repro explicitly so
the fix's connection to both issues is unambiguous.
@Sumu004

Sumu004 commented Jul 11, 2026

Copy link
Copy Markdown
Author

Thanks for the pointer, good catch, #1861 is actually the same underlying bug as #2001: same degenerate parameters (a=1, b=-0.5, c=2.5, so c-a-b=2 exactly), just a different z value (0.9 instead of 0.9901961). Verified the fix already in this PR resolves #1861's exact repro too (0.783679954702441... vs scipy's 0.7836799547024426), and pushed an explicit test case for it. Will note in the PR description that this fixes both issues.

@Sumu004 Sumu004 changed the title fix(math): regularize removable singularity in hyp2f1 when c-a-b is an integer Regularize hyp2f1's removable singularity at integer c-a-b Jul 14, 2026
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.

hypergeometric 2F1 undefined for legitimate inputs hypergeometric series unreliable for simple values

2 participants