Add ellipke for simultaneous K(m) and E(m) computation#510
Open
mgyoo86 wants to merge 14 commits intoJuliaMath:masterfrom
Open
Add ellipke for simultaneous K(m) and E(m) computation#510mgyoo86 wants to merge 14 commits intoJuliaMath:masterfrom
ellipke for simultaneous K(m) and E(m) computation#510mgyoo86 wants to merge 14 commits intoJuliaMath:masterfrom
Conversation
Replace O(n) linear if-elseif chain with O(log n) binary tree branching for polynomial table selection. Key optimizations: - Add _ellipk_core and _ellipe_core with binary tree structure - Use integer index (idx = unsafe_trunc(Int, m*10)) for faster icmp vs fcmp - Simplify wrapper functions to handle only negative m and special cases - Add special handling for extremely negative m where x rounds to 1.0 Performance improvement: - ellipk: 1.33x average speedup (10.5ns -> 7.9ns) - ellipe: 1.46x average speedup (12.0ns -> 8.2ns) - Largest gains for m in [0.65, 0.875]: 1.5x-1.85x speedup All polynomial coefficients unchanged - only branching structure optimized.
Add boundary value tests at each polynomial table transition (m = 0.0, 0.1, ..., 0.9) with prevfloat/nextfloat continuity checks. Add negative m transformation tests and special value edge cases (NaN, Inf, -Inf, DomainError).
Extract polynomial lookup into separate _ellipk_horner_table function with linear if-else structure (LLVM optimizes to switch/jump table). Simplify _ellipk_core to handle only edge cases and delegate to table. Phase 1 of ellip.jl refactoring plan.
Extract polynomial lookup into separate _ellipe_horner_table function with linear if-else structure (LLVM optimizes to switch/jump table). Simplify _ellipe_core to handle only edge cases and delegate to table. Phase 2 of ellip.jl refactoring plan.
Rename internal functions to better reflect their domain (m >= 0). Also reorder functions: docstring → public API → wrapper → impl → table.
Remove separate _ellipk_horner_table/_ellipe_horner_table functions and inline the polynomial lookup directly into _ellipk_core/_ellipe_core. Rename _ellipk_nonneg/_ellipe_nonneg back to _ellipk_core/_ellipe_core. Simpler code structure without performance regression.
…unctions" This reverts commit 162e569.
- Add @BoundsCheck to horner_table functions for debug safety - Use @inbounds when calling horner_table from nonneg functions - Change else to explicit elseif idx == 9 for clarity - Optimize ellipe idx=9: call _ellipk_horner_table directly instead of _ellipk_nonneg to avoid redundant edge case checks
Using 'else' as fallback instead of explicit 'elseif idx == 9' allows LLVM to generate a cleaner switch/jump table with a default case, resulting in ~1ns faster execution.
Computes both complete elliptic integrals as (K, E) tuple by reusing existing _ellipk_horner_table and _ellipe_horner_table functions. More efficient than separate ellipk/ellipe calls when both are needed. - Support for Float16, Float32, Float64 - Handle edge cases: m=0, m=1, m<0, NaN, -Inf - Comprehensive test coverage with bit-wise equality checks
- ellipke(missing) returns (missing, missing) following Base.sincos convention - Add test for missing behavior
For idx == 9 (0.9 <= m < 1.0), compute K and E together in _ellipke_near_one to avoid duplicate log(qd) calls. This gives ~1.8x speedup over naive (ellipk + ellipe) in this range.
2 tasks
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #510 +/- ##
==========================================
- Coverage 94.17% 94.16% -0.02%
==========================================
Files 14 14
Lines 2969 2997 +28
==========================================
+ Hits 2796 2822 +26
- Misses 173 175 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
Depends on: #509 (perf/elliptic-optimization) — this PR builds on the refactored elliptic integral infrastructure.
Motivation
In many physics and engineering applications, both complete elliptic integrals K(m) and E(m) are needed together—for example, when calculating magnetic fields from coils (as in VacuumFields.jl).
Currently, users have to call
ellipkandellipeseparately, which duplicates overhead for input validation and index calculation. MATLAB providesellipkefor exactly this reason, and it makes sense forSpecialFunctions.jlto offer a similar optimized path.Implementation
This implementation computes both integrals in a single pass. The speedup comes from two sources:
idx = trunc(m * 10)) is computed once and reused for both K and E.logterm is computed once and shared between K and E.Benchmark
Environment
Calling
ellipke(m)is consistently faster than(ellipk(m), ellipe(m)). The gain is most significant (~2.3x) near m = 1 due to shared log calculation.Note: The (ellipk + ellipe) baseline already includes the optimizations from PR #509.
Usage
The function returns a tuple, behaving exactly like the standalone counterparts:
Tests
ellipk/ellipecalls across all polynomial branches0,1,-Inf,NaN, and negative valuesDomainErrorfor m > 1