Skip to content

fix: magnification latent finite for pixelized sources - #727

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/pixelized-source-magnification-latent
Sep 5, 2026
Merged

Jammy2211 merged 1 commit into
mainfrom
feature/pixelized-source-magnification-latent

Conversation

@Jammy2211

@Jammy2211 Jammy2211 commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Part of #726 (library half; the euclid_strong_lens_modeling_pipeline follow-up closes the issue). The magnification latent divides image-plane source flux by total_source_flux_mujy, which read the source galaxy's image_2d_from — zeros for a galaxy whose only light model is a Pixelization. Every pixelized fit therefore had total_source_flux = 0.0 and magnification = inf (dropped as a column by PyAutoFit's NaN guard, or archived as a 0.0 sentinel in the Euclid vis_pix / full_model stages), while the Sersic control on the same fit reproduced truth bit-for-bit.

autolens/analysis/latent.py gains _pixelized_source_flux(fit, xp): the inversion's reconstruction integrated over the source mesh with its areas_for_magnification, summed over every mapper that inversion.linear_obj_galaxy_dict assigns to the source galaxy. total_source_flux and total_source_flux_mujy are now light-profile flux plus that pixelized flux; magnification keeps its form and becomes finite. A mesh geometry with no areas_for_magnification yields NaN with a one-time-per-process warning — never a silent zero. The lookup deliberately uses the dict the inversion already carries rather than fit.tracer_to_inversion, which is an uncached property whose freshly built mappers would not key reconstruction_dict. All branching is on Python structure (None checks, isinstance, dict membership), so the per-sample jax.jit latent path is unaffected.

End-to-end Delaunay accuracy depends on PyAutoArray#524 / PR #525 (barycentric dual areas as areas_for_magnification, merged 2026-09-05, pending release). The unit tests here assert internal consistency (Σ image / Σ reconstruction·areas) and hold on either PyAutoArray.

API Changes

Changed behaviour, no signature changes:

  • total_source_flux, total_source_flux_mujy and magnification now include a pixelized source's reconstruction flux. For a pixelization-only source they were 0.0 / 0.0 / inf; they are now the integrated reconstruction and a finite ratio. Light-profile-only fits are numerically unchanged.
    See full details below.

Test Plan

  • test_autolens/analysis/test_latent.py: 31 passed (7 new — Delaunay and rectangular pixelized fits give a finite magnification equal to Σ galaxy image / Σ reconstruction·areas_for_magnification; linear light profile + pixelization sums both terms; light-profile-only fit unchanged; no-inversion and no-source-mapper branches return 0.0; a mapper without mesh areas gives NaN and exactly one warning)
  • Full suite python -m pytest test_autolens/ -n auto: 617 passed, 1 xfailed
  • CI green on the Python 3.12 / 3.13 matrix (+ nojax leg, docs build)
  • Human acceptance after the workspace follow-up: the phase 8 audit's part3b_fit_latent.py construction (Delaunay fit on euclid_dr1_like, magzero 24.6) reports a finite magnification close to the Sersic control's 15.146 — record on fix: magnification latent is inf/0.0 for any pixelized source #726
Full API Changes (for automation & release notes)

Added

  • autolens.analysis.latent._pixelized_source_flux(fit, xp=np) — private helper; source-plane flux of the source galaxy's inversion component, Σ_i s_i A_i over each of its mappers, using inversion.linear_obj_galaxy_dict for mapper → galaxy and mesh_geometry.areas_for_magnification for the areas. Returns 0.0 when the fit has no inversion or no mapper belongs to the source galaxy; NaN (+ one warning) when a mesh geometry publishes no areas_for_magnification.

Changed

  • autolens.analysis.latent.total_source_flux(fit, magzero=None, xp=np) — now light-profile flux + _pixelized_source_flux.
  • autolens.analysis.latent.total_source_flux_mujy(fit, magzero, xp=np) — same sum, then the µJy conversion.
  • autolens.analysis.latent.magnification(fit, magzero, xp=np) — unchanged in form; finite for pixelized sources as a consequence.

Removed

  • None.

Migration

  • None required. Downstream consumers of the magnification latent (e.g. euclid_strong_lens_modeling_pipeline's catalogue) will start receiving real values for pixelized stages; archived vis_pix rows carrying the 0.0 sentinel must be re-derived.

Generated by the PyAutoLabs agent workflow.

🤖 Generated with Claude Code

https://claude.ai/code/session_011kyfKgDB1rMkcQsn19ow4T

`total_source_flux` / `total_source_flux_mujy` read the source galaxy's
`image_2d_from`, which is zeros for a galaxy whose only light model is a
`Pixelization`, so every pixelized fit had `total_source_flux = 0.0` and
`magnification = inf` (a dropped column, or an archived `0.0` sentinel).

Add `_pixelized_source_flux`: the inversion's reconstruction integrated
over the source mesh with its `areas_for_magnification`, summed for every
mapper that `inversion.linear_obj_galaxy_dict` assigns to the source
galaxy. Light-profile flux plus pixelized flux is now the source-plane
flux; `magnification` keeps its form and becomes finite. A mesh geometry
with no `areas_for_magnification` gives NaN with a one-time warning, never
a silent zero. All branching is on Python structure, so the per-sample
`jax.jit` latent path is unaffected.

Tests: Delaunay and rectangular pixelized fits give a finite magnification
equal to sum(image) / sum(reconstruction * areas); linear light profile +
pixelization sums both terms; light-profile-only fits are unchanged; the
no-inversion, no-source-mapper and missing-areas (NaN + one warning)
branches are pinned.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kyfKgDB1rMkcQsn19ow4T
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label Sep 5, 2026 — with Claude
@Jammy2211
Jammy2211 merged commit 633c8e0 into main Sep 5, 2026
4 checks passed
@Jammy2211
Jammy2211 deleted the feature/pixelized-source-magnification-latent branch September 5, 2026 13:58
palacios22c pushed a commit to palacios22c/PyAutoLens that referenced this pull request Sep 13, 2026
…utoLabs#726)

`_pixelized_source_flux` (PR PyAutoLabs#727) returned the plain surface integral
`sum_i s_i A_i` over the source mesh, but every other flux latent in the
module is a sum over data pixels (`integral / A_pix`), and the
reconstruction `s_i` is in those per-data-pixel units. The pixelized
`magnification` therefore scaled with the data's pixel scale: on the
Euclid pipeline's 0.1"/pixel simulated dataset it came out 1095, exactly
100x the plausible 10.95. The 1" fixtures have `A_pix = 1`, which hid it.

Divide by the data pixel area read from `fit.dataset.grids.lp` (the grid
the light-profile term is evaluated on), a Python float so the jit path
is unaffected; NaN plus one warning if no pixel area resolves. Docstrings
state the convention.

Tests: the existing pixelized tests state `sum(s A) / A_pix`; a fit at
0.5"/pixel pins the division (4.561 -> 1.140, exactly the pixel-area
factor); an unresolvable pixel area gives NaN and one warning; a
fixed-field-of-view scene observed at 0.4" and 0.2" gives magnifications
within a factor 2 of each other, which the undivided value violated by 4x.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kyfKgDB1rMkcQsn19ow4T
@Jammy2211 Jammy2211 removed the pending-release PR queued for the next release build label Sep 26, 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.

1 participant