Repository navigation
feat: matrix-free interferometer route in Settings (#638 phase 5a) #657
Description
Overview
Phases 1–4 of #638 landed the matrix-free interferometer inversion (operator data term + Powell-preconditioned PCG, aa.reg.MaternSparse, SLQ/sparse-LU evidence log-dets, interior-point positivity) as utilities only. This task wires them into fits: a new InversionInterferometerMatrixFree class selectable through Settings(interferometer_route="matrix_free"), so PyAutoGalaxy FitInterferometer reaches it with no other API change. Phase 5a of the community proposal by @HRSAstro (PyAutoLabs/.github#39); the profiling note (5b) and workspace example (5c) stay in the phase-5 research prompt.
Heart was RED at launch (release integrate stage, workspace smoke, worktree drift — none touching PyAutoArray/PyAutoGalaxy). The human approved "Human override for Heart RED (development only)" on 2026-10-26.
Plan
- Add an
InversionInterferometerMatrixFreeclass next to the sparse FFT inversion, built on the merged phase 1–4 utilities, that never forms the dense curvature or regularization matrices. - Extend the Settings route option with a
"matrix_free"value, documented and defaulted off in the packaged config, and dispatch it in the inversion factory. - Raise a clear
InversionExceptionfor every configuration the route cannot represent, and for every dense-only attribute, instead of falling back or building the matrix silently. - Make the PyAutoGalaxy interferometer fit reach the route through Settings alone, and guard the one place the plotting code reads the reconstruction noise map.
- Prove parity in CI: reconstruction and log evidence against the FFT sparse route, positive-only parity, and raises for the unsupported cases.
- Mark the wiring item done in the module docstring.
Tier: judge — merge mode: human /prm
Detailed implementation plan
Affected Repositories
- PyAutoArray (primary)
- PyAutoGalaxy (stacked follow-on: parity test, guards only if needed)
Branch Survey
| Repository | Current Branch | Dirty? |
|---|---|---|
| array/PyAutoArray | main (in sync with origin) | clean |
| galaxy/PyAutoGalaxy | main (in sync with origin) | clean |
Suggested branch: feature/matrix-free-interferometer-wiring (both repos)
Worktree: ~/Code/PyAutoLabs-wt/matrix-free-interferometer-wiring/
Implementation Steps (PyAutoArray)
autoarray/inversion/inversion/interferometer/matrix_free.py—InversionInterferometerMatrixFree(InversionInterferometerSparse). Subclassing the sparse FFT class gives it the data vector, mask, transformer and mapped-data dicts, and is whatMatrixFreeDataOperator.from_inversionalready expects. A cached operator property buildsMand the W~ kernel action. Overrides:reconstruction:reconstruction_via_pcg_from, orreconstruction_positive_via_interior_point_fromwhenuse_positive_only_solver.log_det_curvature_reg_matrix_term: Powell log-det + SLQ vialog_det_curvature_reg_via_slq_from(module defaults: 10 probes, 40 steps, fixed seed).log_det_regularization_matrix_term: the regularization's exact sparse term.regularization_term,fast_chi_squared: via the sparseHandoperator.curvature_matvec(inherited versions read the denseF).curvature_matrix,curvature_matrix_diag,curvature_reg_matrix,curvature_reg_matrix_reduced,reconstruction_covariance_matrix,reconstruction_noise_map,errors: raiseInversionExceptionnaming the matrix-free route and pointing atinterferometer_route="fft".- Constructor precondition check (mirrors
interferometer_numba/sparse.py::_check_preconditions), raising for: non-NumPyxp; anyAbstractLinearObjFuncList; mapper count != 1; over-sampling (sub_fraction != 1); non-trivial primary beam; mesh notRectangularUniform; regularization withoutregularization_matrix_sparse_from/regularization_factor_sparse_from(decision: onlyMaternSparsesupplies the sparse factor the SLQ probes need, so other schemes raise rather than densify — flagged in the PR body).
autoarray/settings.py— accept"matrix_free"ininterferometer_route(property + constructor docstring, documented like"fft"/"numba").autoarray/config/general.yaml— extend the comment; default staysnull.autoarray/inversion/inversion/factory.py(inversion_interferometer_from, sparse-operator branch ~L228) — ifsettings.interferometer_route == "matrix_free"return the new class before the numba gate; the class's own checks raise, so the factory never falls back.autoarray/inversion/plot/inversion_plots.py— addexc.InversionExceptionto the twoexceptclauses aroundreconstruction_noise_map_dict(panel 6 andsave_reconstruction_csv) so the visualizer skips the noise map on this route.inversion_interferometer_matrix_free_util.pydocstring — move the wiring line out of "Remaining work (phase 5)" and name the class + Settings value.- Tests
test_autoarray/inversion/inversion/interferometer/test_matrix_free.py(reuse fixtures fromtest_matrix_free_util.py): factory dispatch; reconstruction vs FFT route to 1e-6 of max; log evidence within the documented 0.5%; positive-only parity vs FFT positive-only; every unsupported configuration raises; every dense attribute raises; Settings typo raisesValueError. NumPy/scipy only — a JAX-backend raise test usespytest.importorskip("jax").
Implementation Steps (PyAutoGalaxy, after the PyAutoArray PR merges)
- Expected no source change:
FitInterferometerbuilds through the factory with its Settings, andlog_evidencereads only the two log-det terms,regularization_termandfast_chi_squared. Add totest_autogalaxy/interferometer/test_fit_interferometer.py: rectangularMaternSparsefit withinterferometer_route="matrix_free"vs"fft"at the witness tolerances, plus one positive-only case. Guard any visualizer/aggregator dense-attribute touch found while testing.
Key Files
autoarray/inversion/inversion/interferometer/inversion_interferometer_matrix_free_util.py— phase 1–4 utilities the class composesautoarray/inversion/inversion/interferometer/sparse.py— parent classautoarray/inversion/inversion/interferometer_numba/sparse.py— precondition pattern to mirrorautoarray/inversion/inversion/factory.py— dispatchautoarray/settings.py,autoarray/config/general.yaml— the optionautoarray/inversion/regularization/matern_sparse.py— sparse matrix/factor/log-det providerstest_autoarray/inversion/inversion/interferometer/test_matrix_free_util.py— fixtures and tolerances
Witness
In CI: FitInterferometer with Settings(interferometer_route="matrix_free") on a rectangular MaternSparse case agrees with the FFT sparse route (reconstruction 1e-6 relative, log evidence within the 0.5% SLQ tolerance stated in the module docstring), and every unsupported configuration raises InversionException.
Original Prompt
Click to expand starting prompt
Matrix-free interferometer inversion, phase 5a: make it selectable from a fit
Type: feature
Target: PyAutoArray
Repos:
- PyAutoArray
- PyAutoGalaxy
Autonomy: supervised
Priority: high
Parent: active/matrix_free_interferometer_inversion.md (PyAutoArray#638)
Community: Matrix-free solution to make very large meshes more efficient for Interferometer .github#39 (@HRSAstro)
Consequence: judge
Heart-override: human approved "Human override for Heart RED (development only)" on 2026-10-26 (RED set = release integrate, workspace smoke, worktree drift; none touch PyAutoArray/PyAutoGalaxy)
Context
Phases 1–4 of #638 are merged (PyAutoArray #645, #650, #651, #652). They are released in
2026.10.23.1 and later. They exist only as utility functions in
autoarray/inversion/inversion/interferometer/inversion_interferometer_matrix_free_util.py:
- the operator data term and Powell-preconditioned PCG;
aa.reg.MaternSparse;- the evidence log-dets via exact sparse LU + stochastic Lanczos quadrature;
- positivity via primal-dual interior point with active-set polish.
The module docstring says: "None of it is wired into fits and there is no Settings flag."
This task wires it in. It is the "wiring" item of
draft/research/autolens_profiling/matrix_free_interferometer_phase5.md. Split that draft:
this task is 5a; the profiling note and the workspace example stay in phase 5 as 5b/5c.
What
- A new inversion class
InversionInterferometerMatrixFree(next tosparse.pyin
autoarray/inversion/inversion/interferometer/). It holdsM(scipy.sparse mapping matrix),
the W~ kernel FFT action and the sparse regularization. It provides:reconstruction(PCG, or interior point whenuse_positive_only_solver);log_det_curvature_reg_matrix_term/log_det_regularization_matrix_term
(phase 3 functions);mapped_reconstructed_data/mapped_reconstructed_image.
Dense-only attributes (curvature_matrix,curvature_reg_matrix, its inverse,errors)
raise a clearInversionExceptionnaming the matrix-free route. Do not silently build them.
- A
Settingsoption inautoarray/settings.py, extending the existing
interferometer_route(None/"fft"/"numba") with"matrix_free". Document it like
the existing values and add a packaged config default (off). Dispatch it in
autoarray/inversion/inversion/factory.pyaround line 228 (the
dataset.sparse_operator is not Nonebranch). Where the matrix-free route can't represent the
inversion, raise rather than fall back silently. Those cases are: multiple mappers, linear light
profiles / func lists, over-sampling, a non-trivial primary beam, a non-rectangular mesh, the
JAX backend. - PyAutoGalaxy:
FitInterferometerreaches it throughSettingswith no other API change. Check
thatlog_evidence,figure_of_meritand the visualizer don't touch the dense attributes on
this route. Where they do, skip or guard them. - Tests (PyAutoArray + PyAutoGalaxy):
- on a small rectangular
MaternSparseinterferometer case,FitInterferometervia
interferometer_route="matrix_free"matches the FFT sparse route: reconstruction to 1e-6
of its max, log evidence within the documented SLQ tolerance; - positive-only parity, as in phase 4;
- the unsupported cases raise;
- a nojax leg needs
importorskipwhere relevant.
- on a small rectangular
- Update the module docstring's "Remaining work (phase 5)" list to say wiring is done.
Out of scope
- The profiling campaign. Matrix-free rows are being added as
experiments/cells in the
likelihood-lifecycle 2b interferometer campaign in autolens_profiling, owned by another session.
Don't touch autolens_profiling. - The workspace example.
- Uncertainty maps (stochastic diagonal).
- Multiple mappers and linear light profiles.
- A JAX path.
Witness
In CI: FitInterferometer with Settings(interferometer_route="matrix_free") on a rectangular
MaternSparse case agrees with the FFT sparse route (reconstruction 1e-6 relative, log evidence
within the SLQ tolerance stated in the module docstring), and every unsupported configuration
raises InversionException.
Activity
Jammy2211 commentedon Oct 26, 2026
Library PRs created
InversionInterferometerMatrixFree+Settings(interferometer_route="matrix_free")+ factory dispatch + plotter guard + 21 tests. Full suite 2212 passed, 4 xfailed.Workspace impact: (iii) none — no workspace script references the route; opt-in only. Targeted smoke witness:
autolens_workspace_test/scripts/interferometer/model_fit.py(pixelized interferometer fit, exercises the changed inversion plotter) exit 0 in test mode against both branches.Decisions flagged in the PR body: over-sampling and primary beam supported (parity holds); positive-only + edge-zeroing raises, so the packaged defaults need
use_edge_zeroed_pixels=False; onlyMaternSparseaccepted.Heart RED override (development only). Authorized by the live human on 2026-10-26 for this task ("I approve of the overrider" at launch; "approve" on the plan naming this issue). Exact RED reasons at launch and at ship: release validation FAILED (stage integrate); PyAutoHeart/workflows/Release Integrate: failure; PyAutoHeart/workflows/Workspace Smoke: failure; manifest_drift/checks/workspace checkouts (manifest ↔ disk): red; Release readiness: release validation FAILED (stage integrate); autolens: red; Workspace validation: NOT ready — 1429p / 1f / 125s @ cloud#40294511523; test_run: red; Release validation: NOT release_ready — v? profile=? (2026-10-26T07:48:52+00:00); validation_report/stages/integrate: fail; PyAutoBrain: red; PyAutoLens: red; autolens_profiling: red; Worktree drift: 1 orphan / 0 missing / 3 dirty. Branch gates passed: both full test suites, black, targeted smoke. Permitted actions used: commit, push, PR-open. No release; merge needs a separate human
/prmwith green required checks, library-first (#658 then #670).Tier: judge — merge mode: human /prm.
Jammy2211 commentedon Oct 26, 2026
Shipped
InversionInterferometerMatrixFree+Settings(interferometer_route="matrix_free"); pending-release.CI: 3/3 legs green on each (unittest 3.12, 3.13, nojax). Merged by human
/prmunder the recorded Heart RED development override; no release — both PRs await the next library release (pending-releaselabel + Mind chain).Decisions carried in the PR body and the Mind record: over-sampling and primary beam supported; positive-only +
use_edge_zeroed_pixels=Trueraises (defaults needuse_edge_zeroed_pixels=False);MaternSparseonly. Phases 5b (profiling note) and 5c (workspace example) stay indraft/research/autolens_profiling/matrix_free_interferometer_phase5.md.