Skip to content

fix: make the interferometer simulator's @jax.jit path work - #423

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/interferometer-jax-jit
Jul 30, 2026
Merged

fix: make the interferometer simulator's @jax.jit path work#423
Jammy2211 merged 1 commit into
mainfrom
feature/interferometer-jax-jit

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #420/#421, which fixed the imaging simulator's @jax.jit path and
deliberately excluded the interferometer. Two causes — and one of them was not what
#422 predicted.

The whole change is one file, +56/-1. operators/transformer.py is
deliberately untouched.

1. Interferometer was not a registered pytree

The analogue of #421's site 5: 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. The split was
taken from vars(dataset), not the __init__ signature, which is what got the
Imaging split right in #421.

It must also register Visibilities and VisibilitiesNoiseMap. On the imaging
path Array2D is already registered elsewhere; nothing registers these, so they
surfaced as bare leaves:
returned a value of type Visibilities ... at output component [0].

2. A caller that never threaded xp — one line

via_image_from called transformer.visibilities_from(image=image) with no
xp=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_native already has a complete, jittable JAX branch (lax.scan +
dynamic_slice). The failure reported at transformer.py:660 was in the
NumPy 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.

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, so both are checked)
  • Both NUFFT chunk branches under jit match NumPy to ~5e-13 — single-shot and the lax.scan path. chunk_size is a TransformerNUFFT.__init__ arg the simulator never sets, so that branch is unreachable via the simulator and was exercised on the transformer directly rather than skipped
  • the returned Interferometer keeps uv_wavelengths, real_space_mask and its noise map — not a broken pytree husk
  • NumPy path still ndarray-backed
  • test_autoarray 929 · test_autogalaxy 1009 · test_autolens 488 — all identical to pre-change counts

Unit 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 is
not expected to be.

Docs follow-up (after this merges)

The interferometer __JAX Variant__ sections in both workspaces still say the
jitted wrap "does not currently work" (autolens_workspace#379), and the
TransformerNUFFT "supports jax.jit" claim in
autolens_workspace/scripts/interferometer/simulator.py is 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.txt so CI executes it.

Generated by the PyAutoLabs agent workflow.

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
@Jammy2211
Jammy2211 merged commit 71cb762 into main Jul 30, 2026
4 checks passed
@Jammy2211
Jammy2211 deleted the feature/interferometer-jax-jit branch July 30, 2026 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant