Skip to content

EllipticF, EllipticE and EllipticPi at the working precision - #557

Open
enumeratio wants to merge 1 commit into
cortex-js:mainfrom
enumeratio:elliptic-incomplete-big
Open

enumeratio wants to merge 1 commit into
cortex-js:mainfrom
enumeratio:elliptic-incomplete-big

Conversation

@enumeratio

Copy link
Copy Markdown
Contributor

EllipticF(φ, m), EllipticE(φ, m), EllipticPi(n, φ, m) and EllipticPi(n, m) give N() at the precision asked for real operands. Fixes #550

  • numerics/elliptic-big.ts: R_F, R_J (and R_D = R_J(x, y, z, z)) by the duplication theorem in BigDecimal (DLMF 19.36(i); the series at order 6 as in carlsonRF/carlsonRJ), R_C(1, 1+e) by series for |e| <= 0.01 and by atan/atanh otherwise. The Legendre forms are DLMF 19.25.5, 19.25.9 and 19.25.14, with φ + kπ from 2k times the complete integral (EllipticK/EllipticE kernels already there, and the new complete Π).
  • Cancellation is measured rather than guessed. The kernel adds up the digits lost by: φ - kπ for a large k, π/2 - |r| (so cos φ comes from sin(π/2 - |r|) and keeps its relative accuracy at the double nearest π/2), 1 - m sin²φ (formed as c² + (1-m) sin²φ, so m near 1 loses nothing), the same for 1 - n sin²φ, the R_C(1, 1+e) argument near -1, and the sums s·R_F ∓ …·R_D/R_J and 2kC + …. When the loss is more than the 15 guard digits hold it runs again with that many more, up to 400, and past that returns null. Every loop has a named cap (CARLSON_MAX_DUPLICATIONS, 1000), and the duplication stops when the spread of the arguments is below 10^(-precision/6) of their mean, which puts the truncation error of the order-6 series below a unit in the last place.
  • applyN (boxed-expression/apply.ts): a bigFn result of null declines (the call stays unevaluated) instead of falling to the machine kernel. A NaN still means "not real here, try the next kernel", which is how the complex-valued cases keep their double complex answer.
  • EllipticF and both EllipticPi forms are wrapped in guardDigitsN, as EllipticE is, so an exact operand (Pi/3) is read to guard digits under N().
  • EllipticPi where 1 - n sin²φ < 0 (n > 1 for the complete form) is the Cauchy principal value, as the machine kernel gives it (EllipticPi with a complex parameter gives ComplexInfinity, and Range past 2^53 rounds its elements #422: the real part of mpmath's value for n + i0⁺). R_J for p < 0 is DLMF 19.20.14 in terms of R_J at a positive p', R_F and R_C; the cancellation of its three terms is measured like the others.

Principal value (n > 1): 8 rows (complete and incomplete, amplitudes to 3.2 and 1.5707963267948966, m from -3 to 0.99, n from 1.5 to 100) against the real part of mpmath's ellippi, at 30, 50 and 100 digits, except two rows where mpmath's own 100-digit (and, for one, 50-digit) value loses digits to cancellation at 30 extra working digits and CE agrees with itself at 50, 100 and 160. Five points (n = 1.5 to 4) were also checked at 40 digits against a principal-value integral with mpmath.quad (the pole subtracted and integrated in closed form), which reproduces the real part of ellippi to 40 digits.

Accuracy, against mpmath 1.3.0 (40 more working digits, decimal operands, a 15% sample re-checked at 70 and 110 digits), N() at 30, 50 and 100 digits, 1165 points with a real value (|φ| 1e-5 to 250 and 1.5707963267948966; m from -100 to 50, 1 and 1 - 1e-20; n from -5 to 1 - 1e-20 and 1.5 where 1 - n sin²φ > 0):

head points declined worst, digits lost
EllipticF(φ, m) 118 0 0.7
EllipticE(φ, m) 123 0 0.7
EllipticPi(n, φ, m) 868 0 0.7
EllipticPi(n, m) 56 0 0.7

The hard regions are in the grid: m = 1 - 1e-20 and m = 1 with φ at the double nearest π/2, n = 1 - 1e-20 for the complete form and 0.999 with φ near π/2 for the incomplete, |m| up to 1e6 in the tests, φ up to 250 (k = 80 periods). A call takes about 1 ms at 30 digits and 5 ms at 100.

Tests: test/compute-engine/elliptic-incomplete-big.test.ts has 40 mpmath rows at 30/50/100 digits, exact operands at 30 and 50 (they stay symbolic under evaluate()), a float operand under evaluate(), the machine and compiled-JavaScript routes at double precision (rows whose double operand is not the decimal one, near m = 1, are skipped there), the principal-value rows, the complex value of real operands (EllipticF(2, 2) at 30 digits is the double complex value), and the infinite poles. Not run here: the neighbour files that mention Elliptic (listed in the summary).

Not touched: the complex results of real operands stay doubles at every precision, as for the other special functions (docs/plans/2026-09-27-big-decimal-imaginary-part.md, D4); the compiled targets use the machine kernels, unchanged.

…i(n|m) N() at the precision asked for real operands with Carlson's R_F, R_D and R_J summed by duplication in BigDecimal (DLMF 19.25, 19.36), with a null bignum result that declines instead of falling back to doubles; read exact operands of F and Π to guard digits; add mpmath tables at 30, 50 and 100 digits. Fix cortex-js#550
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

EllipticF(φ, m), EllipticE(φ, m) and EllipticPi answer N(…, d) with the digits of a double

1 participant