Skip to content

fix(inversion): guard data_subtracted_dict on data=None, harden sparse noise check and data-term dtype - #591

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/sparse-data-none-guard
Sep 30, 2026
Merged

Jammy2211 merged 1 commit into
mainfrom
feature/sparse-data-none-guard

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Corrective follow-up to phase 1 of https://github.com/orgs/PyAutoLabs/discussions/13 (#589, PyAutoLabs/PyAutoGalaxy#637, PyAutoLabs/PyAutoLens#757). Closes #590. An independent Codex review reproduced three defects on the sparse data=None path (record: PyAutoLabs/PyAutoLens#757 (comment)). This PR:

  • data_subtracted_dict on an inversion built without data (introduced by phase 1): previously a multi-object inversion raised TypeError: unsupported operand type(s) for -: 'NoneType' and 'complex' and a single-mapper inversion returned {mapper: None}, which made subplot_of_mapper fail with a ValueError inside plot_array. It now raises a clear exc.InversionException naming fit.inversion_with_data / fit.data, and the two mapper subplots (subplot_of_mapper, subplot_mappings) catch it and skip the data-subtracted panel as they already do for AttributeError / KeyError. The workspace interferometer/features/pixelization/fit.py scripts (autogalaxy_workspace, autolens_workspace) are migrated to fit.inversion_with_data in sibling PRs.
  • check_noise_map_real_imag_equal absolute tolerance (pre-existing): np.allclose / np.isclose ran at the default atol=1e-8, so sigma 1e-9 vs 2e-9 passed and the sparse curvature was 1.5x wrong. Now atol=0.0 with the default relative tolerance, so the check is scale-free.
  • Cached data_term dtype (pre-existing path, exposed by the cache): apply_sparse_operator squared data.real / data.imag in the data's own dtype, so complex64 data gave 700140000.0 instead of 700140007.0 (log_evidence +3.5) and disagreed with sparse_terms_from_chunks, which promotes to complex128. The scalar is now computed from complex128-promoted local copies; complex128 inputs are bit-identical to before.

Not in scope: mutating noise_map after apply_sparse_operator stales the operator (W~, dirty image and now the two scalars); that predates phase 1 and is unchanged.

API Changes

Behaviour only. AbstractInversion.data_subtracted_dict raises exc.InversionException (was TypeError / a dict of None) when the inversion has no data; subplot_of_mapper / subplot_mappings tolerate it. check_noise_map_real_imag_equal is stricter for sub-1e-8 sigmas. apply_sparse_operator computes data_term in float64 for any input dtype. No symbol added, removed or renamed; no default changed for complex128 data with Jy-scale noise.
See full details below.

Test Plan

  • pytest test_autoarray — 1765 passed
  • New tests: data_subtracted_dict raises InversionException matching inversion_with_data; both subplots run when it raises; check_noise_map_real_imag_equal rejects 1e-9+2e-9j and accepts 1e-9+1e-9j; complex64 apply_sparse_operator data_term == complex128 value == sparse_terms_from_chunks (rtol 1e-12)
  • Red check: each of the four new tests fails with its source change reverted and passes with it
  • Review reproduction scripts re-run: B → InversionException, both subplots run; D → rejects 1e-9/2e-9; A → 700140007.0 on complex64
  • Workspace scripts: both pixelization fit.py scripts fail on this PR's base with fit.inversion (TypeError / ValueError) and pass with fit.inversion_with_data under the smoke profile
  • CI green on unittest 3.12 / 3.13 / nojax
Full API Changes (for automation & release notes)

Changed Behaviour

  • AbstractInversion.data_subtracted_dict — raises exc.InversionException when inversion.data is None (sparse-operator path with a precomputed data term); message points at fit.inversion_with_data / fit.data
  • autoarray.inversion.plot.inversion_plots.subplot_of_mapper / subplot_mappings — the data-subtracted panel is skipped (not an error) when data_subtracted_dict raises exc.InversionException
  • inversion_interferometer_util.check_noise_map_real_imag_equal — np.allclose / np.isclose with atol=0.0 (default rtol kept)
  • Interferometer.apply_sparse_operator — sparse_operator.data_term computed from complex128-promoted copies of data / noise_map; equals sparse_terms_from_chunks(...).data_term for any input dtype

Migration

  • Code calling subplot_of_mapper(inversion=fit.inversion, ...) (or reading fit.inversion.data_subtracted_dict) on a sparse-operator interferometer fit with no non-linear light profile should pass fit.inversion_with_data.

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

…e noise check and data-term dtype (#590)

Corrective follow-up to Discussion #13 phase 1 (#589), from the independent
review on PyAutoLens#757:

- AbstractInversion.data_subtracted_dict raises a clear InversionException
  (naming fit.inversion_with_data) when the inversion was built without data,
  instead of TypeError / {mapper: None}; subplot_of_mapper and subplot_mappings
  catch it and skip the panel.
- check_noise_map_real_imag_equal uses atol=0.0 so tiny unequal sigmas are
  rejected rather than waved through by the default absolute tolerance.
- apply_sparse_operator computes the cached data_term from complex128-promoted
  copies so one-shot equals sparse_terms_from_chunks for complex64 data.

Tests for all four; red-checked against the reverted source.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Jammy2211

Copy link
Copy Markdown
Collaborator Author

Workspace PR: PyAutoLabs/autogalaxy_workspace#253

@Jammy2211

Copy link
Copy Markdown
Collaborator Author

Workspace PR: PyAutoLabs/autolens_workspace#581

@Jammy2211
Jammy2211 merged commit d429844 into main Sep 30, 2026
3 checks passed
@Jammy2211
Jammy2211 deleted the feature/sparse-data-none-guard branch September 30, 2026 09:51
@Jammy2211 Jammy2211 removed the pending-release PR queued for the next release build label Oct 7, 2026
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.

fix(inversion): guard data_subtracted_dict on data=None, harden sparse noise check and data-term dtype

1 participant