Repository navigation
Description
Overview
InversionInterferometerSparse.data_vector computes mapping_matrix.T @ dirty_image, which materialises the dense (masked pixels × source pixels) mapping matrix via hstack — 4.2 GB at 91,507 × 5,746 on the Aris 512×512 mask. A memory trace attributes all RSS growth from ~3 GB to 7.2 GB in one likelihood call to this line, on both the numba direct_conv and NumPy FFT routes, and it likely dominates the ~9 GB XLA temp memory of the JAX path. The curvature routes already work from the sparse COO triplets of the same operator, so the data vector can be a scatter/segment-sum over those triplets and the dense matrix never needs to exist in the likelihood.
Plan
- Compute each mapper's data-vector block from its sparse triplets (segment-sum of
vals * dirty_image[rows]over source columns) on both the NumPy and JAX backends. - Keep linear function lists (MGE / linear light profiles) on their small dense
mapping_matrix.T @ dirty_imageblock and concatenate blocks in linear-object order. - Audit every other per-call
mapping_matrixtouch in the sparse interferometer inversion and its numba subclass; leave visualisation-only paths alone. - Add tests pinning the sparse data vector to the dense one (over-sampled mapper, mapper + func list, JAX backend,
DatasetInterfacewithsparse_dirty_image). - Measure peak RSS and time before/after on the cached 512×512 setup (5,746 vertices, numba route) and JAX compile memory.
Tier: glance — merge mode: auto-merge on green if the Witness passes, post-merge summary
Detailed implementation plan
Affected Repositories
- PyAutoArray (primary)
Branch Survey
| Repository | Current Branch | Dirty? |
|---|---|---|
| array/PyAutoArray | main (a84f724) | clean |
Suggested branch: feature/interferometer-sparse-data-vector
Implementation Steps
autoarray/inversion/inversion/interferometer/sparse.py— rewritedata_vector: resolvedirty_imageas today (slim, masked-pixel indexed); for each linear object inself.linear_obj_list, if it is aMapperbuild triplets withmapper_util.sparse_triplets_from(..., return_rows_slim=True, xp=self._xp)(slim rows index the slim dirty image; the existing_sparse_triplets_curvature_fromuses extent-flat rows and is NOT the right indexing for this product — add a sibling helper_sparse_triplets_slim_fromor arows_slimflag) and compute the block asnp.bincount(cols, weights=vals * dirty_image[rows], minlength=mapper.params)on NumPy andjnp.zeros(S).at[cols].add(vals * dirty_image[rows])(orjax.ops.segment_sumwithnum_segments=S) on JAX; if it is anAbstractLinearObjFuncListkeepxp.dot(linear_obj.mapping_matrix.T, dirty_image). Concatenate withxp.concatenatein the order ofself.linear_obj_listso the result matchesparam_range_list_from(cls=LinearObj).- Confirm
sparse_triplets_fromincludes the samesub_fractionnormalisation asmapper_util.mapping_matrix_from(the curvature parity tests already rely on this); if the mapper haspix_sizes_for_sub_slim_indexpadding (invalid stencil slots with zero weight), the helper's masking must drop or zero them — verify, do not assume. - Audit per-call
mapping_matrixuse:abstract.pymapping_matrixcached property (now only reached from visualisation /mapped_reconstructed_data_dict),interferometer_numba/sparse.py(no touch per grep),_curvature_matrix_func_list_and_mapper(func-list matrices only — fine). Record findings in the PR body. - Tests in
test_autoarray/inversion/inversion/interferometer/test_interferometer.py(and the numba subclass tests underinterferometer_numba/): sparsedata_vectorequalsmapping_matrix.T @ dirty_imageto 1e-12 for (a) a single over-sampled Delaunay/rectangular mapper, (b) mapper + linear func list, (c)xp=jnp(importorskip jax), (d) aDatasetInterfacewhosesparse_dirty_imagediffers from the operator's. Keep thelog_evidenceparity tests betweenInversionInterferometerSparseandInversionInterferometerMappinggreen. - Witness measurement with the session scratchpad copy of the Aris evidence (
aris_evidence/step8/bench.py --mesh step1 --mode numbastyle run againstdataset_sparse_512.pkl, 5,746 vertices, 1 thread): record peak RSS before (7.17 GB) and after (target ≤ ~3.5 GB), steady s/call, and that the log likelihood at the same parameter vector is unchanged to 1e-8. Also record JAX jit peak RSS / XLA compile temp at 1,459 vertices (3.69 GB / 2.17 GB before) and attempt the 5,746-vertex JAX run that was previously infeasible (8.95 GB compile temp). Put the numbers in the PR body.
Key Files
autoarray/inversion/inversion/interferometer/sparse.py—data_vector(line ~104),_sparse_triplets_curvature_fromautoarray/inversion/mappers/mapper_util.py—sparse_triplets_from(return_rows_slim)autoarray/inversion/inversion/abstract.py—mapping_matrixhstack (line ~270)autoarray/inversion/inversion/interferometer_numba/sparse.py— numba subclass (audit only)test_autoarray/inversion/inversion/interferometer/test_interferometer.py— parity tests
Evidence
Prompt measurements (PROGRESS4 step8.4a): dense mapping matrix 1.07 GB @1459, 1.9 GB @2577, 4.2 GB @5746 vertices (+ hstack copy); route-independent. Cached dataset and bench script copied to this session's scratchpad aris_evidence/step8/ (temporary).
Heart RED development override
Heart was RED at launch (2026-10-15 evening): Heart: RED — Release RED; monitoring RED · 10/100 · 415 unresolved; [red] release validation FAILED (stage integrate); [red] PyAutoHands/workflows/Python Version Matrix: failure; [red] PyAutoHeart/workflows/Release Integrate: failure; [red] PyAutoMind/workflows/Lifecycle Drift: failure; [red] PyAutoArray/Tests: red; [red] autofit_workspace_test/Smoke Tests: red; [red] autogalaxy_workspace_test/Smoke Tests: red; [red] autolens_workspace_test/Smoke Tests: red; [red] Release validation: NOT release_ready — v2026.10.15.1.dev81701; [red] Worktree drift: 5 orphan / 0 missing / 4 dirty; [red] autolens_inference: red; [red] autolens_profiling: red; [red] euclid_strong_lens_modeling_pipeline: red.
The live human authorized the development-only override in session 3e3dea38 (AskUserQuestion answer: "Yes, override for development (Recommended)") for the three PyAutoArray prompts filed from the Aris interferometer profiling. Permitted: commit, push, PR-open behind the autonomous-ship gate (tests, smoke, review must still pass). Merge stays a separate human act. Heart remains RED for release purposes. Launched --auto.
Original Prompt
Click to expand starting prompt
Interferometer sparse inversion: compute data vector without the dense mapping matrix
Type: feature
Target: PyAutoArray
Repos:
- PyAutoArray
Difficulty: medium
Autonomy: safe
Priority: normal
Memory: wiki/lensing/sources/dark-matter-substructure.md; reading-queue.md; wiki/galaxies/sources/cosmos-survey.md
Status: formalised
Consequence: glance
Witness: on the Aris 512x512 setup (91,507 masked pixels, 5,746 Delaunay vertices, use_jax=False) peak RSS of one likelihood call drops from 7.2 GB to <= ~3.5 GB with log likelihood unchanged to 1e-8
Review-minutes: 3
Unattended: ready
autoarray/inversion/inversion/interferometer/sparse.py:104 computes
data_vector = xp.dot(self.mapping_matrix.T, dirty_image), which materialises the dense
(masked pixels x source pixels) mapping matrix via hstack — 4.2 GB at 91,507 x 5,746. The step only
needs the sparse mapping operator (the same triplets the curvature routes already use). A memory
trace on the 512x512 mask attributes all RSS growth from ~3 GB to 7.2 GB to this line, on both the
numba direct_conv and NumPy FFT routes. It probably also explains most of the ~9 GB XLA temp
memory of the JAX path at that size.
Do: compute the data vector from the sparse triplets (scatter/segment-sum: D_j = sum over nonzeros
in column j of val * dirty_image[row]) on both the NumPy and JAX paths, so no dense mapping matrix
is built in the likelihood. Check other places in the interferometer sparse inversion (and
the numba subclass) that touch mapping_matrix per call. Keep outputs identical; add a test.
Measure peak RSS and time before/after on the 512x512 cache (and JAX compile memory).
Evidence: /tmp/claude-1000/-home-jammy-Code-PyAutoLabs/50c8c6cd-6ac2-47c2-9290-7ee73f9c4315/scratchpad/aris/PROGRESS4.md and step8/ (dataset_sparse_512.pkl cache, bench.py). PyAutoArray ccddfba.
Activity
Jammy2211 commentedon Oct 15, 2026
Campaign membership: this task is logged and monitored under the PyAutoPulse interferometer campaign as task
interferometer_large_mask_aris— PyAutoLabs/PyAutoPulse#62 (ledger rows queued in Mind behind the PyAutoPulse claim; the issue holds the campaign intent and baseline numbers until they land). Record the before/after witness table there at close-out.Jammy2211 commentedon Oct 15, 2026
Library PR Created
PR: #630. Labels:
pending-release,decision-taken. This--autorun stops at PR-open, and the human runs/prm.API changes: none. Internal changes only: two private helpers on
InversionInterferometerSparse. One behaviour note: in JAX mixed precision the data vector now uses a float64 data-vector block instead of a float32 mapping matrix.Gate
pyauto-heart smoke autolens autogalaxy autolens_testran 92/92 scripts and 4/4 notebooks with 0 failures. That includes 15 interferometer scripts.release validation FAILED (stage integrate)andPyAutoFit: open PR 8d old.Witness: PASS on memory. Peak RSS went from 7.17 GB to 3.14 GB. On logL, see the PR's "Decision taken" section: the data vector matches the dense product to 5.8e-16, and logL moves by at most 2.5e-11 relative (fp64 rounding at |logL| ≈ 1.4e7). Revert:
git revert 8676b6a0.Workspace impact: recommendation (iii), no workspace impact. There is no public API change, and no workspace, test or pipeline script references the changed helpers. The smoke subsets above confirm this. No
/start_workspaceneeded.Campaign: at close-out, copy the before/after witness table from PR #630 into the PyAutoPulse#62 task
interferometer_large_mask_aris.Jammy2211 commentedon Oct 15, 2026
Shipped
Merged in #630 (merge
7cc71238, head8676b6a0), 2026-10-15. Labelledpending-release. It is not in a published release yet._sparse_triplets_slim_fromand_data_vector_mapper_from. Tests compare the result with the dense product.git revert 8676b6a0.interferometer_large_mask_aris.