fix: make the interferometer simulator's @jax.jit path work - #423
Merged
Conversation
Follow-up to #420/#421, which fixed the imaging simulator and deliberately excluded the interferometer. Two causes, and one of them was not what #422 predicted. 1. Interferometer was not a registered pytree, so it could not cross the jit RETURN boundary. Added _register_interferometer_pytrees, mirroring _register_imaging_pytrees: `data` and `noise_map` dynamic; uv_wavelengths, real_space_mask, transformer, grids, the over-sample sizes and the Nones as aux. It must also register Visibilities and VisibilitiesNoiseMap — unlike Array2D on the imaging path, nothing else registers those, so they surfaced as bare leaves ("returned a value of type Visibilities at output component [0]"). 2. `via_image_from` called `transformer.visibilities_from(image=image)` WITHOUT `xp=xp`. That is the whole of the second fix — one line. #422 characterised (2) as `TransformerNUFFT._forward_native` hard-converting to NumPy and needing restructuring. That was wrong, and transformer.py is deliberately untouched here: `_forward_native` already has a complete, jittable JAX branch (lax.scan + dynamic_slice). The reported failure at transformer.py:660 was in the *NumPy* branch — the traceback pointed at the symptom while the cause was one frame up, in a caller that never threaded xp. Same root shape as site 1 of #421. TransformerDFT had been passing by luck: its arithmetic flows through tracers, so the missing xp never showed. Only NUFFT, which calls into _nufftax and then converts, exposed it. Verified: - DFT and NUFFT, numpy-eager vs jax-jit, agree to ~1e-11 on real AND imaginary parts (complex visibilities) - both NUFFT chunk branches under jit match NumPy to ~5e-13 — single-shot and the lax.scan path (chunk_size is a transformer arg the simulator never sets, so that branch was exercised on the transformer directly) - the returned Interferometer keeps uv_wavelengths, real_space_mask, noise map - NumPy path still ndarray-backed - test_autoarray 929, test_autogalaxy 1009, test_autolens 488 — all unchanged TransformerNUFFTPyNUFFT remains out of scope: the legacy pynufft backend is not JAX-traceable and is not expected to be. Closes #422 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CWqjHGXUut25TEB8octU8H
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
Follow-up to #420/#421, which fixed the imaging simulator's
@jax.jitpath anddeliberately excluded the interferometer. Two causes — and one of them was not what
#422 predicted.
The whole change is one file, +56/-1.
operators/transformer.pyisdeliberately untouched.
1.
Interferometerwas not a registered pytreeThe analogue of #421's site 5: it could not cross the jit return boundary.
Added
_register_interferometer_pytrees, mirroring_register_imaging_pytrees—dataandnoise_mapdynamic;uv_wavelengths,real_space_mask,transformer,grids, the over-sample sizes and theNones as aux. The split wastaken from
vars(dataset), not the__init__signature, which is what got theImagingsplit right in #421.It must also register
VisibilitiesandVisibilitiesNoiseMap. On the imagingpath
Array2Dis already registered elsewhere; nothing registers these, so theysurfaced as bare leaves:
returned a value of type Visibilities ... at output component [0].2. A caller that never threaded
xp— one linevia_image_fromcalledtransformer.visibilities_from(image=image)with noxp=xp.#422 got this wrong, and it is worth correcting explicitly. That issue said
TransformerNUFFT._forward_native"hard-converts to NumPy" and needs"restructuring with its own correctness surface". It does not.
_forward_nativealready has a complete, jittable JAX branch (lax.scan+dynamic_slice). The failure reported attransformer.py:660was in theNumPy branch — the traceback pointed at the symptom while the cause sat one
frame up, in a caller that never passed
xp. Exactly the root shape as site 1 of#421.
Had I edited the transformer as the issue proposed, I would have restructured
working code to fix a bug that was somewhere else.
TransformerDFThad been passing by luck: its arithmetic flows through tracers, sothe missing
xpnever showed. Only NUFFT, which calls into_nufftaxand thenconverts, exposed it.
Verified
lax.scanpath.chunk_sizeis aTransformerNUFFT.__init__arg the simulator never sets, so that branch is unreachable via the simulator and was exercised on the transformer directly rather than skippedInterferometerkeepsuv_wavelengths,real_space_maskand its noise map — not a broken pytree huskndarray-backedtest_autoarray929 ·test_autogalaxy1009 ·test_autolens488 — all identical to pre-change countsUnit tests are NumPy-only by policy across the stack, so the JAX behaviour is
covered by the parity checks above rather than by
test_autoarray/.Out of scope
TransformerNUFFTPyNUFFT— the legacy pynufft backend is not JAX-traceable and isnot expected to be.
Docs follow-up (after this merges)
The interferometer
__JAX Variant__sections in both workspaces still say thejitted wrap "does not currently work" (autolens_workspace#379), and the
TransformerNUFFT"supportsjax.jit" claim inautolens_workspace/scripts/interferometer/simulator.pyis currently unqualified.Once this lands, do for interferometer what autolens_workspace#381 did for
imaging: restore the recipe, uncomment the call, and add the script to
smoke_tests.txtso CI executes it.Generated by the PyAutoLabs agent workflow.