Skip to content

fix: rectangular-mesh pcolormesh plots misaligned with mapper node convention #372

Closed

Description

@Jammy2211

Overview

bug/autoarray/rect_adapt.md (user report: small offsets in RectangularMagnification/adaptive source reconstructions vs other plotting methods). Confirmed and root-caused — the misalignment is real, systematic, and up to ~1.5 mesh pixels.

Assessment (verified numerically)

The rectangular interpolator is node-based: adaptive_rectangular_mappings_weights_via_interpolation_from maps a transformed point U ∈ [0,1]² to index space via (n−3)·U + 1, so reconstruction value (row r, col c) lives at node U_y = (n−r−1)/(n−3), U_x = (c−1)/(n−3) (guard ring at the border, y flipped by row = n − i).

The plot path (autoarray/plot/inversion.py::_plot_rectangular non-uniform branch) instead draws cells on a uniform partition — mesh_geometry.edges_transformed builds linspace(1,0,n+1) / linspace(0,1,n+1) in unit space. The two conventions disagree by ~1 pixel in y (the flip is off-by-one relative to uniform edges) plus border distortion in x.

Delta-function reproduction (n=10, ~identity CDF): a point at (y +0.300, x −0.200) scatters into value cells whose plotted centers are (−0.094…+0.105, −0.105…−0.297) — the dominant-weight cell plots at (−0.094, −0.105). Expected node position for that cell: (≈+0.14, ≈−0.14). Uniform-edge plotting shifts it a further ~1.2 cells in y.

Plan

  • Fix RectangularMeshGeometry.edges_transformed to return mapper-consistent node-midpoint edges: edges_x[c] = (c − 1.5)/(n−3) for c=0..n, edges_y[r] = (n − r − 0.5)/(n−3) (descending, matching the row flip), then transform through the existing CDF machinery. Guard-node cells extend past the CDF domain; reverse_interp clamps, so border guard cells render squashed to the data span (honest: they hold boundary scatter, not interior flux).
  • Both consumers inherit the fix: plot/inversion.py pcolormesh path and mesh_geometry/rectangular_rotated.py (which rotates edges_transformed).
  • Unit test: numpy-only delta-function round-trip — scatter a point through the mapper, assert the plotted cell (via the fixed edges) contains/centers on the point's node to tolerance; plus a regression check that uniform-CDF edges reproduce node midpoints analytically.
  • Spline variant (spline_deg) goes through the same edge array, so the fix is upstream of both CDF flavours.

Autonomy: supervised effective (--auto; bug cap binds over the safe header). Ship sign-off will park with a question per the contract.

Activity

  1. Jammy2211 commented on Jul 10, 2026

    @Jammy2211
    CollaboratorAuthor

    Ship sign-off question (supervised --auto run parks here per the autonomy contract)

    Implementation complete in the worktree (~/Code/PyAutoLabs-wt/rect-adapt/PyAutoArray, branch feature/rect-adapt, deliberately uncommitted pending this sign-off).

    What was being done: the reported "small offsets" in rectangular-mesh source-reconstruction plots — confirmed real via a delta-function reproduction (dominant flux cell plotted ~1.5 mesh pixels from its true node in y).

    The change (1 file + 1 test):

    • autoarray/inversion/mesh/mesh_geometry/rectangular.py::edges_transformed — replaces the uniform [0,1] unit-space partition with the interpolation mapper's node-midpoint edges (U_x = (c−1.5)/(n−3), U_y = (n−r−0.5)/(n−3), guard-safe denominator for degenerate n≤3 meshes), transformed through the existing CDF machinery unchanged. Corrected the docstring (was a copy-paste about pixel neighbors). Both consumers inherit the fix: the pcolormesh plot path and rectangular_rotated.py's warped-centre reconstruction. Spline and linear CDF variants share the same index convention ((n−3)·U + 1), so both are fixed upstream.
    • New regression test: delta scattered through the mapper → weighted centroid of the plotted cells matches the input point to sub-cell precision (pre-fix uniform edges miss by ~0.4 units ≈ 1.5 cells in y).

    Gate status: full suite 877 passed (incl. the pre-existing test__edges_transformed, whose 3×3 expectation is preserved by CDF clamping, and the rotated-mesh ghost tests). Review-faculty + smoke + Heart legs run at ship, after this sign-off.

    On approval: commit, push, open the PR (with ## API Changes: changed behaviour of edges_transformed — plot geometry only; no inversion/likelihood surface touched — the mapper itself is unchanged, so reconstructions/likelihoods are byte-identical; only where flux is drawn moves). On rejection: the worktree is disposable.

    Recommendation: ship — the fix is small, mapper-consistent by construction, and the misalignment it removes is exactly what the report describes.

  2. Jammy2211 commented on Jul 10, 2026

    @Jammy2211
    CollaboratorAuthor

    Sign-off answered in-conversation (go --auto): shipped as PR #375 (suite 877 re-run at ship, review CLEAN, Heart YELLOW within the session ack, MERGEABLE against current main). Merge stays human per the autonomy contract — validation checklist on the PR.

  3. Jammy2211 commented on Jul 10, 2026

    @Jammy2211
    CollaboratorAuthor

    Independent verification from the kernel-CDF session (#373/#374): the fix is confirmed, visually and numerically — including on the new kernel meshes.

    Merged feature/rect-adapt (c177391) into feature/rectangular-kernel-cdf-mesh locally (clean merge, distinct hunks as predicted) and built a visual+numeric verification the original fix lacked: autolens_workspace_developer/plotting_alignment/kernel_cdf_alignment.py, on the mass_centre_source_up_more dataset (truth source at (y,x) = (1.0, 0.0), lens fixed at simulator truth, mask r=3.0).

    Renders (real plot_inversion_reconstruction pipeline, full extent): all five meshes place the source at truth — bright centroids/peak cells within ~1 mesh pixel of (1.0, 0.0) and of each other (uniform imshow reference: centroid (0.969, −0.060); adaptive pcolormesh: adapt_image (0.969, −0.031), adapt_density (0.905, −0.014), kernel variants likewise). The reported ~0.5–1.5 px systematic y-shift is gone. PNGs in C:\Users\Jammy\FromWSL\kernel_cdf_alignment\.

    Strict checks (per adaptive mesh — AdaptImage, AdaptDensity, KernelAdaptDensity, KernelAdaptImage):

    • Node containment: every interior node lies inside its drawn pcolormesh cell (guard-ring clamp ties at the few-ulp level only) — holds for all three CDF flavours, as the node-midpoint construction guarantees under any monotone CDF.
    • Mapper-faithfulness: a delta scattered through the mapper draws at the mapper-encoded centroid Σw·node to 0.01–0.07 local cells. The residual |Σw·node − query| = 0.09–0.24 cells is the mapper's own bilinear-in-U interpolation on non-uniform cells — physics, identical pre/post fix, not a plotting error.

    Notes: the kernel meshes inherit the fix automatically through the shared edges_transformed dispatch (verified in the merged code); the Delaunay tripcolor cross-reference is blocked by the pre-existing Delaunay-in-FitImaging crash (#332), so RectangularUniform imshow served as the trusted reference. Post-merge, the full PyAutoArray suite passes on the combined tree (893, including the new regression test), and the #373 jax_grad certification was re-run against the merged library and passes in full.

    Merge order: #375 first is cleanest (my #374 branch already contains c177391 and has re-run its suite + certification on the combined tree, so nothing further is needed on my side whichever lands first).

  4. Jammy2211 commented on Jul 10, 2026

    @Jammy2211
    CollaboratorAuthor

    PR #375 merged 2026-07-10 (human-directed, from the kernel-CDF session per the coordinated order #375→#374). Fix verified visually + numerically upthread across linear and kernel adaptive meshes. Closing; the rect-adapt session's registry entry can retire.

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