Skip to content

feat: matrix-free interferometer route in Settings (#638 phase 5a) #657

Closed

Description

@Jammy2211

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 InversionInterferometerMatrixFree class 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 InversionException for 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)

  1. 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 what MatrixFreeDataOperator.from_inversion already expects. A cached operator property builds M and the W~ kernel action. Overrides:
    • reconstruction: reconstruction_via_pcg_from, or reconstruction_positive_via_interior_point_from when use_positive_only_solver.
    • log_det_curvature_reg_matrix_term: Powell log-det + SLQ via log_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 sparse H and operator.curvature_matvec (inherited versions read the dense F).
    • curvature_matrix, curvature_matrix_diag, curvature_reg_matrix, curvature_reg_matrix_reduced, reconstruction_covariance_matrix, reconstruction_noise_map, errors: raise InversionException naming the matrix-free route and pointing at interferometer_route="fft".
    • Constructor precondition check (mirrors interferometer_numba/sparse.py::_check_preconditions), raising for: non-NumPy xp; any AbstractLinearObjFuncList; mapper count != 1; over-sampling (sub_fraction != 1); non-trivial primary beam; mesh not RectangularUniform; regularization without regularization_matrix_sparse_from / regularization_factor_sparse_from (decision: only MaternSparse supplies the sparse factor the SLQ probes need, so other schemes raise rather than densify — flagged in the PR body).
  2. autoarray/settings.py — accept "matrix_free" in interferometer_route (property + constructor docstring, documented like "fft"/"numba"). autoarray/config/general.yaml — extend the comment; default stays null.
  3. autoarray/inversion/inversion/factory.py (inversion_interferometer_from, sparse-operator branch ~L228) — if settings.interferometer_route == "matrix_free" return the new class before the numba gate; the class's own checks raise, so the factory never falls back.
  4. autoarray/inversion/plot/inversion_plots.py — add exc.InversionException to the two except clauses around reconstruction_noise_map_dict (panel 6 and save_reconstruction_csv) so the visualizer skips the noise map on this route.
  5. inversion_interferometer_matrix_free_util.py docstring — move the wiring line out of "Remaining work (phase 5)" and name the class + Settings value.
  6. Tests test_autoarray/inversion/inversion/interferometer/test_matrix_free.py (reuse fixtures from test_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 raises ValueError. NumPy/scipy only — a JAX-backend raise test uses pytest.importorskip("jax").

Implementation Steps (PyAutoGalaxy, after the PyAutoArray PR merges)

  1. Expected no source change: FitInterferometer builds through the factory with its Settings, and log_evidence reads only the two log-det terms, regularization_term and fast_chi_squared. Add to test_autogalaxy/interferometer/test_fit_interferometer.py: rectangular MaternSparse fit with interferometer_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 composes
  • autoarray/inversion/inversion/interferometer/sparse.py — parent class
  • autoarray/inversion/inversion/interferometer_numba/sparse.py — precondition pattern to mirror
  • autoarray/inversion/inversion/factory.py — dispatch
  • autoarray/settings.py, autoarray/config/general.yaml — the option
  • autoarray/inversion/regularization/matern_sparse.py — sparse matrix/factor/log-det providers
  • test_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:

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

  1. A new inversion class InversionInterferometerMatrixFree (next to sparse.py in
    autoarray/inversion/inversion/interferometer/). It holds M (scipy.sparse mapping matrix),
    the W~ kernel FFT action and the sparse regularization. It provides:
    • reconstruction (PCG, or interior point when use_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 clear InversionException naming the matrix-free route. Do not silently build them.
  2. A Settings option in autoarray/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.py around line 228 (the
    dataset.sparse_operator is not None branch). 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.
  3. PyAutoGalaxy: FitInterferometer reaches it through Settings with no other API change. Check
    that log_evidence, figure_of_merit and the visualizer don't touch the dense attributes on
    this route. Where they do, skip or guard them.
  4. Tests (PyAutoArray + PyAutoGalaxy):
    • on a small rectangular MaternSparse interferometer case, FitInterferometer via
      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 importorskip where relevant.
  5. 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

  1. Jammy2211 commented on Oct 26, 2026

    @Jammy2211
    CollaboratorAuthor

    Library PRs created

    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; only MaternSparse accepted.

    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 /prm with green required checks, library-first (#658 then #670).

    Tier: judge — merge mode: human /prm.

  2. Jammy2211 commented on Oct 26, 2026

    @Jammy2211
    CollaboratorAuthor

    Shipped

    • PyAutoArray#658 MERGED (7eba0c0) — InversionInterferometerMatrixFree + Settings(interferometer_route="matrix_free"); pending-release.
    • PyAutoGalaxy#670 MERGED (d44a83db) — fit/visualizer parity tests; pending-release.

    CI: 3/3 legs green on each (unittest 3.12, 3.13, nojax). Merged by human /prm under the recorded Heart RED development override; no release — both PRs await the next library release (pending-release label + Mind chain).

    Decisions carried in the PR body and the Mind record: over-sampling and primary beam supported; positive-only + use_edge_zeroed_pixels=True raises (defaults need use_edge_zeroed_pixels=False); MaternSparse only. Phases 5b (profiling note) and 5c (workspace example) stay in draft/research/autolens_profiling/matrix_free_interferometer_phase5.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions