PolyGamma(m, z) at a complex z; Part of #340 - #359
Merged
Merged
Conversation
…5.15.2) and a digamma asymptotic series for m = 0; stay unevaluated where Euler-Maclaurin cancellation leaves no accurate digits; mark the JS compile lane real-only to match. Part of cortex-js#340
Dual review (Codex + Claude), every value checked against mpmath.
Correctness:
- The shift loops ran once per unit of -Re(z) with no cap: PolyGamma(0,
-1e12 + i).N() hung the thread, and the cancellation guard declined 11%
of a left-half-plane grid. Both are replaced by the reflection formula
for Re(z) < 1/2 (DLMF 5.15.6), with the m-th derivative of cot(pi z)
taken from whichever of three series loses the fewest digits (partial
fractions, Fourier, or the cot polynomial); cot(pi z) is built from
e^{2 pi i z} so it does not cancel for a large |Im z|. Every declined grid
point now answers within 1e-14, and the cost no longer depends on Re(z).
- m! and zeta were combined outside the double range: PolyGamma(171, 1+i)
gave ComplexInfinity (a false pole; the value is -1.6e283 + 9e248i) and
PolyGamma(100, 10000+i) a false 0 (the value is -9.4e-245). Values are
carried as a mantissa times a power of two; an unrepresentable result
stays symbolic. Two overflow/underflow causes in the complex helpers were
fixed on the way (log of a huge argument, abs of a tiny one).
- The digamma tail formed w^2 … w^28 explicitly and overflowed for |z| above
1e154 (PolyGamma(0, 1e160+i) was ComplexInfinity); it uses powers of
1/w^2.
- Digamma(z) and Trigamma(z) now use the same complex kernel instead of
staying symbolic while PolyGamma(0|1, z) evaluated.
- An order above 10 000 stays symbolic (the cost grows linearly with it).
Tests and documentation: the decline test pins the mpmath value; the
"answers accurately" test has an oracle value; CHANGELOG under
[Unreleased]; the remaining decline region (orders above 100 within 1e-5 of
a half-integer on the real axis) is in ROADMAP; `isComplex` spelling; the
unrelated Clamp reformat in error-model.test.ts is dropped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Member
|
Thanks, merged, with a review pass on your branch as
The one region that still declines (orders above 100 within |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #340
PolyGamma(m, z)now evaluates at a complexzfor every integer order. For m ≥ 1 it uses ψ⁽ᵐ⁾(z) = (−1)^(m+1) m! ζ(m+1, z) (DLMF 5.15.2) on the complexHurwitzZetakernel; the digamma gets its own shift and asymptotic series. A non-positive integerzis still the pole at every order, and realzis unchanged.At a high order and large negative Re(z), the Euler–Maclaurin sum cancels past what doubles can hold. There it stays unevaluated rather than answer, using a guard on the ratio of the largest term to the result. Checked against mpmath
polygammafor m ≤ 8 over |z| ≤ 50 in every quadrant, near the poles and between them: no answered value off by more than 4e-13.Also: the compiled JavaScript
PolyGammais now marked real-only, asZetaandHurwitzZetaare. Before, a complex-typed argument compiled and ran the real kernel on it.