Repository navigation
Description
Overview
Split the rectangular adaptive mesh family into two user-facing variants: Bilinear (RectangularBilinearAdaptDensity / RectangularBilinearAdaptImage), a resurrected empirical rank-CDF lattice transform that is fast on CPU, and RTU (RectangularRTUAdaptDensity / RectangularRTUAdaptImage), pure renames of the current kernel-CDF classes. The RTU kernel-CDF transform dominates the numba CPU likelihood (55% of a euclid eval, 89% at hst; O(M_sub × N_data) erf sum), so the conceptually simpler rank-CDF variant becomes the workspace default while RTU is documented as the advanced option (GPU / gradient samplers / interferometer). Resolves option (2) of the Phase 14 default-mesh decision (autolens_profiling PROGRAMME Phase 14 / autolens_profiling#153).
Plan
- Resurrect the empirical rank-CDF adaptive transform (sort + cumsum) deleted in the July mesh consolidation and pair it with the existing 4-pixel bilinear interpolation as
RectangularBilinearAdaptDensity/RectangularBilinearAdaptImage. - Pure-rename the current kernel-CDF classes to
RectangularRTUAdaptDensity/RectangularRTUAdaptImage— implementation and likelihood values unchanged; the RTU implementation is never deleted (it is the only certified gradient mesh on the interferometer sparse path). RectangularUniformstays as-is.- Update exports, configs and unit tests; PR release notes carry an
## API Changesheading (breaking rename). - Docs must state: gradient inference on the Bilinear default needs
over_sample_size_pixelization >= 4(imaging); interferometer gradient work needs RTU. - Workspace follow-up (separate PRs once the library API lands): default examples switch to Bilinear; RTU documented separately as the advanced option, folding in the queued Enzi-citation docs draft; prior-config yamls added/renamed;
_testlikelihood pins regenerated for Bilinear scripts (overwrite) while RTU pin scripts survive as pure renames; update autolens_profiling#153 / PROGRAMME Phase 14 with the decision.
Detailed implementation plan
Work Classification
Both (library first — workspace PRs follow once the PyAutoArray API lands).
Affected Repositories
- PyAutoArray (primary)
- autolens_workspace (follow-up)
- autogalaxy_workspace (follow-up)
- autolens_workspace_test (follow-up)
Branch Survey
| Repository | Current Branch | Dirty? |
|---|---|---|
| ./PyAutoArray | main (90f6f0c) | clean |
| ./PyAutoMind | claude/bilinear-rtu-mesh-docs-pbhsxg (tracks main) | clean |
Workspace repos are not checked out in this session; the workspace phase attaches them after the library PR merges. Local-dev worktree root (created by /start_library when resumed locally): ~/Code/PyAutoLabs-wt/rectangular-bilinear-rtu-mesh-split/.
Suggested branch: feature/rectangular-bilinear-rtu-mesh-split
Implementation Steps (library phase, PyAutoArray)
- Recover the empirical rank-CDF transform from the pre-consolidation tree —
git show 22b28463^:autoarray/inversion/mesh/interpolator/rectangular.py(create_transformswithargsort/take_along_axis/cumsum, plus itsforward_interp/reverse_interpjax+numpy pairs). Do not reinvent it. Add it toautoarray/inversion/mesh/interpolator/rectangular.pyalongside the kernel-CDFcreate_transforms, selectable by the interpolator (e.g. atransform="rank"|"kernel"seam threaded throughadaptive_rectangular_transformed_grid_from,adaptive_rectangular_areas_from,adaptive_rectangular_mappings_weights_via_interpolation_fromandInterpolatorRectangular). - New mesh classes
RectangularBilinearAdaptDensity/RectangularBilinearAdaptImageinautoarray/inversion/mesh/mesh/— mirror the currentRectangularAdaptDensity/RectangularAdaptImageshape (overlay grid,mesh_weight_map_from,interpolator_from) but with the rank-CDF transform and nobandwidth/n_knotshyperparameters (that simplicity is the point of the split). - Pure-rename
RectangularAdaptDensity→RectangularRTUAdaptDensityandRectangularAdaptImage→RectangularRTUAdaptImage(kernel-CDF internals untouched). Updateautoarray/inversion/mesh/mesh/__init__.pyand top-levelautoarray/__init__.pyexports. No back-compat aliases — the rename is intentionally breaking, recorded under## API Changes. - Docstrings encode the guidance split: Bilinear = fast CPU default, piecewise-constant likelihood at
over_sample_size_pixelization=1(gradients exactly zero — gradient users set os_pix>=4 or use RTU); RTU = smooth C-inf transform, correct gradients in every configuration incl. the interferometer sparse path, recommended on GPU (cite Enzi et al. arXiv:2606.30620). - Tests: recover/adapt the relevant deleted empirical-CDF unit tests from
22b28463^(test_autoarray/inversion/pixelization/...), keep the existing rectangular tests running against the renamed RTU classes, add Bilinear cases to the interpolator + meshtest_rectangular.pyfiles. - Grep for any config/registry references to the old class names inside PyAutoArray (e.g.
autoarray/config, JSON/dict serialization of mesh classes) and update. - Run the mesh/pixelization test suite; open the library PR with
## API Changesrelease notes.
Workspace follow-up (separate PRs, after library merge)
autolens_workspace(~224 uses),autogalaxy_workspace, HowToLens (~42): default examples →RectangularBilinearAdaptDensity; RTU documented as the recommended advanced option (GPU / gradient samplers / interferometer), folding inPyAutoMind draft/docs/workspaces/rectangular_mesh_enzi_citation_examples.md(retire that draft when folded in).- Prior configs: add
mesh/rectangular_bilinear_adapt_*.yaml; rename RTU yamls to the new class names (autoconf lowercases keys). autolens_workspace_test: RTU pin scripts survive as pure renames (values unchanged); add/switch default-mesh scripts to Bilinear and regenerate pins, overwriting (pre-consolidation historical values are not reusable — over-sampling configs changed since:3b4156e,602ffce).- Measure the Bilinear classes in the existing autolens_profiling cells (
scripts/imaging/likelihood_runtime/pixelization_numba.pyetc.), record a versioned result, and update autolens_profiling#153 / PROGRAMME Phase 14 with the decision.
Key Files
autoarray/inversion/mesh/interpolator/rectangular.py— kernel-CDFcreate_transformstoday; gains the resurrected rank-CDF variant + selection seam.autoarray/inversion/mesh/mesh/rectangular_adapt_density.py/rectangular_adapt_image.py— current kernel-CDF mesh classes → renamed RTU classes; templates for the new Bilinear classes.autoarray/inversion/mesh/mesh/__init__.py,autoarray/__init__.py— exports.test_autoarray/inversion/pixelization/interpolator/test_rectangular.py,test_autoarray/inversion/pixelization/mesh/test_rectangular.py— test homes.- Reference for resurrection: PyAutoArray
22b28463^(pre-consolidation tree; deletion commit refactor: consolidate rectangular meshes — kernel takes plain names, retire linear/spline/rotated #402, 2026-07-23).
Gradient evidence (why the split is safe)
Certified in the July gradient audit (autolens_workspace_developer/jax_profiling/gradient/README.md): at os_pix=1 the rank-CDF likelihood is exactly piecewise-constant in mass/shear (zero gradients — the reason it was deleted); at os_pix=4 full FD sweeps validate both adaptive meshes (AdaptImage <=~1% production shape, AdaptDensity <=~3%). The interferometer sparse path has no over-sampling, so Bilinear is permanently gradient-dead there — RTU (or RectangularUniform) must remain the interferometer gradient mesh. Sampler-level mesh-family ranking is already PROGRAMME Phase 5 — not duplicated here.
Original Prompt
Click to expand starting prompt
Rectangular mesh split: Bilinear (fast CPU default) vs RTU (advanced/GPU)
Type: feature
Target: autoarray
Repos:
- @PyAutoArray
- @autolens_workspace
- @autogalaxy_workspace
- @autolens_workspace_test
Difficulty: medium
Autonomy: supervised
Priority: high
Original request (verbatim, 2026-08-21)
Ok, I think we want RectangularBilinearAdaptDensity (workspace default)
RectangularBilinearAdaptImage, RectangularRTUAdaptDensity and
RectangularRTUAdaptImage. Confirm RectangularBilinearAdaptDensity is CPU fast
and gradient robust. Thsi will prob means we need to again updated all _test
workspace likelihood values in certain scripts, see if history has them but
over write if not (and keep RTU files somewhere)? make prompt to hand off to
mobile
Preceding context (same conversation): the RTU kernel-CDF transform dominates
the numba CPU likelihood (55% of a euclid eval, 89% at hst; O(M_sub x N_data)
erf sum), making the workspace-default rectangular mesh slow on CPU and on the
interferometer path. Proposal: a simpler/faster rectangular mesh becomes the
workspace default; RTU is documented separately as the recommended option for
advanced modeling, especially on GPU. This subsumes option (2) of the Phase 14
default-mesh decision (autolens_profiling PROGRAMME.md Phase 14, issue
autolens_profiling#153) — resolve/update that issue as part of this work.
Grounding (verified 2026-08-21, this session)
- Current classes (
PyAutoArray autoarray/inversion/mesh/mesh/__init__.py):
RectangularAdaptDensity/RectangularAdaptImage(RTU kernel-CDF,
Enzi et al. arXiv:2606.30620) andRectangularUniform(plain uniform
lattice + bilinear, no transform). There is no bareRectangular. - Naming caveat: BOTH the RTU meshes and every historical adaptive variant
use the same 4-pixel bilinear interpolation on the warped lattice
(interpolator/rectangular.py:435-442). What "Bilinear vs RTU" actually
distinguishes is the lattice transform: the resurrect candidate is the
empirical rank-CDF adaptive transform (sort + cumsum), deleted in
PyAutoArray22b28463(refactor: consolidate rectangular meshes — kernel takes plain names, retire linear/spline/rotated #402, 2026-07-23, -3738 lines) when kernel-CDF took
the plain names. Recover its implementation from that commit
(create_transformswithargsort/cumsum) — do NOT reinvent it. - CPU fast: YES. The rank-CDF transform is O(N log N) sort/cumsum and
eliminates the erf sum that is 55-89% of the numba CPU eval (post-perf: windowed numba fast path for the kernel-CDF forward transform #458 the
RTU eval is euclid 1.17 s / hst 10.1 s on a 4-core container). Measure the
new class in the existingautolens_profilingcells
(scripts/imaging/likelihood_runtime/pixelization_numba.pyetc.) and record
a versioned result. - Gradient robust: CONDITIONALLY. Certified evidence from the July
gradient audit (autolens_workspace_developer/jax_profiling/gradient/README.md):- os_pix=1 (the current default): rank-CDF likelihood is exactly
piecewise-constant in mass/shear — gradients exactly zero. Unusable. - over_sample_size_pixelization=4: full FD sweeps validate both adaptive
meshes — AdaptImage production shape <=~1% on mass, AdaptDensity <=~3%.
Acceptable for most JAX gradient samplers. - Interferometer sparse path: no over-sampling exists — the staircase with
no escape hatch. RTU (and RectangularUniform) are the only certified
gradient meshes there.
So docs MUST say: gradient inference on the Bilinear default needs
os_pix>=4 (imaging); interferometer gradient work needs RTU. Sampler-level
mesh-family ranking is already PROGRAMME Phase 5 — do not duplicate it here.
- os_pix=1 (the current default): rank-CDF likelihood is exactly
- Alternative implementation considered: interpolated-kernel-CDF forward
(K=8192 -> dlnL <= +4e-3, 18-55x on the step, measured in feature/triangle array typing #151/feature/revert to float #153 work).
Rejected for the Bilinear pair (it is still RTU with a bandwidth
hyperparameter, defeating "conceptually simple"); it remains feature/revert to float #153's lever
for making RTU itself faster on CPU.
Goal
- PyAutoArray: four adaptive classes —
RectangularBilinearAdaptDensity(resurrected rank-CDF transform +
bilinear) andRectangularBilinearAdaptImage;
RectangularRTUAdaptDensity/RectangularRTUAdaptImage(pure renames of
the current kernel-CDF classes, values unchanged).RectangularUniform
stays as-is (library-test baseline; certified interferometer gradient
mesh). Never delete the RTU implementation. Breaking rename → release
notes need the## API Changesheading. - Workspaces (
autolens_workspace~224 uses,autogalaxy_workspace,
HowToLens ~42): default examples switch to
RectangularBilinearAdaptDensity; RTU documented separately as the
recommended advanced option (GPU / gradient samplers / interferometer),
folding in the queued Enzi-citation docs draft
(draft/docs/workspaces/rectangular_mesh_enzi_citation_examples.md).
Prior configs: addmesh/rectangular_bilinear_adapt_*.yaml, rename the
RTU yamls to match the new class names (autoconf lowercases keys). - _test workspace: likelihood pin scripts
(scripts/{imaging,multi_dataset,interferometer}/jax_likelihood/rectangular*.py
and siblings) — keep RTU pin scripts alive under the renamed classes
(values unchanged by a pure rename), add/switch default-mesh scripts to
Bilinear and regenerate pins, overwriting. History does hold
pre-consolidation (empirical-CDF era) values but under older paths/configs
(over-sampling changed since:3b4156e,602ffce) — not reusable. - Update autolens_profiling#153 / PROGRAMME Phase 14 with the decision.
Library first, workspace follow-up once the API lands (standard both-routing).
Activity
Jammy2211 commentedon Aug 21, 2026
Library phase implemented and shipped as PR #462 (
feature/rectangular-bilinear-rtu-mesh-split,pending-release).create_transforms_rankrecovered verbatim from22b28463^with atransform="rank"|"kernel"seam through the interpolator and mesh geometry (areas/edges route through the same transform as the mapper).RectangularBilinearAdaptDensity(shape)/RectangularBilinearAdaptImage(shape, weight_power, weight_floor)added — no kernel hyperparameters in their signatures.RectangularRTUAdaptDensity/RectangularRTUAdaptImageare pure renames;RectangularUniformuntouched.test_transformer.pypynufft failures remain (reproduced identically on pristinemain).Remaining (workspace follow-up after #462 merges): workspace/HowToLens default switch + RTU advanced docs (folding the Enzi-citation draft), prior-config yamls,
_testpin regeneration (Bilinear overwrite, RTU renames), profiling measurement + autolens_profiling#153 / PROGRAMME Phase 14 update.Note: this issue's body was briefly overwritten by a tooling slip and immediately restored — the current body is the original; the intermediate revision in the edit history can be ignored.
Generated by Claude Code
Jammy2211 commentedon Aug 21, 2026
Full fan-out complete — library merged, all follow-up PRs open (
pending-release):RectangularBilinear*, interferometer →RectangularRTU*, mesh-variants docs with the Enzi et al. 2026 citation (folds the queued Mind Enzi-citation draft), prior-config yamls split.rectangular.<Class>prior-config keys that verifiably never resolved.JAX_ENABLE_X64=1and re-verified green (imaging-651692.997799, imaging-mge-85.41696632, imaging-dspl-3695.93899659with the bandwidth kwarg dropped, multi-12932.06852498, multi-mge-6157.55707862); RTU coverage survives asrectangular*_rtu.pypure-rename copies with unchanged pins (spot-verified), interferometer scripts pass unchanged incl. the NUFFT cross-check.One environment note for future pin work: the
_testvmap pins requireJAX_ENABLE_X64=1— a bare container's float32 vmap returns-inffor these scripts even on unmodifiedmain(pre-existing, unrelated to this change).Generated by Claude Code
Jammy2211 commentedon Aug 21, 2026
Complete — all seven PRs merged 2026-08-21 (human-authorized):
--rect-mesh {bilinear,rtu}— both meshes explicit in the profiling cells).32a729e+ DECISIONS.md entry); the versioned Bilinear-vs-RTU CPU measurement remains tracked on autolens_profiling#153.complete/2026/08/rectangular-bilinear-rtu-mesh-split.md; the folded Enzi docs draft retired (on session branchclaude/bilinear-rtu-mesh-docs-pbhsxg).Generated by Claude Code