Skip to content

audit: Delaunay pixel areas and magnification paths (euclid-dr1-prep phase 8) #522

Description

@Jammy2211

Overview

Phase 8 of the euclid-dr1-prep epic (was 6c; renumbered in the 2026-09-01 Cortex split). The Delaunay source model's magnification has never been validated and the epic's owner suspects the pixel areas. This is a source-code audit of every Delaunay area/magnification path in PyAutoArray, with known-answer tests landed regardless of outcome. It is the software half of the question; the empirical half is Cortex phase 7 (PyAutoCortex/phases/euclid/magnification_robustness.md), which this audit must brief. Any real defect is filed as a separate bug prompt — this task does not grow into the fix.

Plan

  • Map every Delaunay area/magnification code path — Voronoi cell areas behind areas_for_magnification, barycentric dual areas behind the split-point weights, the tripcolor plotting path, the workspace source_science.py consumer, and the Euclid pipeline's magnification latent — and state what each computes.
  • Prove or drop each suspicion with known-answer tests (analytic lattices, convex-hull totals, exact integrals of a linear field), not resemblance.
  • Land the missing direct tests for areas_for_magnification and barycentric_dual_area_from, and correct the areas_for_magnification docstring to what is actually zeroed.
  • Check whether the Euclid pipeline's magnification latent survives a Delaunay vis_pix / full_model stage at all.
  • Write the verdict here and in the epic ledger, with an explicit statement for Cortex phase 7: are Delaunay magnification numbers trustworthy, and which area must they use?
  • Any proven defect → separate draft/bug/autoarray/ prompt with the reproduced failing case.
Detailed implementation plan

Affected Repositories

  • PyAutoArray (primary — tests + docstring)
  • PyAutoLens (read-only: autolens/analysis/latent.py::magnification reachability check; no edits unless a defect is found, then a separate prompt)

Branch Survey

Repository Current Branch Dirty?
./PyAutoArray main clean
./PyAutoLens main clean

Suggested branch: feature/delaunay-area-magnification-audit

What the pre-plan survey established

  1. Two different "areas" exist for a Delaunay mesh on two different paths:
    • MeshGeometryDelaunay.areas_for_magnification (autoarray/inversion/mesh/mesh_geometry/delaunay.py:195) = voronoi_areas_numpy (shoelace over scipy Voronoi cells). Only unbounded cells are marked -1 and zeroed; bounded-but-huge boundary cells are kept (the existing test__voronoi_areas_via_delaunay_from shows index 3 = 29.8 kept, index 4 = −1). The docstring says boundary pixels are zeroed — it overstates.
    • barycentric_dual_area_from (autoarray/inversion/mesh/interpolator/delaunay.py:342, plus an in-graph JAX scatter-add copy at ~:309) = Σ(triangle area / 3) per vertex; used only to weight split-regularisation points via areas_factor * sqrt(areas). Not used for flux or magnification.
  2. Plotting (autoarray/plot/inversion.py::_plot_delaunay) uses tripcolor Gouraud — linear interpolation over triangles, whose integral is the barycentric dual area, not the Voronoi cell. Picture and number come from different geometry.
  3. areas_for_magnification has zero library callers; it serves only the four pixelized source_science.py workspace scripts (Σ reconstruction × mesh_areas as source-plane flux). Those scripts are what Cortex phase 7's Delaunay leg would use.
  4. The Euclid pipeline's magnification latent (autolens/analysis/latent.py::magnification) is a flux ratio via galaxies[-1].image_2d_from(...) and never touches areas_for_magnification. Whether it is finite for a Pixelization source galaxy is unverified.
  5. sqrt is not on the magnification path; the known NaN-gradient hazard is confined to the interpolator's split-point weights.
  6. The cluster-epic prompt draft/test/workspaces/mesh_magnification_correctness.md owns the library-level magnification API, non-Delaunay meshes and the simulate-and-recover campaign. This task stays Delaunay-only.

Implementation Steps

  1. test_autoarray/inversion/pixelization/mesh_geometry/test_delaunay.py — add:
    • test__areas_for_magnification__uniform_lattice: n×n unit lattice → every interior Voronoi cell area == 1.0; every hull point unbounded → 0.0; Σ == (n−2)². Pins the real semantics.
    • test__areas_for_magnification__bounded_boundary_cells_are_kept: the existing 6-point configuration → index 3 (≈29.8) is kept, only index 4 zeroed — documents the bias candidate explicitly.
    • test__areas_for_magnification__repeat_calls_agree: two calls agree; the -1 sentinel is not written back into any cached state.
  2. test_autoarray/inversion/mesh/interpolator/test_delaunay.py (create if absent, mirroring the existing interpolator test layout) — add:
    • test__barycentric_dual_area__single_triangle: each vertex == A/3.
    • test__barycentric_dual_area__sums_to_convex_hull_area: random points → Σ dual == scipy.spatial.ConvexHull(points).volume; assert Σ areas_for_magnification differs for the same points (records the divergence).
    • test__linear_field_integral__dual_areas_exact: f = a + b·y + c·x at the vertices; exact integral over the hull equals Σ f_i·dual_i to rounding; Σ f_i·voronoi_i differs on a non-uniform mesh.
    • NumPy vs JAX parity: barycentric_dual_area_from(xp=np) equals the in-graph scatter-add on the same padded simplices.
  3. autoarray/inversion/mesh/mesh_geometry/delaunay.py — rewrite the areas_for_magnification docstring to the proven semantics (unbounded cells → 0; bounded boundary cells kept; these are Voronoi cells, not the interpolant's dual areas). No behavioural change.
  4. Audit evidence (scratch script, numbers reported here, not shipped): a Gaussian source strictly inside the hull of a Delaunay mesh; compare Σ reconstruction·voronoi areas, Σ reconstruction·dual areas, and the mapped-reconstruction image-plane flux against the analytic source flux. Report which denominator recovers μ and the sign/size of the boundary effect.
  5. Euclid reachability (read-only): look for magnification in the latent outputs of the vis_pix / full_model stages under euclid_strong_lens_modeling_pipeline/output/ral_*; if absent (NaN latents are dropped from latent_summary.json), confirm by evaluating the latent on a stored Delaunay fit in test mode. Outcome is a finding.
  6. If a defect is proven (expected candidate: the magnification denominator should integrate the interpolant with barycentric dual areas, or bounded boundary cells bias the Voronoi sum), file draft/bug/autoarray/<name>.md via /intake with the reproduced case and Epic: euclid-dr1-prep; cross-link from the ledger and here. Do not fix in this PR.
  7. Write the per-path audit table (path, what it computes, verdict, evidence) and the Cortex-phase-7 statement into this issue and the epic ledger.

Key Files

  • autoarray/inversion/mesh/mesh_geometry/delaunay.py — voronoi_areas_numpy, voronoi_areas, areas_for_magnification
  • autoarray/inversion/mesh/interpolator/delaunay.py — barycentric_dual_area_from, in-graph dual areas, split-point weights
  • autoarray/plot/inversion.py — _plot_delaunay
  • test_autoarray/inversion/pixelization/mesh_geometry/test_delaunay.py — existing test__voronoi_areas_via_delaunay_from
  • autolens_workspace/scripts/imaging/features/pixelization/source_science.py — the only consumer of areas_for_magnification
  • PyAutoLens/autolens/analysis/latent.py — magnification, total_source_flux

Verification

  • pytest test_autoarray/inversion/pixelization/mesh_geometry/test_delaunay.py test_autoarray/inversion/mesh -q green in the worktree; full pytest test_autoarray -q before the PR.
  • Every Delaunay area path either matched to an analytic answer in a landed test or flagged with a reproduced failing case here.

Original Prompt

Click to expand starting prompt

Audit the Delaunay pixel-area and magnification source code for correctness bugs

Type: bug
Target: autoarray
Repos:

  • PyAutoArray
  • PyAutoLens
    Themes:
  • pixelization
  • euclid
    Difficulty: small-medium
    Autonomy: supervised
    Priority: normal
    Status: formalised
    Consequence: judge
    Review-minutes: 25
    Unattended: ready
    Epic: euclid-dr1-prep
    Phase: 8
    Parent: draft/feature/euclid/euclid_dr1_prep_epic.md
    Filed: 2026-08-28

Phase 8 of the Euclid DR1 preparation epic (was 6c; renumbered 2026-09-01 in the Cortex split,
where the old 6b became PyAutoCortex phases/euclid/magnification_robustness). Can run
alongside the magnification-robustness phase and does not gate on it
— that phase is the
empirical half (does μ come out right on data?), this one is the
source-code half (does the code look right?). Either can find the defect first.

User request (verbatim, the follow-up clause of 6b):

"""
We have also never really validated or tested the magnifcitations using
the Delaunay source model, and this could even have bugs (E.g due to pixel areas not being quite right). So, for this
Euclid epic do all of the magnification comparisons you can on the 10 lenses in this spirit, but also have a follow up issue
which checks if the soruce code itself looks like it might have a bug or issue with the Delaunay area and thus magnification calculations.
"""

Where to look

  • PyAutoArray mesh geometry: the Delaunay areas_for_magnification implementation
    (voronoi_areas, with boundary cells zeroed via a -1 sentinel) and its
    rectangular-adaptive sibling. The cluster epic's audit
    (draft/test/workspaces/mesh_magnification_correctness.md) already established that
    areas_for_magnification exists for only two mesh geometries, has zero library
    callers
    (it serves only workspace scripts), and has no direct test — only its
    delegates are tested. That is exactly the shape a latent bug hides in.
  • The boundary-cell semantics. Zeroing boundary cells changes the summed source-plane
    area and therefore μ directly. Is the zeroing correct, or does it bias μ high?
  • PyAutoArray/autoarray/plot/inversion.py::_plot_delaunay and the Delaunay mapper —
    check whether the areas used for plotting and the areas used for magnification
    come from the same computation. Divergence there is a classic source of "the picture
    looks right but the number is wrong".
  • The interaction with sqrt on dual areas — there is a known NaN hazard in the Delaunay
    dual-area gradient path; check whether the same expression is on the magnification
    path.

Method

This is an audit, not a rewrite. Read the code, then prove each suspicion or drop it:

  • Construct known-answer configurations (a uniform triangulation of known total area,
    a mesh with an analytically computable source-plane area) and check the code returns
    the analytic value. A resemblance verdict is not verification.
  • Where a suspected bug is found, reproduce it in a minimal test before proposing a
    fix, and check whether it is reachable from the paths Euclid actually uses.
  • Do not exempt a case because it "looks intentional" — if boundary zeroing is
    deliberate, find the commit or comment that says so.

Deliverables

  1. A written audit: each area/magnification code path, what it computes, and whether it
    is correct.
  2. Known-answer tests for Delaunay areas_for_magnification (the missing direct test),
    landed regardless of whether a bug is found — the absence of a test is itself the
    defect the cluster-epic audit flagged.
  3. If a real defect is found: file a separate bug prompt for the fix rather than
    growing this one, and tell phase 6b immediately so its Delaunay leg is interpreted
    correctly.

Acceptance / gate

  • Every Delaunay area/magnification path either verified against an analytic answer or
    flagged with a reproduced failing case.
  • Direct tests exist where there were none.
  • A clear statement to 6b: are its Delaunay magnification numbers trustworthy?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions