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

pending-release PR queued for the next release build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant