Overview
Under PYAUTO_SMALL_DATASETS=1, cap_array_2d_for_small_datasets handles one case and silently drops the other: data larger than the 16x16 cap is cropped and rebuilt at SMALL_DATASETS_PIXEL_SCALES (0.6), but data already at-or-below the cap early-returns, keeping the caller's uncapped pixel_scales (0.1). A capped simulator writes its data at 0.6, so the loader mislabels the frame 6x — +/-0.8" instead of +/-4.8".
Off-centre galaxies then fall outside the mislabelled frame, their non-negative linear intensity solve correctly returns exactly 0.0, total_luminosity becomes 0, and min(5 * 0.5 * 0**0.6, 5.0) collapses a UniformPrior to lower == upper == 0.0. The resulting PriorException surfaces four steps downstream, in autolens_workspace/scripts/group/slam.py, naming neither the loader nor the pixel scale.
Reproduced on clean main in PyAutoHeart Workspace Smoke run 30790463134 (group/slam.py and group/slam.ipynb). This is the root cause; the fault is in PyAutoArray, not in any workspace script.
Plan
- Make the at-or-below-cap branch rebuild the array at the capped pixel scale when the cap is active, mirroring what the crop branch already does.
- Correct the docstring, which currently documents that branch as a deliberate no-op.
- Replace the two unit tests that assert the buggy behaviour as intended, and add one covering the rebuild.
- Validate end-to-end against
group/slam.py, plus the four scripts whose committed 15x15 datasets change scale, plus one crop-path script.
- Report whether the two sibling
scaling_relation/slam NEEDS_FIX parks now clear.
Detailed implementation plan
Affected Repositories
- PyAutoArray (primary, and the only repo edited)
Not edited: autolens_workspace and HowToLens appear in the originating prompt's Repos: header, but both legs are already done — the group/slam no_run line was removed by autolens_workspace PR #312 (a9b7ac1a), and HowToLens has no group/ scripts and no matching no_run entry. autolens_workspace is currently claimed by another task and is used here for validation runs only (writes land in gitignored dataset/ and output/).
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoArray |
main |
clean |
No task in active.md claims PyAutoArray — no conflict.
Suggested branch: feature/small-datasets-loader-pixel-scales
Implementation Steps
-
autoarray/util/dataset_util.py, in cap_array_2d_for_small_datasets: replace the if h <= cap_h and w <= cap_w: return array_2d, pixel_scales early return (lines 41-42) with a rebuild returning Array2D.no_mask(values=array_2d.native.array, pixel_scales=SMALL_DATASETS_PIXEL_SCALES) and SMALL_DATASETS_PIXEL_SCALES. Shape is preserved — this branch must NOT crop.
Returning a corrected scalar alone is not enough: the Array2D is constructed before the call and carries its own geometry. A first prototype that only fixed the scalar still failed.
The PYAUTO_SMALL_DATASETS != "1" guard above is untouched, so non-smoke behaviour is unchanged.
-
Same file, docstring lines 15-18: currently promises "Returns (array_2d, pixel_scales) unchanged ... array_2d.shape_native is already at-or-below the cap (16, 16)". Rewrite to state that under an active cap, at-or-below-cap data is relabelled to 0.6, because such data was written by a capped simulator at that scale.
-
test_autoarray/util/test_dataset_util.py: test__env_set__shape_already_at_cap__returns_inputs_unchanged and test__env_set__shape_below_cap__returns_inputs_unchanged both assert result is array and pixel_scales == 0.08 — they encode the bug as intended behaviour. Rewrite both to assert the rebuild: result is not array, pixel_scales == SMALL_DATASETS_PIXEL_SCALES, shape preserved (not cropped), pixel values preserved.
Leave test__env_unset__returns_inputs_unchanged and both crop-path tests as-is — they are the guard that this fix stays scoped to the cap-active path.
Validation
pytest test_autoarray/.
scripts/group/slam.py under the standard capped smoke env (PYAUTO_TEST_MODE=2, PYAUTO_SMALL_DATASETS=1, etc.), with dataset/ and output/ cleared, expecting exit 0 and all six SLaM searches running. Already confirmed green against a monkeypatched loader; re-run against the real edit.
- The four scripts with committed 15x15 datasets that will now receive 0.6 instead of 0.1:
double_einstein_ring, mass_stellar_dark, scaling_relation, extra_and_scaling_galaxies. This is the declared behaviour change and the main risk.
- One crop-path script, to confirm that branch is untouched.
- Then check whether
imaging/features/scaling_relation/slam and multi_galaxy/features/scaling_relation/slam now pass — scaling_relation is among the four affected datasets, so their 0.0-luminosity NEEDS_FIX park may clear. Un-parking them is a follow-up PR in autolens_workspace, not part of this one.
Explicitly out of scope
Flooring or guarding the collapsed prior, in group/slam.py or anywhere else. An off-frame profile solving to exactly zero intensity is the correct result of a non-negative solve. (A 2026-05 clamp on two sibling group SLaM scripts treated this symptom; that is not the pattern to extend.)
Known residual risk
The shape <= cap implies 0.6 inference is an inference, not a measurement. Every committed and generated imaging dataset checked is either cropped-and-relabelled to 0.6 or written by a capped run at 0.6, but a genuinely small real-scale dataset added later would be mislabelled. Follow-up prompt on simulators recording their own scale to be filed separately.
Key Files
autoarray/util/dataset_util.py — cap_array_2d_for_small_datasets, the fix and its docstring
test_autoarray/util/test_dataset_util.py — the two tests asserting the bug, plus the new rebuild test
Original Prompt
Click to expand starting prompt
PYAUTO_SMALL_DATASETS loader keeps uncapped pixel_scales for at-or-below-cap data
Type: bug
Target: PyAutoArray
Repos:
- PyAutoArray
- autolens_workspace
- HowToLens
Difficulty: small
Autonomy: supervised
Priority: normal
Status: formalised
PYAUTO_SMALL_DATASETS loader keeps uncapped pixel_scales for at-or-below-cap data.
Root cause of the group/slam PriorException (supersedes draft/bug/autolens/group_slam_priorexception_limits.md). The fault is in PyAutoArray, not in any workspace script.
autoarray/util/dataset_util.py cap_array_2d_for_small_datasets handles one case and silently drops the other:
- data LARGER than the 16x16 cap -> crops it AND rebuilds it at SMALL_DATASETS_PIXEL_SCALES (0.6). Correct.
- data already AT-OR-BELOW the cap (because a capped simulator just wrote it at 0.6) -> early-returns, keeping the callers uncapped pixel_scales (0.1). Wrong.
Consequence: the frame is mislabelled 6x (plus/minus 0.8 arcsec instead of plus/minus 4.8 arcsec). Off-centre galaxies fall outside it, their non-negative linear intensity solve correctly returns exactly 0.0, total_luminosity becomes 0, and min(50.50**0.6, 5.0) collapses a UniformPrior to lower==upper==0.0. The PriorException is four steps downstream of the fault.
FIX (about 4 lines): in the at-or-below-cap branch, when the cap is active, rebuild the Array2D at SMALL_DATASETS_PIXEL_SCALES and return that scale, mirroring what the crop branch already does. Returning a corrected scalar alone is NOT enough: the Array2D is constructed before the call and carries its own geometry. A first prototype that only fixed the scalar still failed. Also update the docstring, which currently documents only the crop case and calls the early return a no-op.
NO workspace script changes are needed. pixel_scales=0.1 is a TRUE statement about the dataset in normal operation and belongs in a tutorial script; the cap silently invalidates it and the loader must correct it.
PROVEN on clean main, standard capped smoke env (PYAUTO_TEST_MODE=2, PYAUTO_SMALL_DATASETS=1), fresh dataset:
- unmodified scripts/group/slam.py + patched loader -> EXIT 0, all six searches ran
- unmodified script, unpatched loader -> PriorException at slam.py:307
- script with pixel_scale hardcoded to 0.6, unpatched loader -> EXIT 0 (confirms the scale, not the science, was the problem)
Evidence the capped data really is 0.6 arcsec/px: the simulated 16x16 image has clumps at (+3.6,+2.4) and (-4.8,-4.8) arcsec under 0.6, matching the declared extra-galaxy centres (3.5,2.5) and (-4.4,-5.0). Under 0.1 they would be at (0.55,0.45) - nowhere near.
Safety of the >=cap-implies-0.6 inference: every committed and generated imaging dataset checked is either cropped-and-relabelled to 0.6 or written by a capped run at 0.6. It is still an inference; a genuinely small real-scale dataset added later would be mislabelled. See the follow-up prompt on simulators recording their own scale.
BEHAVIOUR CHANGE TO VALIDATE: four datasets have committed 15x15 files (double_einstein_ring, mass_stellar_dark, scaling_relation, extra_and_scaling_galaxies). Scripts loading them will now receive 0.6 instead of 0.1. That is a correction (the data genuinely is 0.6) but must be spot-checked.
ALSO IN SCOPE: remove the group/slam NEEDS_FIX line from autolens_workspace/config/build/no_run.yaml, and the dead one from HowToLens/config/build/no_run.yaml (HowToLens has no group/ scripts at all).
VALIDATION PLAN: pytest test_autoarray/ (add an at-or-below-cap test case); clean-dataset and clean-output run of scripts/group/slam.py under the standard capped smoke env expecting exit 0; the four committed-15x15 scripts; one crop-path script to confirm that branch is untouched.
DEAD ENDS ALREADY RULED OUT, do not redo: (1) the 132-vs-35 should_simulate split is NOT drift - commit 0f294fc70 scoped that migration to smoke-tested scripts deliberately, and since dataset/ is gitignored, exists() and should_simulate are equivalent on a clean CI checkout. (2) An FOV-preserving rewrite of the cap is NOT needed for this bug. (3) Flooring or guarding the collapsed prior is wrong - an off-frame profile solving to exactly zero intensity is the correct result of a non-negative solve.
Overview
Under
PYAUTO_SMALL_DATASETS=1,cap_array_2d_for_small_datasetshandles one case and silently drops the other: data larger than the 16x16 cap is cropped and rebuilt atSMALL_DATASETS_PIXEL_SCALES(0.6), but data already at-or-below the cap early-returns, keeping the caller's uncappedpixel_scales(0.1). A capped simulator writes its data at 0.6, so the loader mislabels the frame 6x — +/-0.8" instead of +/-4.8".Off-centre galaxies then fall outside the mislabelled frame, their non-negative linear intensity solve correctly returns exactly 0.0,
total_luminositybecomes 0, andmin(5 * 0.5 * 0**0.6, 5.0)collapses aUniformPriortolower == upper == 0.0. The resultingPriorExceptionsurfaces four steps downstream, inautolens_workspace/scripts/group/slam.py, naming neither the loader nor the pixel scale.Reproduced on clean
mainin PyAutoHeart Workspace Smoke run 30790463134 (group/slam.pyandgroup/slam.ipynb). This is the root cause; the fault is in PyAutoArray, not in any workspace script.Plan
group/slam.py, plus the four scripts whose committed 15x15 datasets change scale, plus one crop-path script.scaling_relation/slamNEEDS_FIX parks now clear.Detailed implementation plan
Affected Repositories
Not edited:
autolens_workspaceandHowToLensappear in the originating prompt'sRepos:header, but both legs are already done — thegroup/slamno_run line was removed by autolens_workspace PR #312 (a9b7ac1a), and HowToLens has nogroup/scripts and no matching no_run entry.autolens_workspaceis currently claimed by another task and is used here for validation runs only (writes land in gitignoreddataset/andoutput/).Branch Survey
No task in
active.mdclaims PyAutoArray — no conflict.Suggested branch:
feature/small-datasets-loader-pixel-scalesImplementation Steps
autoarray/util/dataset_util.py, incap_array_2d_for_small_datasets: replace theif h <= cap_h and w <= cap_w: return array_2d, pixel_scalesearly return (lines 41-42) with a rebuild returningArray2D.no_mask(values=array_2d.native.array, pixel_scales=SMALL_DATASETS_PIXEL_SCALES)andSMALL_DATASETS_PIXEL_SCALES. Shape is preserved — this branch must NOT crop.Returning a corrected scalar alone is not enough: the
Array2Dis constructed before the call and carries its own geometry. A first prototype that only fixed the scalar still failed.The
PYAUTO_SMALL_DATASETS != "1"guard above is untouched, so non-smoke behaviour is unchanged.Same file, docstring lines 15-18: currently promises "Returns
(array_2d, pixel_scales)unchanged ...array_2d.shape_nativeis already at-or-below the cap (16, 16)". Rewrite to state that under an active cap, at-or-below-cap data is relabelled to 0.6, because such data was written by a capped simulator at that scale.test_autoarray/util/test_dataset_util.py:test__env_set__shape_already_at_cap__returns_inputs_unchangedandtest__env_set__shape_below_cap__returns_inputs_unchangedboth assertresult is arrayandpixel_scales == 0.08— they encode the bug as intended behaviour. Rewrite both to assert the rebuild:result is not array,pixel_scales == SMALL_DATASETS_PIXEL_SCALES, shape preserved (not cropped), pixel values preserved.Leave
test__env_unset__returns_inputs_unchangedand both crop-path tests as-is — they are the guard that this fix stays scoped to the cap-active path.Validation
pytest test_autoarray/.scripts/group/slam.pyunder the standard capped smoke env (PYAUTO_TEST_MODE=2,PYAUTO_SMALL_DATASETS=1, etc.), withdataset/andoutput/cleared, expecting exit 0 and all six SLaM searches running. Already confirmed green against a monkeypatched loader; re-run against the real edit.double_einstein_ring,mass_stellar_dark,scaling_relation,extra_and_scaling_galaxies. This is the declared behaviour change and the main risk.imaging/features/scaling_relation/slamandmulti_galaxy/features/scaling_relation/slamnow pass —scaling_relationis among the four affected datasets, so their 0.0-luminosity NEEDS_FIX park may clear. Un-parking them is a follow-up PR inautolens_workspace, not part of this one.Explicitly out of scope
Flooring or guarding the collapsed prior, in
group/slam.pyor anywhere else. An off-frame profile solving to exactly zero intensity is the correct result of a non-negative solve. (A 2026-05 clamp on two sibling group SLaM scripts treated this symptom; that is not the pattern to extend.)Known residual risk
The
shape <= cap implies 0.6inference is an inference, not a measurement. Every committed and generated imaging dataset checked is either cropped-and-relabelled to 0.6 or written by a capped run at 0.6, but a genuinely small real-scale dataset added later would be mislabelled. Follow-up prompt on simulators recording their own scale to be filed separately.Key Files
autoarray/util/dataset_util.py—cap_array_2d_for_small_datasets, the fix and its docstringtest_autoarray/util/test_dataset_util.py— the two tests asserting the bug, plus the new rebuild testOriginal Prompt
Click to expand starting prompt
PYAUTO_SMALL_DATASETS loader keeps uncapped pixel_scales for at-or-below-cap data
Type: bug
Target: PyAutoArray
Repos:
Difficulty: small
Autonomy: supervised
Priority: normal
Status: formalised
PYAUTO_SMALL_DATASETS loader keeps uncapped pixel_scales for at-or-below-cap data.
Root cause of the group/slam PriorException (supersedes draft/bug/autolens/group_slam_priorexception_limits.md). The fault is in PyAutoArray, not in any workspace script.
autoarray/util/dataset_util.py cap_array_2d_for_small_datasets handles one case and silently drops the other:
Consequence: the frame is mislabelled 6x (plus/minus 0.8 arcsec instead of plus/minus 4.8 arcsec). Off-centre galaxies fall outside it, their non-negative linear intensity solve correctly returns exactly 0.0, total_luminosity becomes 0, and min(50.50**0.6, 5.0) collapses a UniformPrior to lower==upper==0.0. The PriorException is four steps downstream of the fault.
FIX (about 4 lines): in the at-or-below-cap branch, when the cap is active, rebuild the Array2D at SMALL_DATASETS_PIXEL_SCALES and return that scale, mirroring what the crop branch already does. Returning a corrected scalar alone is NOT enough: the Array2D is constructed before the call and carries its own geometry. A first prototype that only fixed the scalar still failed. Also update the docstring, which currently documents only the crop case and calls the early return a no-op.
NO workspace script changes are needed. pixel_scales=0.1 is a TRUE statement about the dataset in normal operation and belongs in a tutorial script; the cap silently invalidates it and the loader must correct it.
PROVEN on clean main, standard capped smoke env (PYAUTO_TEST_MODE=2, PYAUTO_SMALL_DATASETS=1), fresh dataset:
Evidence the capped data really is 0.6 arcsec/px: the simulated 16x16 image has clumps at (+3.6,+2.4) and (-4.8,-4.8) arcsec under 0.6, matching the declared extra-galaxy centres (3.5,2.5) and (-4.4,-5.0). Under 0.1 they would be at (0.55,0.45) - nowhere near.
Safety of the >=cap-implies-0.6 inference: every committed and generated imaging dataset checked is either cropped-and-relabelled to 0.6 or written by a capped run at 0.6. It is still an inference; a genuinely small real-scale dataset added later would be mislabelled. See the follow-up prompt on simulators recording their own scale.
BEHAVIOUR CHANGE TO VALIDATE: four datasets have committed 15x15 files (double_einstein_ring, mass_stellar_dark, scaling_relation, extra_and_scaling_galaxies). Scripts loading them will now receive 0.6 instead of 0.1. That is a correction (the data genuinely is 0.6) but must be spot-checked.
ALSO IN SCOPE: remove the group/slam NEEDS_FIX line from autolens_workspace/config/build/no_run.yaml, and the dead one from HowToLens/config/build/no_run.yaml (HowToLens has no group/ scripts at all).
VALIDATION PLAN: pytest test_autoarray/ (add an at-or-below-cap test case); clean-dataset and clean-output run of scripts/group/slam.py under the standard capped smoke env expecting exit 0; the four committed-15x15 scripts; one crop-path script to confirm that branch is untouched.
DEAD ENDS ALREADY RULED OUT, do not redo: (1) the 132-vs-35 should_simulate split is NOT drift - commit 0f294fc70 scoped that migration to smoke-tested scripts deliberately, and since dataset/ is gitignored, exists() and should_simulate are equivalent on a clean CI checkout. (2) An FOV-preserving rewrite of the cap is NOT needed for this bug. (3) Flooring or guarding the collapsed prior is wrong - an off-frame profile solving to exactly zero intensity is the correct result of a non-negative solve.