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.
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_frommaps a transformed pointU ∈ [0,1]²to index space via(n−3)·U + 1, so reconstruction value(row r, col c)lives at nodeU_y = (n−r−1)/(n−3),U_x = (c−1)/(n−3)(guard ring at the border, y flipped byrow = n − i).The plot path (
autoarray/plot/inversion.py::_plot_rectangularnon-uniform branch) instead draws cells on a uniform partition —mesh_geometry.edges_transformedbuildslinspace(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
RectangularMeshGeometry.edges_transformedto 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_interpclamps, so border guard cells render squashed to the data span (honest: they hold boundary scatter, not interior flux).plot/inversion.pypcolormesh path andmesh_geometry/rectangular_rotated.py(which rotatesedges_transformed).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.