Repository navigation
Description
Title: Adaptive pixelisations (Delaunay, KNNBarycentric) crash in FitImaging; ConstantSplit regularization broken on RectangularUniform
Repo: Jammy2211/PyAutoArray
Tag: @Jammy2211
While auditing the released 2026.5.21.1 libraries against the documented public
API, I found two related defects in the inversion path. Both are reproducible with
the snippets below in a fresh uv venv (uv pip install autolens, Python 3.12.8).
1. Delaunay and KNNBarycentric raise AttributeError inside FitImaging
import numpy as np
import autolens as al
psf = al.Convolver.from_gaussian(shape_native=(11, 11), sigma=0.1,
pixel_scales=0.05, normalize=True)
grid = al.Grid2D.uniform(shape_native=(50, 50), pixel_scales=0.05, over_sample_size=2)
tracer_true = al.Tracer(galaxies=[
al.Galaxy(redshift=0.5,
mass=al.mp.Isothermal(centre=(0, 0), ell_comps=(0.1, 0.0),
einstein_radius=1.2)),
al.Galaxy(redshift=1.0,
bulge=al.lp.Sersic(centre=(0.05, 0), intensity=0.3,
effective_radius=0.1, sersic_index=2)),
])
sim = al.SimulatorImaging(exposure_time=300, background_sky_level=0.1, psf=psf,
add_poisson_noise_to_data=True, noise_seed=1)
dataset = sim.via_tracer_from(tracer=tracer_true, grid=grid)
mask = al.Mask2D.circular(shape_native=dataset.shape_native,
pixel_scales=dataset.pixel_scales, radius=2.0)
dataset = dataset.apply_mask(mask=mask)
lens = al.Galaxy(redshift=0.5,
mass=al.mp.Isothermal(centre=(0, 0), ell_comps=(0.1, 0),
einstein_radius=1.2))
pix = al.Pixelization(
mesh=al.mesh.Delaunay(pixels=100), # or KNNBarycentric(pixels=100)
regularization=al.reg.Constant(coefficient=1.0),
)
source = al.Galaxy(redshift=1.0, pixelization=pix)
tracer = al.Tracer(galaxies=[lens, source])
fit = al.FitImaging(dataset=dataset, tracer=tracer)
fit.log_evidence # AttributeErrorFile "autoarray/inversion/mesh/border_relocator.py", line 446, in relocated_mesh_grid_from
grid.array[self.border_slim], xp=xp
AttributeError: 'NoneType' object has no attribute 'array'
The Optional[BorderRelocator] guard at mesh/abstract.py:90 is in place, but the
grid argument passed into BorderRelocator.relocated_mesh_grid_from() is
None, so the failure happens one level deeper at
border_relocator.py:446 (grid.array[self.border_slim]).
Effect: in the released wheel Delaunay and KNNBarycentric are unusable in
FitImaging. RectangularUniform works fine.
2. ConstantSplit regularization is broken on RectangularUniform
Same simulation as above, but swap the regularization:
pix = al.Pixelization(
mesh=al.mesh.RectangularUniform(shape=(15, 15)),
regularization=al.reg.ConstantSplit(coefficient=1.0),
)AttributeError: 'InterpolatorRectangularUniform' object has no attribute
'_mappings_sizes_weights_split'
Constant works on the same mesh; only the Split variant is broken.
Environment
autolens / autogalaxy / autoarray / autofit / autoconf == 2026.5.21.1- Python 3.12.8, macOS arm64, fresh
uvvenv.
What I tried next
-
Read the upstream
Jammy2211/PyAutoArray:autoarray/inversion/mesh/mesh/abstract.py
onmain; theif border_relocator is not Noneguard is identical to the
installed file. The issue is that callers in autogalaxy / autolens now invoke
the new mesh API (without the oldimage_mesh=kwarg) without setting up a
source_plane_data_grid, leaving itNone. -
The new
Delaunay(pixels=N)/KNNBarycentric(pixels=N)constructors no
longer take an explicit image-plane mesh. If that's intentional, the wiring
to set up the data grid / border relocator probably needs to move inside the
mesh classes themselves.
Happy to dig further if useful. Full reproducer scripts live at
work/edge_case_tests/06_inversion.py in the audit workspace.
Activity
Jammy2211 commentedon Jul 28, 2026
Thanks @rhayes777, and sorry for the delay on this one. I've re-run both parts against current
main(autoarray@616e8b4c,autolens@6567b3b1c). Here's what I found.Part 2 is confirmed straight out.
ConstantSplitonRectangularUniformraises:while the same regularization works fine on
Delaunay(log_evidence 5096.44on your simulation). That's a real bug with no workaround, and we'll fix it.Part 1 is subtler than it first looked.
DelaunayandKNNBarycentricaren't broken in the released wheel — they need an image-plane mesh grid supplied viaadapt_images, and with it both fit cleanly on your exact simulation:The idiom is:
as used in
autolens_workspace/scripts/imaging/features/pixelization/delaunay.py. Your snippet omitted it, which is why the grid arrived at the border relocator asNone.That said, you found a genuine defect and your diagnosis was close to the mark. A missing required precondition should not surface as
AttributeError: 'NoneType' object has no attribute 'array'three frames down inborder_relocator.py:446— that's exactly the head-scratch failure mode you describe in your other reports. And your reading that the wiring belongs inside the mesh classes is the direction we'll take: either the mesh sets the grid up itself, or construction fails immediately with a message that namesadapt_images. Reproducing this took me straight to the right line, so the report did its job even though the headline was broader than the underlying cause.Both items are being tracked alongside the rest of your audit (#333, PyAutoGalaxy#440, PyAutoLens#531, PyAutoLens#532) as a single campaign — the two here are in the "genuine crash / bad error" bucket rather than the constructor-validation sweep. I'll link the tracking issue here shortly.
Jammy2211 commentedon Jul 28, 2026
As promised above — the tracking issue for this campaign is
#415, which carries the verification evidence for all 16
findings and the phase breakdown.
The findings from this issue are in phase 1 (#416), which
is where we're starting — they're the ones with real user impact and no workaround.
One thing worth flagging back to you: while tracing the
ConstantSplitfailure Ifound it's wider than reported.
InterpolatorRectangularUniformis missing_mappings_sizes_weights_splitentirely (InterpolatorRectangularandInterpolatorDelaunayboth have it, and the abstract base doesn't supply it) — andthree regularizations consume that property, not one:
So
AdaptSplitandAdaptSplitZerothfail onRectangularUniformfor the samereason. You found one of three. The fix and its tests cover all of them.
Jammy2211 commentedon Jul 28, 2026
Correction to my note above, and it changes the outcome for you — worth flagging
rather than leaving you to discover it from a changelog.
Rectangular meshes do not support
Splitregularization. That's the designintent, confirmed with @Jammy2211. So the fix here is not to make
ConstantSplitwork onRectangularUniform— it's to raise a clear exceptionsaying the combination is unsupported, pointing you at a mesh that does support it.
Digging into it turned up more than the one case you hit. Nine combinations are
affected, in two different failure modes — three rectangular meshes x three
Splitregularizations:
RectangularUniformAttributeError: ... has no attribute '_mappings_sizes_weights_split'(your case)RectangularAdaptDensityIndexError: index 4 is out of bounds for axis 0 with size 4RectangularAdaptImageIndexError(inherits)The second mode is the one I'd have missed by reading only your report.
InterpolatorRectangulardoes define_mappings_sizes_weights_split, but as apass-through with this comment:
That's aspirational rather than true — it hands 4-corner mappings to
reg_split_from, which expects the split-cross structure, and crashes one framelater. It's going away in favour of the explicit error.
Delaunay+ConstantSplitworks fine (log_evidence 5096.4420), so if you needsplit regularization that's the direction. Tracked in #416.
Jammy2211 commentedon Jul 28, 2026
Phase 1 is up for review — the crash-level findings from your audit.
PointSolvercrashesTwo things worth reporting back, since both change what you'd expect to see.
Your
ConstantSplitfinding was one of nine.InterpolatorRectangular(used byRectangularAdaptDensityandRectangularAdaptImage) does define_mappings_sizes_weights_split, but as a pass-through commented "split regularization reuses thesame mappings". It doesn't —
reg_split_fromexpects the split-cross structure, so those twomeshes failed with
IndexErrorrather than yourAttributeError. Three meshes × three splitschemes, two failure modes. All nine now raise one clear error at
Pixelizationconstructionnaming both sides, and the misleading pass-through is deleted.
Your outside-the-caustic case yields zero images, not one. You predicted a single image near
the source position; the tiling finds none, because the grid spans ±2″ and the source sits at
(5,5). So rather than fabricate an image,
solve()now returns an empty grid and logs why.Your underlying point stands — a raw
AxisErrorwas useless — but the fix is "return nothing,clearly" rather than "return the one image".
Chasing the second one turned up something adjacent that you didn't hit but would have: with zero
images,
FitPositionsImagePairAllcomputed-log(0)against an infinite sum and produced a NaNchi-squared.
fitness.pyconverts NaN likelihoods into resample-rejects, so a search would havesilently discarded those models instead of scoring them. Its two sibling fit classes already had a
finite
no_image_residualfloor for exactly this;PairAllwas the one missing it. Fixed in thesame PR.
Still to come: the constructor-validation sweep (#333, PyAutoGalaxy#440, #532's B4), the
adapt_imageserror-legibility fix, and thez_lens > z_sourcequestion I asked you on #532 —that one's genuinely open, no rush.
fix: reject split regularization on rectangular meshes at construction (
Jammy2211 commentedon Aug 9, 2026
Fixed and merged in #442 (
5dedb5e). Thanks @rhayes777.To restate the correction from our earlier reply, since it is what shaped the fix: the meshes are not broken.
DelaunayandKNNBarycentricwork; they require an image-plane mesh grid, supplied viaadapt_images. Re-verified on the branch — with it supplied,Delaunay + Constant,KNNBarycentric + ConstantandDelaunay + ConstantSplitall fit, andRectangularUniform + Constantfits with noadapt_imagesat all.So the defect was the error, and that is what changed. Instead of
AttributeError: 'NoneType' object has no attribute 'array'from insideborder_relocator.py, you now get aMeshExceptionthat namesadapt_images, shows theAdaptImagesidiom, and points out that the rectangular family needs none. The guard sits atDelaunay.interpolator_from, which the whole adaptive family inherits.On your suggestion that "the wiring probably needs to move inside the mesh classes themselves" — we went the other way, and it is worth saying why rather than just doing it. Building an image-plane mesh grid requires a weighting policy, and that policy is exactly what
adapt_imagescarries. Having the mesh invent one would make a science choice on the user's behalf, silently, in precisely the case where they have given no indication of what they want. Failing fast keeps that choice explicit. It also matches how the rectangular/split combination was handled in #417. Happy to revisit if you think the intended API really is forDelaunay(pixels=N)to self-wire.Small correction to our own notes while we were in there: the
Noneis themesh_gridargument (border_relocator.py:450), not thegridone at line 446.Part 2 of this issue (
ConstantSplitonRectangularUniform) shipped back in #417 and is untouched.The regression tests assert the clear failure plus controls that each adaptive mesh still builds with its grid supplied — so if these meshes ever genuinely do break, those controls fail and the correction above gets revisited rather than quietly becoming wrong.
That closes phases 2 and 3 of the audit. Epic #415 stays open for the one remaining item, the
z_lens > z_sourcequestion on PyAutoLens#532, which is still with you.Generated by Claude Code