orbit: make Orbit.L() cross-product namespace-consistent#985
Conversation
Under a forced jax/torch backend, L()'s position/velocity accessors were inconsistent (some returned a backend Tensor, some stayed numpy), so `out = numpy.zeros(...); out[...,0] = y*vz - z*vy` hit `numpy.ndarray * Tensor -> TypeError`. Resolve a single namespace from all six components (`xp = get_namespace(x,y,z,vx,vy,vz)`), coerce them onto xp only when xp is not numpy, and assemble the cross product with `xp.stack(..., axis=-1)` (also avoids jax's immutable-array in-place-assign). numpy path byte-identical: get_namespace(<all numpy>) is numpy and the coercion block is skipped. xp.stack(axis=-1) of the F-contiguous accessor outputs is not C-contiguous for ensemble shapes, so on the numpy branch the result is passed through numpy.ascontiguousarray to restore the historical numpy.zeros()+assign C-contiguous layout -> byte-identical in BOTH data and flags (verified SHA-256 identical + C_CONTIGUOUS for single/ensemble/init-time L()). Fixes the torch `numpy.ndarray * Tensor` failure in Orbit.L(). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was no backend test for Orbit.L(). PR-6 made its phasedim==6 cross-product namespace-consistent; add jax + torch tests that integrate a backend orbit and call o.L(ts), asserting it returns a backend array matching the numpy/C reference (and a numpy-path no-change check) -- covering the new get_namespace+asarray+xp.stack coercion branch in the coverage shard. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## feat/backends #985 +/- ##
==============================================
Coverage 99.93% 99.93%
==============================================
Files 253 253
Lines 39803 39806 +3
Branches 839 836 -3
==============================================
+ Hits 39778 39781 +3
Misses 25 25 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
All-backend test status (jax / torch)Commit Green is achieved via the checked-in xfail-ledger ( Overall: jax: 1051 passed · 271 xfail · 725 deferred | torch: 766 passed · 1277 xfail · 1 deferred Ledger size: 2357 entries (jax=284, torch=2073).
Per-shard counts
|
What
Burndown PR-6 (
Orbits.pyonly). Under a forced jax/torch backend,L()'s position/velocity accessors were inconsistent (some returned a backend Tensor, some stayed numpy), soout=numpy.zeros(...); out[...,0]=y*vz-z*vyhitnumpy.ndarray * Tensor → TypeError. Resolve one namespace from all six components, coerce onto it only when non-numpy, and assemble viaxp.stack(..., axis=-1)(also avoids jax's immutable-array in-place assign).Byte-identity + review
numpy path byte-identical in data and flags:
get_namespace(<all numpy>)is numpy, coercion skipped, and the result is passed throughnumpy.ascontiguousarrayto restore the historicalnumpy.zeros()+assignC-contiguous layout (xp.stack(axis=-1)of the F-contiguous accessor outputs is otherwise non-C-contiguous for ensemble shapes — caught by adversarial review). Verified SHA-256 identical + C_CONTIGUOUS for single/ensemble/init-timeL(). Fixes the torchOrbit.L()TypeError; the other L() code paths (1D/2D/phasedim-5) are untouched.🤖 Generated with Claude Code