You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PointSolver.solve crashes on single-image sources and on loose pixel_scale_precision #531
Two crashes in PointSolver.solve that look like missing edge-case handling.
Both reproduce in a fresh venv with autolens == 2026.5.21.1, Python 3.12.
1. Source outside the caustic → single image → numpy.AxisError
importautolensasalgrid=al.Grid2D.uniform(shape_native=(80, 80), pixel_scales=0.05)
lens=al.Galaxy(
redshift=0.5,
mass=al.mp.Isothermal(centre=(0, 0), ell_comps=(0.1, 0), einstein_radius=1.0),
)
src_pos= (5.0, 5.0) # comfortably outside the causticsrc=al.Galaxy(redshift=1.0, point_0=al.ps.Point(centre=src_pos))
tracer=al.Tracer(galaxies=[lens, src])
solver=al.PointSolver.for_grid(grid=grid, pixel_scale_precision=0.001)
solver.solve(tracer=tracer, source_plane_coordinate=src_pos)
# numpy.exceptions.AxisError: axis 1 is out of bounds for array of dimension 1
A source far from the lens should produce a single image (approximately at the
source position). Instead the solver bails out with a NumPy axis error from
deep inside the iterative refinement.
2. Loose pixel_scale_precision → IndexError: list index out of range
src_pos= (0.05, 0.02) # well inside the causticsrc=al.Galaxy(redshift=1.0, point_0=al.ps.Point(centre=src_pos))
tracer=al.Tracer(galaxies=[lens, src])
solver=al.PointSolver.for_grid(grid=grid, pixel_scale_precision=0.1)
solver.solve(tracer=tracer, source_plane_coordinate=src_pos)
# IndexError: list index out of range
Same input as a working pixel_scale_precision=0.001 call (which returns
four images), only the precision was loosened. The solver doesn't gracefully
degrade — it indexes an empty candidate list.
Why it matters
Both inputs are realistic: surveys with many simulated lenses commonly have
configurations outside the caustic (especially during model exploration), and
"loose precision for a quick first pass" is exactly how one would script the
solver. A clear error ("source outside the caustic; only one image", or
"pixel_scale_precision is too large to resolve any image") would be
massively better than a raw AxisError / IndexError.
Thanks @rhayes777, and sorry this went unanswered for so long.
Both crashes reproduce on current main (autolens @ 6567b3b1c), unchanged:
Source outside the caustic — src_pos = (5.0, 5.0), pixel_scale_precision=0.001: numpy.exceptions.AxisError: axis 1 is out of bounds for array of dimension 1.
Loose precision — src_pos = (0.05, 0.02) inside the caustic: pixel_scale_precision=0.001 returns 4 images as expected, and simply loosening it to 0.1 gives IndexError: list index out of range. Same tracer, same source, only the precision changed.
Your point about why it matters is the part that lands hardest for us. Both of these are things a user does on purpose — configurations outside the caustic show up constantly during model exploration, and "loosen the precision for a quick first pass" is exactly how someone would script the solver. Getting a raw AxisError from inside the iterative refinement gives no hint that the input was the problem, let alone which input.
So we're treating these as genuine bugs rather than validation niceties: the single-image case should return the one image it finds, and the too-loose-precision case should raise a clear error naming pixel_scale_precision and saying it can't resolve any image at that scale. Degrading gracefully where we can, failing legibly where we can't.
This is being tracked with the rest of your audit (PyAutoArray#332, PyAutoArray#333, PyAutoGalaxy#440, PyAutoLens#532) as a single campaign — these two sit in the crash-bug bucket alongside the ConstantSplit failure in #332, which is where we'll start. I'll link the tracking issue here shortly and keep you posted as it lands.
As promised above — the tracking issue for this campaign is PyAutoLabs/PyAutoArray#415, which carries the verification evidence for all 16
findings and the phase breakdown.
The findings from this issue are in phase 1 (PyAutoLabs/PyAutoArray#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 ConstantSplit failure I
found it's wider than reported. InterpolatorRectangularUniform is missing _mappings_sizes_weights_split entirely (InterpolatorRectangular and InterpolatorDelaunay both have it, and the abstract base doesn't supply it) — and three regularizations consume that property, not one:
Two things worth reporting back, since both change what you'd expect to see.
Your ConstantSplit finding was one of nine.InterpolatorRectangular (used by RectangularAdaptDensity and RectangularAdaptImage) does define _mappings_sizes_weights_split, but as a pass-through commented "split regularization reuses the
same mappings". It doesn't — reg_split_from expects the split-cross structure, so those two
meshes failed with IndexError rather than your AttributeError. Three meshes × three split
schemes, two failure modes. All nine now raise one clear error at Pixelization construction
naming 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 AxisError was 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, FitPositionsImagePairAll computed -log(0) against an infinite sum and produced a NaN
chi-squared. fitness.py converts NaN likelihoods into resample-rejects, so a search would have
silently discarded those models instead of scoring them. Its two sibling fit classes already had a
finite no_image_residual floor for exactly this; PairAll was the one missing it. Fixed in the
same PR.
Still to come: the constructor-validation sweep (#333, PyAutoGalaxy#440, #532's B4), the adapt_images error-legibility fix, and the z_lens > z_source question I asked you on #532 —
that one's genuinely open, no rush.
Source outside the tiled region — no longer raises AxisError. Returns an empty grid and
logs why. (As noted above, the tiling genuinely finds zero images for your (5,5) case rather
than the single image you predicted, so returning nothing clearly is the honest fix.)
Loose pixel_scale_precision — no longer raises IndexError. Raises a clear ValueError
naming pixel_scale_precision, reporting the triangle scale, and suggesting a workable value.
The root cause was an off-by-one: n_steps goes negative when precision exceeds the triangle
scale, and the existing guard only tested == 0.
Chasing the first one also turned up a defect you didn't hit: with zero images FitPositionsImagePairAll produced a NaN chi-squared, which the sampler machinery converts
into a silent resample-reject. Its two sibling fit classes already had a finite no_image_residual
floor for exactly this case; it now matches them.
Closing this one. Your other three issues (PyAutoArray#332, PyAutoArray#333, PyAutoGalaxy#440,
PyAutoLens#532) remain open and tracked in PyAutoArray#415 — the constructor-validation sweep and
the adapt_images error-legibility fix are next. Thanks again for the report, and sorry once more
that it sat as long as it did.
Activity
Jammy2211 commentedon Jul 28, 2026
Thanks @rhayes777, and sorry this went unanswered for so long.
Both crashes reproduce on current
main(autolens@6567b3b1c), unchanged:src_pos = (5.0, 5.0),pixel_scale_precision=0.001:numpy.exceptions.AxisError: axis 1 is out of bounds for array of dimension 1.src_pos = (0.05, 0.02)inside the caustic:pixel_scale_precision=0.001returns 4 images as expected, and simply loosening it to0.1givesIndexError: list index out of range. Same tracer, same source, only the precision changed.Your point about why it matters is the part that lands hardest for us. Both of these are things a user does on purpose — configurations outside the caustic show up constantly during model exploration, and "loosen the precision for a quick first pass" is exactly how someone would script the solver. Getting a raw
AxisErrorfrom inside the iterative refinement gives no hint that the input was the problem, let alone which input.So we're treating these as genuine bugs rather than validation niceties: the single-image case should return the one image it finds, and the too-loose-precision case should raise a clear error naming
pixel_scale_precisionand saying it can't resolve any image at that scale. Degrading gracefully where we can, failing legibly where we can't.This is being tracked with the rest of your audit (PyAutoArray#332, PyAutoArray#333, PyAutoGalaxy#440, PyAutoLens#532) as a single campaign — these two sit in the crash-bug bucket alongside the
ConstantSplitfailure in #332, which is where we'll start. I'll link the tracking issue here shortly and keep you posted as it lands.Jammy2211 commentedon Jul 28, 2026
As promised above — the tracking issue for this campaign is
PyAutoLabs/PyAutoArray#415, which carries the verification evidence for all 16
findings and the phase breakdown.
The findings from this issue are in phase 1 (PyAutoLabs/PyAutoArray#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
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: PointSolver zero-image and coarse-precision crashes, plus two co…
Jammy2211 commentedon Jul 28, 2026
Both crashes from this report are fixed and merged in #662
(with PyAutoLabs/PyAutoArray#417).
AxisError. Returns an empty grid andlogs why. (As noted above, the tiling genuinely finds zero images for your (5,5) case rather
than the single image you predicted, so returning nothing clearly is the honest fix.)
pixel_scale_precision— no longer raisesIndexError. Raises a clearValueErrornaming
pixel_scale_precision, reporting the triangle scale, and suggesting a workable value.The root cause was an off-by-one:
n_stepsgoes negative when precision exceeds the trianglescale, and the existing guard only tested
== 0.Chasing the first one also turned up a defect you didn't hit: with zero images
FitPositionsImagePairAllproduced a NaN chi-squared, which the sampler machinery convertsinto a silent resample-reject. Its two sibling fit classes already had a finite
no_image_residualfloor for exactly this case; it now matches them.
Closing this one. Your other three issues (PyAutoArray#332, PyAutoArray#333, PyAutoGalaxy#440,
PyAutoLens#532) remain open and tracked in PyAutoArray#415 — the constructor-validation sweep and
the
adapt_imageserror-legibility fix are next. Thanks again for the report, and sorry once morethat it sat as long as it did.