P2.3 Ellipsoidal/triaxial potentials: backend-agnostic namespace-swap#895
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## feat/backends #895 +/- ##
===============================================
Coverage 99.92% 99.92%
===============================================
Files 228 228
Lines 34213 34941 +728
Branches 712 714 +2
===============================================
+ Hits 34186 34914 +728
Misses 27 27 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a579adf to
99e9be4
Compare
… tests
Complete the backend-agnostic migration of the ellipsoidal/triaxial family
beyond the main compute methods done in the prior commit:
- Migrate the CLOSED-FORM _mass methods to the backend namespace:
* PerfectEllipsoidPotential._mass: numpy.arctan -> xp.atan, numpy.pi -> math.pi
* TriaxialNFWPotential._mass: numpy.log -> xp.log, numpy.pi -> math.pi
* TriaxialHernquistPotential / TriaxialJaffePotential._mass: numpy.pi -> math.pi
These now run and differentiate under numpy / jax / torch (values bit-identical
to before: math.pi == numpy.pi, numpy.atan/log are the same ufuncs).
- Leave (and explicitly annotate as Pspecial-blocked) the _mass methods with no
backend-agnostic replacement:
* TwoPowerTriaxialPotential._mass / _psi / __init__: scipy.special.hyp2f1, gamma
* TriaxialGaussianPotential._mass: scipy.special.erf
* EllipsoidalPotential._mass (generic, incl. PowerTriaxial): scipy.integrate.quad
The Gauss-Legendre compute path was already migrated; the glorder=None
scipy.integrate fallback stays deferred. The init-time amp/rotation setup and
the numpy-gated quadrature hash cache remain numpy by design.
- Tests (tests/test_backend_ellipsoidal.py):
* numpy-vs-jax-vs-torch parity + grad-vs-FD for the four migrated closed-form
_mass methods.
* ROTATED (zvec/pa) value-parity coverage for the rotated compute path
(forces / phitorque / dens / migrated _evaluate) across all subclasses, plus
rotated _evaluate grad-vs-FD — the rotated path previously had no coverage.
All 393 backend-ellipsoidal tests pass under numpy+jax+torch; numpy potential
mass/triaxial sanity tests unchanged.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ntities Replace the finite-difference autodiff checks (test_grad_evaluate / test_grad_rforce vs FD, rtol 1e-5) with exact analytic-identity checks at rtol=1e-9. Under galpy's sign conventions autodiff of a lower-order quantity equals the negative of the corresponding analytic higher-order quantity: AD(_evaluate wrt R/z/phi) == -_Rforce / -_zforce / -_phitorque AD(_Rforce wrt R/z/phi) == -_R2deriv / -_Rzderiv / -_Rphideriv AD(_zforce wrt z/phi) == -_z2deriv / -_phizderiv AD(_phitorque wrt phi) == -_phi2deriv These hold to round-off (lower and higher share the same Gauss-Legendre quadrature nodes), so they cross-validate the hand-coded triaxial forces and the phi-dependent Hessian, far more stringently than finite differences. The new parametrized test (potentials x AD_BACKENDS x identity pairs) gates each pair on both methods being namespace-migrated: the six force/2nd-derivative pairs run for every potential (they depend only on the pure-arithmetic _mdens), while the three _evaluate pairs are skipped for TwoPowerTriaxialPotential (its _psi uses scipy.special.hyp2f1 and is Pspecial-deferred). 60 identity tests per AD backend (54 for the six eval-migrated potentials + 6 force/Hessian pairs for TwoPower). The mass-gradient and rotated-frame FD checks are kept (no analytic counterpart). Green on numpy + jax + torch. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 10, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 12, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 13, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 15, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 17, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 19, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 19, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jobovy
added a commit
that referenced
this pull request
Jun 25, 2026
…#895) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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 the P2.x potential namespace-swap series (→ #892).
Migrated:
EllipsoidalPotentialbase (all force/2nd-deriv/dens + the Gauss-Legendre quadrature path) + PerfectEllipsoid, PowerTriaxial, TriaxialGaussian/Hernquist/Jaffe/NFW, TwoPowerTriaxial (forces/2nd-derivs/dens).Key refactor: the mutable per-instance quadrature cache (
_force_hash/_cached_Fx…) is now read/written only whenget_namespace(...) is numpy; the traced (jax/torch) path computes the shared GL integral as a pure local and never touches self-state (verified by cache-isolation tests).coords.cyl_to_rect+ the rotation matmul inlined as backend-agnostic arithmetic.Deferred:
_mass(scipy.integrate/special), the scipy.integrate fallback quadrature path, TwoPowerTriaxial_evaluate(hyp2f1).Tests:
tests/test_backend_ellipsoidal.py— 258 passed (parity + grad-vs-FD + cache-isolation). Numpy byte-identical incl. rotated cases.🤖 Generated with Claude Code