Skip to content

fix: adaptive rectangular meshes report a uniform overlay as node positions #667

Closed

Description

@Jammy2211

Overview

For the four adaptive rectangular meshes, mapper.source_plane_mesh_grid returns a uniform overlay over the traced data's bounding box, not the source-plane positions where the mapper's interpolant places each reconstruction value. Plots are fine (they use edges_transformed), but the reconstruction CSV's y/x columns, inversion.max_pixel_centre (used by PyAutoLens automatic position updating) and every workspace interpolation of the reconstruction use the wrong positions. Found while working on autolens_workspace#605 (epic cluster-strong-lensing, phase 4e blocker).

Plan

  • Add a node-centre property to the rectangular mesh geometry: each node's unit-CDF position mapped back through the same inverse adaptive transform the interpolant uses; guard-ring nodes clamped to the data span (documented).
  • Make mapper.source_plane_mesh_grid return those centres for adaptive meshes; leave interpolator.mesh_grid alone (the geometry's extent depends on it). RectangularUniform and the Delaunay family unchanged.
  • The CSV writer and max_pixel_centre then pick up the right positions; verify each.
  • Tests: node basis weight is 1 at its reported centre; griddata on the centres integrates to sum(reconstruction * areas_for_magnification); RectangularUniform unchanged.
  • Measure how far the brightest-pixel centre moves on a realistic fit and state in the PR whether past SLaM runs with adaptive meshes were materially affected.
  • Re-run autolens_workspace_test scripts/imaging/mesh_magnification.py (areas untouched).
  • Then resume autolens_workspace#605 on top.

Tier: judge — merge mode: human /prm.

Detailed implementation plan

Affected Repositories

  • PyAutoArray (primary)

Branch Survey

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

Suggested branch: feature/adaptive-rect-mesh-node-positions

Implementation Steps

  1. autoarray/inversion/mesh/interpolator/rectangular.py: helper adaptive_rectangular_node_centres_from(...) — node unit coordinates U_y = (n_y - r - 1)/(n_y - 3), U_x = (c - 1)/(n_x - 3) in mapper order, through _transforms_from inverse (as adaptive_rectangular_transformed_grid_from); clamp guard nodes to the data span; xp-compatible.
  2. autoarray/inversion/mesh/mesh_geometry/rectangular.py: node_centres property (adaptive → helper; uniform → mesh_grid).
  3. autoarray/inversion/mappers/abstract.py (or the rectangular interpolator): route source_plane_mesh_grid through mesh_geometry.node_centres for rectangular meshes, returned as Grid2DIrregular.
  4. Check consumers: inversion/plot/inversion_plots.py CSV writer, inversion/inversion/abstract.py max_pixel_centre, inversion/mappings/mapping.py (Delaunay/kNN only), preloads (Delaunay mesh grids only).
  5. Tests in test_autoarray/inversion/... per the plan; nojax leg: guard JAX-only tests with importorskip.
  6. Downstream: PyAutoGalaxy/PyAutoLens test suites; workspace_test mesh_magnification.

Key Files

  • autoarray/inversion/mesh/mesh/rectangular_rtu_adapt_density.py (~L273, overlay_grid_from)
  • autoarray/inversion/mesh/interpolator/rectangular.py
  • autoarray/inversion/mesh/mesh_geometry/rectangular.py
  • autoarray/inversion/mappers/abstract.py
  • PyAutoLens autolens/analysis/result.py ~L566 (position updating consumer)

Original Prompt

Click to expand starting prompt

Adaptive rectangular meshes report a uniform overlay as their source-plane node positions

Type: bug
Target: autoarray
Repos:

  • PyAutoArray
    Epic: cluster-strong-lensing
    Phase: 4e (blocker)
    Arc: draft/feature/autolens/source_cluster_arc.md
    Difficulty: medium
    Autonomy: supervised
    Priority: high
    Consequence: judge
    Filed: 2026-10-30
    Witness: for every adaptive rectangular mesh, mapper.source_plane_mesh_grid equals the source-plane positions at which the mapper's interpolant takes each reconstruction value (node i's basis function peaks there); griddata of the reconstruction on those positions integrates to sum(reconstruction * areas_for_magnification) within a few percent; the reconstruction CSV writes those positions; RectangularUniform and Delaunay-family output is unchanged.

Original request (human, 2026-10-30)

Library fix first (Recommended) — Park this PR. File and fix a PyAutoArray bug so source_plane_mesh_grid (or a
new attribute) gives the adapted node centres, then come back and finish this workspace PR on top of it,
library-first. The whole script then gives right answers.

Finding (autolens_workspace#605 comment, 2026-10-30)

Every adaptive rectangular mesh (RectangularBilinearAdaptDensity, RectangularBilinearAdaptImage,
RectangularRTUAdaptDensity, RectangularRTUAdaptImage) builds its interpolator with
mesh_grid=overlay_grid_from(shape, relocated_grid.over_sampled)
(autoarray/inversion/mesh/mesh/rectangular_rtu_adapt_density.py ~L273): a uniform grid over the traced
data's bounding box. The mapper's interpolant instead places reconstruction value (row r, col c) at unit-CDF
node U_y = (n_y - r - 1)/(n_y - 3), U_x = (c - 1)/(n_x - 3) (guard ring at the border), mapped back to the
source plane through the inverse adaptive CDF (adaptive_rectangular_transformed_grid_from). So
mapper.source_plane_mesh_grid (= interpolator.mesh_grid) is not where the values live.

Measured on autolens_workspace imaging/simple__no_lens_light (true tracer, 28×28 BilinearAdaptDensity,
coefficient 1.0, extra galaxy noise-scaled): reported node spacing is uniformly 0.11″ (data span / 28); the
library's own areas_for_magnification at the brightest nodes is 0.0013 arcsec² against 0.0097 implied by the
reported positions; correlation between the areas and the Delaunay dual of the reported positions is 0.13.
griddata of the reconstruction on the reported positions integrates ~4.6× too high, so the workspace
interpolation-route magnification is 6.9 against a mesh-route 31.6 and a truth of 34.9, at any interpolation
resolution.

Blast radius

  • autoarray/inversion/plot/inversion_plots.py ~L510: source_plane_reconstruction_{i}.csv (a documented science
    product) writes y, x from mapper.source_plane_mesh_grid, so its coordinates are wrong for adaptive meshes.
  • Every workspace source_science.py / CSV-loading example that interpolates with griddata on those positions
    (interpolated source, zoom, errors, S/N masking, interpolation-route magnification), plus anything downstream
    that reads the CSV.
  • autoarray/inversion/inversion/abstract.py ~L1640 max_pixel_centre: the brightest source pixel's position is
    taken from mapper.source_plane_mesh_grid, so it is wrong for adaptive meshes. PyAutoLens
    autolens/analysis/result.py ~L566 uses it for automatic position updating (brightest source pixel →
    PointSolver → image-plane multiple-image positions → SLaM positions likelihood). Quantify how far the reported
    brightest-pixel centre moves on a realistic fit, and say in the PR whether past SLaM runs with adaptive
    rectangular meshes were materially affected.
  • Plots are fine: autoarray/plot/inversion.py uses mesh_geometry.edges_transformed.
  • Check before changing interpolator.mesh_grid itself: the rectangular interpolator passes it to its mesh geometry
    (interpolator/rectangular.py ~L759), and preloads / to_inversion code paths touch source_plane_mesh_grid.
    A separate attribute that the mapper and the CSV writer use may be the safer shape — decide in the plan.

Work

  1. Decide the API: make mapper.source_plane_mesh_grid return the adapted node centres for adaptive rectangular
    meshes, or add a node-centre attribute on the rectangular mesh geometry and route the mapper property and the
    CSV writer through it. Guard-ring nodes: state where they sit (clamped to the data span) and say so in the
    docstring.
  2. Unit tests: node centres equal the positions where each node's basis weight is 1 (map a grid at the centres
    through the mapper and check the weight matrix is ~identity on interior nodes); the griddata integral check in
    the Witness; RectangularUniform unchanged.
  3. Fix the CSV writer if it does not pick the change up automatically; note the coordinate change in the PR body
    (CSV contents change for adaptive meshes).
  4. Re-run autolens_workspace_test scripts/imaging/mesh_magnification.py (areas untouched; it must stay green).
  5. Then resume pixelization-mesh-mag-docs (autolens_workspace#605, branch feature/pixelization-mesh-mag-docs,
    WIP commit 7c30f74f): scale out the extra galaxy, interferometer coefficient, print the truth beside both routes,
    honest accuracy wording.

Activity

  1. Jammy2211 commented on Oct 30, 2026

    @Jammy2211
    CollaboratorAuthor

    Library PR Created

    PR: #668 (judge tier, human /prm). Positions fixed; kernel-regularization distances kept on the lattice explicitly (no likelihood change). Workspace follow-up: autolens_workspace#605 (resumes on top of this PR). Research: Mind draft/research/autolens_inference/kernel_regularization_distances_adaptive_rectangular.md.

  2. Jammy2211 commented on Oct 30, 2026

    @Jammy2211
    CollaboratorAuthor

    Shipped

    Merged #668 (8ce5baeb), pending release. Adaptive rectangular meshes now report their true pixel centres via source_plane_mesh_grid (CSV, max_pixel_centre, plot zoom, potential correction); kernel regularizations stay on the lattice explicitly (regularization_mesh_grid, no likelihood change). Workspace follow-up autolens_workspace#605 resumes on top.

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