Skip to content

feat(interferometer): fits and save_attributes on array-free datasets (streaming phase 2) - #758

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/streaming-p2-fit-save-reload
Sep 30, 2026
Merged

Jammy2211 merged 1 commit into
mainfrom
feature/streaming-p2-fit-save-reload

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

PyAutoLens half of streaming phase 2 (Mind epic streaming-visibilities; https://github.com/orgs/PyAutoLabs/discussions/13). Pairs with PyAutoLabs/PyAutoGalaxy#639 (issue PyAutoLabs/PyAutoGalaxy#638); requires PyAutoLabs/PyAutoArray#593 (phase 1). CI on this branch resolves the same-named PyAutoGalaxy branch.

  • FitInterferometer.profile_visibilities returns None on an array-free dataset when the tracer has no ordinary light profile and raises a typed DatasetException when it does; profile_subtracted_visibilities evaluates it first, then returns None without data; inversion_with_data returns the inversion itself without data. In-memory fits unchanged.
  • AnalysisInterferometer.save_attributes writes dataset.fits through autogalaxy's interferometer_hdu_list_from (array-free datasets persist their SparseTerms as FITS extensions) and writes transformer_class.json only when a transformer exists. The aggregator loader is autogalaxy's (imported), so reload needs no change here.

Witness (same setup as the PyAutoGalaxy PR, with an Isothermal lens + pixelized source): log_evidence −315436.25588951 in memory, identical array-free and after aggregator reload (rel 0.0); array-free vs in-memory rel 1.9e-16; jit vs numpy rel 2.6e-14; dataset.fits 80,640 B (vs 4.8 MB of visibilities).

API Changes

Behaviour on array-free datasets only: profile_visibilities / profile_subtracted_visibilities may return None or raise aa.exc.DatasetException; save_attributes writes EXTNAME-tagged HDUs (same arrays as before for in-memory datasets) and skips transformer_class.json without a transformer.
See full details below.

Test Plan

  • pytest test_autolens — 770 passed, 1 xfailed
  • New: array-free pixelization-only lens fit == in-memory sparse fit at rel 1e-8 (numpy + jax.jit); lens light on an array-free dataset raises; tracer save → reload round trip with EXTNAME assertions
  • CI green on unittest 3.12 / 3.13 / nojax + docs

Heart RED override (development only)

Heart verdict at ship: RED, exact reason release validation FAILED (stage integrate) (2026-09-30T12:42Z, unrelated). Live human authorization in-session 2026-09-30 for issue PyAutoLabs/PyAutoGalaxy#638 and both branches ("Authorize override for #638"): commit, push, pending-release PRs only. Branch gates: test_autolens 770 passed + 1 xfailed, test_autogalaxy 1287 passed, round-trip red-check, Codex review: FINDINGS (3): #1 + #3 fixed in-branch (red-checked), #2 pre-existing → bug draft filed. Merge needs its own explicit human command with every check green.

Full API Changes (for automation & release notes)

Changed Behaviour

  • FitInterferometer.profile_visibilities / profile_subtracted_visibilities — None on an array-free dataset with no ordinary light profile; aa.exc.DatasetException with one; inversion_with_data returns the inversion itself when the fit has no data
  • AnalysisInterferometer.save_attributes — uses autogalaxy.interferometer.model.analysis.interferometer_hdu_list_from; transformer_class.json only when a transformer exists

Migration

  • None for in-memory datasets.

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JZZksyZ8LTA4LLxoZjQMNF

… (streaming phase 2, PyAutoGalaxy#638)

Mirror the autogalaxy guards in FitInterferometer (profile_visibilities None
or a typed DatasetException without a transformer; profile_subtracted_visibilities
None without data; inversion_with_data returns the inversion) and write
dataset.fits through autogalaxy's interferometer_hdu_list_from, skipping
transformer_class.json when there is no transformer. The aggregator loader is
autogalaxy's and needs no change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZZksyZ8LTA4LLxoZjQMNF
@Jammy2211

Copy link
Copy Markdown
Collaborator Author

Independent review (Codex gpt-6-astra) of this branch pair before PR-open

  1. P2 — FITS serialization loses float64 precision (in-diff). analysis.py:96 writes the scalar terms as ordinary numeric header cards. A byte-stream Astropy round trip converts 1.2345678901234567e+20 to 1.23456789012345e+20. Thus SUMW, DATATERM, and NOISENRM do not reliably survive exactly; changed data/noise terms change the reloaded evidence. The header test compares unserialized values, where this truncation has not happened. Store lossless representations or binary float64 scalars.

  2. P2 — Database searches cannot reload the saved dataset (pre-existing, still affects this feature). Galaxy analysis.py:350 and Lens analysis.py:368 write directly to disk without registering the HDU list through DatabasePaths.save_fits. For a search using database paths, database Fit.value("dataset") returns None; mask loading then fails on [0]. Unlike SearchOutput, database Fit.value does not scan the image directory. Both new round-trip tests bypass this failure with _FitStub and DirectoryPaths.

  3. P2 — Non-square pixel scales silently corrupt reloaded geometry (pre-existing helper defect, inherited by the new loader). agg_util.py:104 reads PIXSCAY for both axes. A saved mask with scales (0.1, 0.2) reloads as (0.1, 0.1). The new interferometer.py:89 also copies these incorrect scales into SparseTerms, so from_sparse_terms’ geometry check passes while the stored operator describes the original geometry. Subsequent fits can produce different reconstructions and evidence.

FINDINGS (3)

Disposition: FINDINGS (3) — #1 FITS header cards truncate float64 scalars (reproduced: 1.2345678901234567e+20 → 1.23456789012345e+20): fixed in-branch with a lossless SPARSE_TERMS_SCALARS float64 HDU (header cards kept as readable copies, loader falls back to them for older files), red-checked; #3 agg_util.mask_header_from read PIXSCAY for both axes (pre-existing): fixed in-branch with a non-square round-trip test, red-checked; #2 database-path searches never register dataset.fits via save_fits (pre-existing, in-memory path too): filed as draft/bug/autogalaxy/database_paths_dataset_fits_not_registered.md.

🤖 Generated with Claude Code

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.

1 participant