Repository navigation
fix: tie-safe rank-CDF mesh transform; NumPy stays fp64 under mixed precision (#552) - #556
Merged
Merged
Conversation
…der mixed precision (#552) The JAX-vs-NumPy log-likelihood gap of the RectangularBilinearAdaptImage + Adapt smoke script (15.5 nats on the 316-pixel dataset, 5.8 nats on the old 716-pixel one) is not a mixed-precision loss: mixed vs fp64 within either backend differs by ~1e-4 nats, the fnnls/jaxnnls solvers agree to 1e-12 on identical inputs, and the FFT and real-space convolutions agree to 1e-16. The whole gap sits in `create_transforms_rank`'s weighted branch, which accumulates the adapt-image weights in `argsort` order: `np.argsort` (quicksort, unstable) and `jnp.argsort` (stable) order the heavily tied coordinates of an unlensed image grid differently (553 of 632 permutation entries, 80 sub-pixels landing in a different mesh cell). - interpolator/rectangular.py: every point of a tie block takes the block's final cumulative weight (the right-continuous empirical CDF), so the transform depends only on the point multiset, not on the backend's tie-break or the input order. Untied points are unchanged bit-for-bit. - mapper_util.mapping_matrix_from / Convolver.mapping_matrix_native_from: the fp32 allocation under `use_mixed_precision` is now JAX-only, so the NumPy backend is the fp64 reference the Settings docstring promises. - inversion_util.curvature_matrix_via_mapping_matrix_from: one fp64 expression on every backend; the former fp32 `1/noise_map` on the JAX branch never produced an fp32 accumulation (the blurred mapping matrix is already fp64) and only weighted F inconsistently with D. - Settings docstring updated to match. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018bJQFXsFc4iCqpUywydex2
#552) - interpolator/test_rectangular.py: the weighted rank CDF on tied points agrees between NumPy and JAX, is invariant to the input ordering, keeps the plain cumsum/interp value on untied points, and takes the whole block's weight at a tie. - mappers/test_mapper_util.py: mapping_matrix_from stays float64 under numpy with use_mixed_precision=True, is float32 under jax.numpy, float64 on both without the flag. - inversion/test_inversion_util.py: the curvature matrix under mixed precision matches NumPy on the JAX backend and is weighted with the fp64 1/noise_map (a noise value whose reciprocal is not float32-representable). - operators/test_convolver.py: the native cube dtype per backend, and the mixed-precision JAX FFT blurred matrix against the NumPy fp64 result. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018bJQFXsFc4iCqpUywydex2
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.
Summary
Closes #552.
The JAX-vs-NumPy log-likelihood gap of the
RectangularBilinearAdaptImage+Adaptparity script (15.5 nats on the 316-pixelautogalaxy_workspace_testdataset, 5.8 nats on the old 716-pixel one) is not a mixed-precision loss. Measured on freshly simulated data: mixed vs fp64 within either backend differs by ~1e-4 nats, fnnls and jaxnnls agree to 1e-12 on identical inputs, and the real-space and FFT convolutions agree to 1e-16. The whole gap sits increate_transforms_rank(the"rank"lattice transform of theRectangularBilinear*meshes): it accumulates the adapt-image weights inargsortorder, andnp.argsort(quicksort, unstable) andjnp.argsort(stable) order the heavily tied coordinates of an unlensed image grid differently — 553 of 632 permutation entries, 80 sub-pixels landing in a different mesh cell. The transform was ill-defined on ties (NumPy alone was not invariant to the input ordering). Full attribution tables: #552 (comment).Three changes:
create_transforms_rank, weighted branch (autoarray/inversion/mesh/interpolator/rectangular.py): every point of a tie block takes the block's final cumulative weight — the right-continuous empirical CDF, viasearchsorted(side="right") - 1. The transform now depends only on the point multiset: backend- and order-independent,jit-traceable, bit-identical on untied inputs. The unweighted branch is untouched.use_mixed_precision(mapper_util.mapping_matrix_from,Convolver.mapping_matrix_native_from): the fp32 allocation is now JAX-only, so a NumPy fit is the fp64 reference theSettingsdocstring promises.curvature_matrix_via_mapping_matrix_fromweights with fp641/noise_mapon every backend (onexp-agnostic expression). The old JAX branch never accumulated in fp32 — the blurred mapping matrix is already fp64 — it only rounded1/σinFinconsistently with the fp64D.Settings.use_mixed_precisiondocstring corrected to match. Firstuse_mixed_precisionunit tests intest_autoarray(12 new tests).After the fix, on the same data: the parity assertion gap is −8e−5 nats (316 px) and −8e−4 nats (716 px); at fp64 the backends agree to 1e−11; the mapper's sub-pixel assignment is identical entry for entry. The smoke script passes.
API Changes
No symbol was added, removed or renamed. Two behaviour changes:
use_mixed_precision=Trueno longer changes anything on the NumPy backend (was: fp32 mapping matrix and native cube). NumPy mixed and NumPy fp64 fits are now bit-identical.See full details below.
Test Plan
python -m pytest test_autoarray— 1511 passed, 73 skipped, 0 failed (with numba installed; without it 8 pre-existing w-tilde tests raise the documentedInversionException).test_rectangular.py(tied weighted points: NumPy = JAX to 1e-12, permutation-invariant, untied points bit-identical to the old formula, block-total at a tie),test_mapper_util.pyandtest_convolver.py(dtype per backend under the flag),test_inversion_util.py(fp64 weighting on JAX, matches NumPy).autogalaxy_workspace_test/scripts/imaging/jax_likelihood/rectangular.pyagainst this branch: NumPy −791.956, JIT −792.88, PASS atrtol=2e-2(the 0.9-nat residual there is the script's ownimport jax-before-autogalaxyx64-off defect, a workspace follow-up).Full API Changes (for automation & release notes)
Removed
Added
Renamed
Changed Signature
Changed Behaviour
autoarray.inversion.mesh.interpolator.rectangular.create_transforms_rank(traced_points, mesh_weight_map=..., xp=)— weighted branch: tied coordinates take the tie block's final cumulative weight (right-continuous empirical CDF). Untied inputs unchanged. AffectsRectangularBilinearAdaptImage(and any"rank"-transform mesh with a weight map) on data whose traced source-plane coordinates tie exactly — in practice unlensed image grids.autoarray.inversion.mappers.mapper_util.mapping_matrix_from(..., use_mixed_precision=True, xp=np)— returns float64 (was float32). Underxp=jax.numpystill float32.autoarray.operators.convolver.Convolver.mapping_matrix_native_from(..., use_mixed_precision=True, xp=np)— float64 cube (was float32). JAX unchanged.autoarray.inversion.inversion.inversion_util.curvature_matrix_via_mapping_matrix_from(...)— underxp=jax.numpywithsettings.use_mixed_precision=True,Fis weighted by fp641/noise_map(was float32-rounded). Result dtype float64 on every backend, as before.Migration
Generated by the PyAutoLabs agent workflow.
🤖 Generated with Claude Code
https://claude.ai/code/session_018bJQFXsFc4iCqpUywydex2
Generated by Claude Code