Repository navigation
feat(interferometer): array-free Interferometer.from_stream / from_sparse_terms (streaming phase 1, Discussion #13) #592
Description
Overview
Phase 1 of the array-free streamed interferometer dataset (Discussion https://github.com/orgs/PyAutoLabs/discussions/13, phase 2 of the proposal; Mind epic streaming-visibilities). Phase 1 of the discussion (#589 + PyAutoGalaxy#637 + PyAutoLens#757 + #591) made the sparse likelihood array-free, but the process still holds every visibility: Interferometer.__init__ always builds a TransformerNUFFT from uv_wavelengths (~48 B/vis on top of the dataset's 48 B/vis), AbstractDataset.__init__ rejects noise_map=None, and apply_sparse_operator_from_chunks returns a dataset that retains all arrays. This phase adds Interferometer.from_stream / from_sparse_terms that build a dataset with no arrays and no transformer, and makes every consumer either array-free or fail with a typed error.
Plan
- Give
SparseTermsprovenance (mask shape/pixel_scales/origin, eps, transformer class) and make__add__check it. - Let
Interferometerbe built withoutuv_wavelengths/ arrays / transformer and storesparse_terms; addfrom_sparse_termsandfrom_stream. - Route
AbstractInversionInterferometer.maskthrough the dataset mask instead of the transformer; typedDatasetExceptionfrom every array/transformer property when absent. - Add
dirty_image_naturalanddirty_beamon both in-memory and array-free paths. - Tests for construction, sparse-inversion parity (numpy + jax), typed errors, provenance; fresh-process RSS witness.
Detailed implementation plan
Affected Repositories
- PyAutoArray (only). PyAutoGalaxy / PyAutoLens suites run for regression; no change there.
Branch Survey
| Repository | Current Branch | Dirty? |
|---|---|---|
| ./PyAutoArray | main | clean |
Worktree guard clear. Suggested branch: feature/streaming-p1-array-free-dataset
Design decisions (human-approved 2026-09-30, recorded in the epic ledger)
(a) transformer=None, no stub class; consumers gate and raise exc.DatasetException. (c) in-memory dirty_image unchanged; new dirty_image_natural / dirty_beam on both paths. (e) SparseTerms carries provenance.
Implementation Steps
autoarray/inversion/inversion/interferometer/inversion_interferometer_util.py:SparseTermsoptional fieldsshape_native,pixel_scales,origin,eps,transformer_class_name;sparse_terms_from_chunksfills them fromreal_space_mask/ kwargs;__add__raises on mismatched shape / pixel_scales / eps.autoarray/dataset/interferometer/dataset.py__init__:uv_wavelengthsoptional; build the transformer only when uv is present andtransformer_classis notNone, elseself.transformer = None; skip the DFT-limit check when uv isNone; newsparse_terms=Nonekwarg stored.autoarray/dataset/abstract/dataset.py__init__: guard thenoise_map=Nonepath (no covariance/diag work when both arrays areNone);shape_slimguarded.Interferometer.from_sparse_terms(terms, real_space_mask, *, batch_size=128)andfrom_stream(chunks, real_space_mask, *, transformer_class=TransformerNUFFT, method="nufft", eps=None, chunk_size=None, chunk_k=2048, use_jax=False, show_progress=False, batch_size=128);apply_sparse_operator_from_chunksstores the terms on the returned dataset.- New properties
dirty_image_natural(dirty_image_native / sum_weights) anddirty_beamfromsparse_termswhen present, else from the operator's cached native images;amplitudes,phases,uv_distances,dirty_image,dirty_noise_map,signal_to_noise_map,psf_precision_operator_from,apply_sparse_operatorraiseexc.DatasetExceptionnamingfrom_streamwhen their input isNone. autoarray/inversion/inversion/interferometer/abstract.pymask→ dataset mask (real_space_maskonInterferometer,grids.lp.maskonDatasetInterface); check the sparse numba/jax classes andmapped_reconstructed_data_dictfollow;mapped_reconstructed_operated_data_dictraises the typed exception whentransformer is None.autoarray/fit/fit_interferometer.py:mask,transformer,dirty_*, residual / normalized-residual / chi-squared maps raise the typed exception when arrays are absent.- Tests + fresh-process RSS witness (5e5..4e6 vis, 400-pixel image, 4096-vis chunks) reported in the PR body.
Key Files
autoarray/dataset/interferometer/dataset.py,autoarray/dataset/abstract/dataset.pyautoarray/inversion/inversion/interferometer/{inversion_interferometer_util.py,abstract.py}autoarray/fit/fit_interferometer.pytest_autoarray/dataset/interferometer/test_dataset.py,test_autoarray/inversion/inversion/interferometer/
Original Prompt
Click to expand starting prompt
Streaming phase 1: array-free Interferometer.from_stream / from_sparse_terms (PyAutoArray)
Type: feature
Target: PyAutoArray
Repos:
- PyAutoArray
Themes: - interferometer
- sparse-operator
- memory
Autonomy: supervised
Priority: medium
Status: draft
Epic: streaming-visibilities
Phase: 1
Difficulty: medium
Consequence: judge
Witness:aa.Interferometer.from_stream(chunks, real_space_mask, ...)andfrom_sparse_terms(terms, real_space_mask)return a dataset whosedata,noise_map,uv_wavelengthsandtransformerare allNoneand which carriessparse_terms; anaasparse inversion (rectangular mesh, numpy and jax) on it gives log_evidence equal to the in-memoryapply_sparse_operatorinversion at rel 1e-8; every array/transformer property raises a typedDatasetExceptionnamingfrom_stream;SparseTerms.__add__refuses mismatched provenance; a fresh-process RSS witness is flat from 5e5 to 4e6 visibilities (≤ ~40 MB over baseline).
Review-minutes: 8
Unattended: ready
Parent: draft/feature/autoarray/interferometer_from_stream_array_free_dataset.md
Source: https://github.com/orgs/PyAutoLabs/discussions/13 phase 2, sliced 2026-09-30 (design decisions (a), (c), (e) in the ledger).
Why
After phase 1 the likelihood is array-free but the process is not: Interferometer.__init__ always builds a
TransformerNUFFT from uv_wavelengths (~48 B/vis on top of the dataset's 48 B/vis), AbstractDataset.__init__
raises on noise_map=None, the DFT-limit check reads uv_wavelengths.shape, and apply_sparse_operator_from_chunks
returns a dataset that still retains every array. The one transformer read on the sparse likelihood path is
AbstractInversionInterferometer.mask (transformer.real_space_mask).
What
SparseTermsgains optional provenance fields (shape_native,pixel_scales,origin,eps,
transformer_class_name);sparse_terms_from_chunksfills them;__add__requires shape / pixel_scales / eps equal.Interferometer.__init__:uv_wavelengthsoptional; transformer built only when uv is present (elseNone);
DFT-limit check skipped when uv isNone; newsparse_terms=kwarg stored.AbstractDataset.__init__guards
noise_map=None(no covariance work when both arrays areNone);shape_slimguarded.Interferometer.from_sparse_terms(terms, real_space_mask, *, batch_size=128)and
from_stream(chunks, real_space_mask, *, transformer_class=TransformerNUFFT, method, eps, chunk_size, chunk_k, use_jax, show_progress, batch_size);apply_sparse_operator_from_chunksalso stores the terms.- New
dirty_image_natural(dirty_image_native / sum_weights) anddirty_beamon both paths.amplitudes,phases,
uv_distances,dirty_image,dirty_noise_map,signal_to_noise_map,psf_precision_operator_from,
apply_sparse_operatorraiseexc.DatasetExceptionnaming the array-free dataset when their input isNone. AbstractInversionInterferometer.maskreads the dataset mask (real_space_mask/grids.lp.mask);
mapped_reconstructed_operated_data_dictraises the typed exception whentransformer is None.
aa.FitInterferometermask,transformer,dirty_*, residual/chi-squared maps raise it when arrays are absent.- Tests in
test_autoarray/dataset/interferometer,inversion/interferometer,test_inversion_interferometer_util.py;
witness script (fresh process per N_vis) in the PR body. No autogalaxy/autolens change; run their suites for regression.
🤖 Generated with Claude Code
Activity
Jammy2211 commentedon Sep 30, 2026
Heart RED override — development only (recorded per AUTONOMY.md)
release validation FAILED (stage integrate). Unrelated to this branch (a release-integrate leg).feature/streaming-p1-array-free-dataset: "Authorize override for feat(interferometer): array-free Interferometer.from_stream / from_sparse_terms (streaming phase 1, Discussion #13) #592" — commit, push and open the pending-release PR. Merge remains a separate explicit human command with every required check green. No release, no rehearsal, no claim that this branch repairs Heart.test_autoarray1779 passed;test_autogalaxy/interferometer43 passed andtest_autolens/interferometer29 passed with no edits there; red-checks on the new tests (mask reroute reverted → 3 fail, guard removed → 1 fail, provenance check disabled → 2 fail); independent Codex gpt-6-astra review of the working tree: FINDINGS (4) — Feature/config #1 geometry guard infrom_sparse_terms(pixel_scales/origin), Fft #2origininSparseTerms.__add__, Fft #3 provenance merge when one operand is unrecorded: all three fixed in-branch with red-checked tests; Feature/clean #4 (AnalysisInterferometer.save_attributes/ aggregator cannot handle an array-free dataset) is phase 2 by design, documented.🤖 Generated with Claude Code
Jammy2211 commentedon Sep 30, 2026
Library PR created
🤖 Generated with Claude Code
Jammy2211 commentedon Sep 30, 2026
Shipped
#593 (merge bd03e09) — array-free
Interferometer.from_stream/from_sparse_terms,SparseTermsprovenance, dataset-routed inversion mask, typed exceptions on array-dependent properties,dirty_image_natural/dirty_beam. Labelledpending-release. Streaming epic phase 1 of 5; phase 2 (fit + save/reload) is next.🤖 Generated with Claude Code