fix: sparse interferometer fits with ordinary light profiles use the profile-subtracted dirty image (PyAutoArray#575) - #629
Merged
Conversation
…cted visibilities (PyAutoArray#575) FitInterferometer passes sparse_dirty_image (the noise-weighted adjoint transform of profile_subtracted_visibilities, as apply_sparse_operator builds it) whenever the dataset has a sparse operator and a galaxy has an ordinary light profile (Basis contents checked, so an all-linear MGE keeps the cached image at no cost). Shared helper sparse_dirty_image_from is reused by PyAutoLens. Tests: sparse vs dense log_likelihood / log_evidence to 1e-8 with and without an ordinary light profile. Refs PyAutoLabs/PyAutoArray#575 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…T (PyAutoArray#575) sparse_dirty_image_from now returns d~ - W~ i_p, where i_p is the ordinary light-profile image the fit's profile_visibilities transform. W~ is the operator the sparse curvature matrix already uses, so this adds no new assumption and replaces a type-1 NUFFT over every visibility with one FFT convolution (alma CPU jit, MGE + lens Sersic: 4.54 s -> 1.30 s; dense 13.8 s). FitInterferometer caches profile_image and builds profile_visibilities from it. Tests assert profile_image transforms to the fit's profile_visibilities. (The TransformerDFT.image_from xp change in PyAutoArray, an incidental fix for a latent jit crash, is kept.) Refs PyAutoLabs/PyAutoArray#575 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inary light (PyAutoArray#575) An MGE Basis counts as a LightProfile, so profile_visibilities forward-NUFFTed an all-zero image on every call of an MGE-only fit. It now returns zeros unless _has_light_profile_non_linear (Basis-aware, structural, jit-safe) and only then evaluates profile_image. alma CPU jit, MGE-only sparse: 0.995 s -> 0.579 s. Test: MGE-only profile_visibilities are zeros and the transformer is not called. Refs PyAutoLabs/PyAutoArray#575 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Added a follow-up commit (approved by the human, 2026-09-26): |
3 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This fixes a silent correctness bug in sparse (W~) interferometer fits that also contain ordinary (non-linear) light profiles. The sparse data vector was formed from the dirty image cached at
apply_sparse_operator()time, which is built from the unsubtracted visibilities, while the chi-squared used the profile-subtracted visibilities. With a Sersic + linear Gaussian, the sparse fit gave logL −26.772 against −24.342 dense; the test fails on the old code and passes with the fix. At sma, D was off by 23% once a lens Sersic was present. Mixed mapper + MGE fits were already affected; PyAutoArray#575 routes MGE-only fits through W~ too.The fix uses linearity:
Re(Fᴴ W (d − F i_p)) = d~ − W~ i_p.W~is the operator the sparse curvature matrix already uses, so the correction is one FFT convolution on the real-space grid (≈2 ms at alma). An adjoint NUFFT over every visibility would take ≈4 s, and there is no new noise assumption. All-linear fits (including an MGEBasis) are detected structurally and pay nothing. At alma under CPU jit, MGE + lens Sersic takes 1.30 s sparse against 13.8 s dense, with identical logL.Stacked on PyAutoLabs/PyAutoArray#576 (PyAutoArray#575) (merge that first).
API Changes
autogalaxy.interferometer.fit_interferometer.sparse_dirty_image_from(dataset, galaxies, image, xp=np): the corrected dirty image for the sparse inversion, orNonewhen the cached one is right.FitInterferometer.profile_image: the summed ordinary-light image ongrids.lp.profile_visibilitiesnow transforms it, so the light is evaluated once. The values are unchanged.See full details below.
Test Plan
test_autogalaxyfull suite: 1240 passedBasis. Controls: all-linear gives aNonedirty image, andprofile_imagetransforms toprofile_visibilitiesto 1e-12.Full API Changes (for automation & release notes)
Added
autogalaxy.interferometer.fit_interferometer.sparse_dirty_image_from(dataset, galaxies, image, xp=np)ag.FitInterferometer.profile_image(cached property)Changed Behaviour
FitInterferometer.galaxies_to_inversionpassessparse_dirty_imagetoaa.DatasetInterfacewhen the dataset has a sparse operator and any galaxy has an ordinary light profile. ABasismade only of linear profiles does not count as an ordinary light profile.FitInterferometer.profile_visibilitiesis computed fromprofile_image. The values are unchanged.Migration
Heart RED override (development only)
The human authorized the AUTONOMY.md "Human override for Heart RED (development only)" for this task (PyAutoArray#575, branch
feature/interferometer-mge-w-tilde-route) on 2026-09-26. When asked how to proceed with the three library PRs, they chose "Override, open PRs". The override covers commit, push and opening pending-release PRs only. Merging needs a separate/prmwith every required check green. This branch does not claim to fix Heart.Exact RED reasons at ship time:
Branch gates passed: 24/24 targeted interferometer smoke scripts pass on the branch (autolens_workspace 11, autogalaxy_workspace 5, autolens_workspace_test 5, autogalaxy_workspace_test 3: MGE, pixelization, linear light profiles, sparse-operator JAX assertions and likelihood pins).
autolens_workspace_test/.../jax_likelihood/mge.pyfirst failed identically on the branch and on the mains because the local dataset was stale (2026-08-06, older than the 09-18 simulator change); after regenerating it, the script passes on both. Unit suites: PyAutoArray 1706 passed, PyAutoGalaxy 1240 passed, PyAutoLens 757 passed + 1 xfailed. My own diff review is in-session only; the human reviews at merge.🤖 Generated with Claude Code