Skip to content

fix: mask invalid mapper triplets on the imaging sparse path; backend-dispatched scatter-add helpers - #649

Merged
Jammy2211 merged 3 commits into
mainfrom
feature/imaging-sparse-triplets-masking
Oct 21, 2026
Merged

Jammy2211 merged 3 commits into
mainfrom
feature/imaging-sparse-triplets-masking

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Closes #643. Side finding from #639 / #641.

Summary

The imaging sparse-inversion path read Mapper.sparse_triplets_data / sparse_triplets_curvature built without the slot-size and negative-index zeroing the interferometer path applied locally, and its two scatter-add helpers imported JAX inside the function on every call.

  • mapper_util.sparse_triplets_from gains pix_sizes_for_sub=None and masks invalid entries (slot ≥ pix_sizes, or cols < 0 → weight 0, column 0) on both backends, after slim_weights. Mapper.sparse_triplets_data / sparse_triplets_curvature pass it. The interferometer _sparse_triplets_slim_from now uses the shared masking (its local block removed); its triplets, data_vector, curvature_matrix and reconstruction are identical before and after (24 arrays, Delaunay + rectangular, with and without primary beam).
  • data_vector_via_psf_weighted_data_from and mapped_reconstructed_operated_data_via_sparse_operator_from take xp; numpy uses np.bincount(minlength=) with no JAX import, JAX keeps segment_sum. The four imaging call sites pass xp=self._xp.

Is the defect latent? Yes. On the 7×7 fixtures (Delaunay meshes also given a grid reaching outside the hull): Rectangular (all variants), KNN and KNNBarycentric emit no unused slots and no negative indices; Delaunay (56 unused slots) and DelaunayNN (1082) hold index −1 with weight exactly 0 in every unused slot, never a negative index in a used slot nor a non-zero weight in an unused one. Those entries wrap harmlessly under segment_sum today, but the new numpy bincount branch raises on −1, so the masking is a prerequisite, not only a guard.

Narrower than planned: ImagingSparseOperator.from_noise_map_and_psf and the imaging curvature methods still import JAX unconditionally, so InversionImagingSparse as a whole cannot run without JAX even on numpy; fixing that is beyond this PR (follow-up). The interferometer _sparse_triplets_curvature_from is left as is (its util drops out-of-range columns itself).

API Changes

Additive/internal: mapper_util.sparse_triplets_from(..., pix_sizes_for_sub=None); the two imaging util helpers gain xp=np. No public behaviour changes.
See full details below.

Test Plan

  • test_autoarray/inversion/pixelization/mappers/test_mapper_util.py: hand-built index table with a −1 index and an unused slot with non-zero weight → zeroed on numpy and JAX.
  • test_autoarray/inversion/inversion/imaging/test_imaging.py: imaging sparse parity (data vector, curvature, mapped reconstructed image) vs the dense mapping inversion on such a mapper, numpy backend and JAX backend; subprocess no-JAX test (jax/jaxlib/nufftax blocked) covering the triplets and both helpers against the dense mapping matrix.
  • Controls: masking removed → parity test fails; pre-fix util → no-JAX test fails.
  • Worktree runs: pytest test_autoarray/inversion -q → 1037 passed, 4 xfailed; pytest test_autoarray/dataset/imaging -q → 36 passed; no-JAX emulation 23 passed, 10 skipped, no jax module loaded.

Heart YELLOW acknowledged by the human on 2026-10-21 (manifest drift 1 mismatch; release validation stale: source moved since rehearsal (PyAutoArray)).

Full API Changes (for automation & release notes)

Changed Signature

  • autoarray.inversion.mappers.mapper_util.sparse_triplets_from(..., pix_sizes_for_sub=None) — new optional kwarg; when given, invalid stencil entries are zeroed.
  • inversion_imaging_util.data_vector_via_psf_weighted_data_from(..., xp=np), mapped_reconstructed_operated_data_via_sparse_operator_from(..., xp=np) — backend dispatch; numpy branch no longer imports JAX.

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

Jammy2211 and others added 3 commits October 21, 2026 12:56
sparse_triplets_from gains pix_sizes_for_sub: entries mapping_matrix_from
drops (slot >= pix_sizes, or a negative source index) get value 0 and
column 0 on both numpy and JAX branches. Mapper.sparse_triplets_data /
sparse_triplets_curvature pass it, so every imaging sparse consumer is
covered; the interferometer _sparse_triplets_slim_from uses the shared
masking and drops its local copy (outputs bit-identical).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
data_vector_via_psf_weighted_data_from and
mapped_reconstructed_operated_data_via_sparse_operator_from take xp:
numpy uses np.bincount with no JAX import, JAX keeps segment_sum with
the import inside its branch. InversionImagingSparse passes xp=self._xp.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- sparse_triplets_from zeroes a -1 index and unused slots with non-zero
  weights (numpy and JAX), and its triplets scatter to mapping_matrix_from;
- InversionImagingSparse on a mapper with -1 indices, a negative index in a
  used slot and weighted unused slots matches the mapping inversion
  (data_vector, curvature_matrix, mapped reconstructed data) on numpy and
  JAX backends;
- the mapper triplets and both imaging scatter-add helpers run with jax,
  jaxlib and nufftax blocked (subprocess import hook).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Jammy2211
Jammy2211 merged commit 3343559 into main Oct 21, 2026
3 checks passed
@Jammy2211
Jammy2211 deleted the feature/imaging-sparse-triplets-masking branch October 21, 2026 12:09
@Jammy2211 Jammy2211 added pending-release PR queued for the next release build and removed pending-release PR queued for the next release build labels Oct 21, 2026
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.

fix: mask invalid mapper triplets on the imaging sparse path; lazy JAX import in its scatter-add helpers

1 participant