Skip to content

Commit 01c687e

Browse files
committed
fix: make the PYAUTO_SMALL_DATASETS fast-mode cap even (15x15 -> 16x16)
The fast-mode cap (`PYAUTO_SMALL_DATASETS=1`) shrank masks/grids/arrays to an odd 15x15. Any feature that requires even mask dimensions then failed under fast-mode — notably PyAutoLens' potential-correction dpsi mesh (`dpsi_factor=2`, `PairRegularDpsiMesh`), which raises `ValueError: both mask dimensions must be divisible by dpsi_factor`. This broke the `interferometer/features/ potential_correction` scripts (start_here.py + likelihood_function.py) in the Heart release-fidelity validation, even though they run fine at full resolution (their real-space mask is 72x72). Cap to an even 16x16 instead across all three cap sites (`mask_2d.circular`, `grid_2d.uniform`, `cap_array_2d_for_small_datasets`) plus the `SMALL_DATASETS_SHAPE_NATIVE` constant, so fast-mode shapes stay compatible with even-divisibility requirements. Fast-mode is a CI-speed convenience only (16x16 vs 15x15 is negligible) and real runs are unaffected. Tests asserting the cap updated to 16x16; the divisibility invariant in the potential-correction mesh is left strict. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013ciVftxvYpefh59wSkR7jN
1 parent cfaab30 commit 01c687e

6 files changed

Lines changed: 133 additions & 132 deletions

File tree

autoarray/mask/mask_2d.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,8 @@ def circular(
361361
"""
362362

363363
if os.environ.get("PYAUTO_SMALL_DATASETS") == "1":
364-
if shape_native[0] > 15 or shape_native[1] > 15:
365-
shape_native = (15, 15)
364+
if shape_native[0] > 16 or shape_native[1] > 16:
365+
shape_native = (16, 16)
366366
pixel_scales = 0.6
367367
scale_scalar = (
368368
pixel_scales

autoarray/structures/grids/uniform_2d.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -491,14 +491,14 @@ def uniform(
491491
origin
492492
The origin of the grid's mask.
493493
respect_small_datasets
494-
When ``PYAUTO_SMALL_DATASETS=1`` is set, grids larger than 15x15 are silently shrunk to
495-
``(15, 15)`` at ``pixel_scales=0.6`` to keep smoke runs fast. Pass ``False`` to opt out
494+
When ``PYAUTO_SMALL_DATASETS=1`` is set, grids larger than 16x16 are silently shrunk to
495+
``(16, 16)`` at ``pixel_scales=0.6`` to keep smoke runs fast. Pass ``False`` to opt out
496496
of that shrink for grids whose spatial extent is load-bearing for the script (e.g. a
497497
visualization asserting cluster-scale critical curves at ~30-50").
498498
"""
499499
if respect_small_datasets and os.environ.get("PYAUTO_SMALL_DATASETS") == "1":
500-
if shape_native[0] > 15 or shape_native[1] > 15:
501-
shape_native = (15, 15)
500+
if shape_native[0] > 16 or shape_native[1] > 16:
501+
shape_native = (16, 16)
502502
pixel_scales = 0.6
503503

504504
pixel_scales = geometry_util.convert_pixel_scales_2d(pixel_scales=pixel_scales)

autoarray/util/dataset_util.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pathlib import Path
44

55

6-
SMALL_DATASETS_SHAPE_NATIVE = (15, 15)
6+
SMALL_DATASETS_SHAPE_NATIVE = (16, 16)
77
SMALL_DATASETS_PIXEL_SCALES = 0.6
88

99

@@ -15,10 +15,10 @@ def cap_array_2d_for_small_datasets(array_2d, pixel_scales):
1515
Returns ``(array_2d, pixel_scales)`` unchanged in any of these cases:
1616
1717
- ``PYAUTO_SMALL_DATASETS`` is not set to ``"1"``.
18-
- ``array_2d.shape_native`` is already at-or-below the cap (15, 15).
18+
- ``array_2d.shape_native`` is already at-or-below the cap (16, 16).
1919
20-
When the env var is set and the input shape exceeds (15, 15), returns a
21-
new ``Array2D`` center-cropped to (15, 15) with ``pixel_scales`` overridden
20+
When the env var is set and the input shape exceeds (16, 16), returns a
21+
new ``Array2D`` center-cropped to (16, 16) with ``pixel_scales`` overridden
2222
to 0.6 — matching the convention used by ``Mask2D.circular`` and
2323
``Grid2D.uniform`` so the loaded dataset stays shape-consistent with masks
2424
and grids built under the same env var.

test_autoarray/dataset/imaging/test_dataset.py

Lines changed: 116 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ def test__from_fits__small_datasets_env_caps_data_and_noise_map(
196196
noise_map_path=Path(test_data_path) / "noise_map_30x30.fits",
197197
)
198198

199-
assert dataset.data.shape_native == (15, 15)
200-
assert dataset.noise_map.shape_native == (15, 15)
199+
assert dataset.data.shape_native == (16, 16)
200+
assert dataset.noise_map.shape_native == (16, 16)
201201
assert dataset.pixel_scales == (0.6, 0.6)
202202
assert dataset.psf.kernel.shape_native == (5, 5)
203203

@@ -410,117 +410,117 @@ def test__psf_not_odd_x_odd_kernel__raises_error():
410410
noise_map=noise_map,
411411
psf=psf,
412412
)
413-
414-
415-
def test__convolve_over_sample_size__validation_and_plumbing():
416-
data = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
417-
noise_map = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
418-
kernel_fine = aa.Array2D.no_mask(values=np.ones((9, 9)), pixel_scales=0.5)
419-
psf = aa.Convolver(kernel=kernel_fine)
420-
421-
# convolve size must be a plain int.
422-
with pytest.raises(TypeError):
423-
aa.Imaging(
424-
data=data,
425-
noise_map=noise_map,
426-
psf=psf,
427-
over_sample_size_lp=2,
428-
convolve_over_sample_size_lp=2.0,
429-
)
430-
431-
# k x s coupling: every over_sample_size entry must be divisible by the
432-
# convolve size — a non-divisible int raises, divisible ints and adaptive
433-
# arrays are legal.
434-
with pytest.raises(aa.exc.DatasetException):
435-
aa.Imaging(
436-
data=data,
437-
noise_map=noise_map,
438-
psf=psf,
439-
over_sample_size_lp=3,
440-
convolve_over_sample_size_lp=2,
441-
)
442-
443-
sub_size_adaptive = np.full(fill_value=2, shape=data.shape_slim)
444-
sub_size_adaptive[0] = 4
445-
446-
dataset_adaptive = aa.Imaging(
447-
data=data,
448-
noise_map=noise_map,
449-
psf=psf,
450-
over_sample_size_lp=aa.Array2D(values=sub_size_adaptive, mask=data.mask),
451-
convolve_over_sample_size_lp=2,
452-
)
453-
assert dataset_adaptive.convolve_over_sample_size_lp == 2
454-
455-
sub_size_bad = np.full(fill_value=2, shape=data.shape_slim)
456-
sub_size_bad[0] = 3
457-
458-
with pytest.raises(aa.exc.DatasetException):
459-
aa.Imaging(
460-
data=data,
461-
noise_map=noise_map,
462-
psf=psf,
463-
over_sample_size_lp=aa.Array2D(values=sub_size_bad, mask=data.mask),
464-
convolve_over_sample_size_lp=2,
465-
)
466-
467-
# Differing lp / pixelization convolve sizes are not supported (single PSF kernel).
468-
with pytest.raises(aa.exc.DatasetException):
469-
aa.Imaging(
470-
data=data,
471-
noise_map=noise_map,
472-
psf=psf,
473-
over_sample_size_lp=2,
474-
over_sample_size_pixelization=4,
475-
convolve_over_sample_size_lp=2,
476-
convolve_over_sample_size_pixelization=4,
477-
)
478-
479-
# Valid dataset: the psf carries the convolve size and apply_mask preserves it,
480-
# precomputing the fine state and building the blurring grid at the fine resolution.
481-
dataset = aa.Imaging(
482-
data=data,
483-
noise_map=noise_map,
484-
psf=psf,
485-
over_sample_size_lp=2,
486-
convolve_over_sample_size_lp=2,
487-
)
488-
489-
assert dataset.convolve_over_sample_size_lp == 2
490-
assert dataset.psf.convolve_over_sample_size == 2
491-
492-
mask = aa.Mask2D.circular(shape_native=(11, 11), pixel_scales=1.0, radius=3.5)
493-
masked = dataset.apply_mask(mask=mask)
494-
495-
assert masked.convolve_over_sample_size_lp == 2
496-
assert masked.psf.convolve_over_sample_size == 2
497-
assert masked.psf._state is not None
498-
assert masked.psf._state.sub_slim_to_fine_slim is not None
499-
500-
# The blurring grid footprint uses the kernel's image-resolution shape (5x5 for a
501-
# 9x9 fine kernel at s=2) and is evaluated at the fine resolution.
502-
blurring_mask = mask.derive_mask.blurring_from(
503-
kernel_shape_native=(5, 5), allow_padding=True
504-
)
505-
assert np.array(masked.grids.blurring.over_sampled).shape == (
506-
blurring_mask.pixels_in_mask * 4,
507-
2,
508-
)
509-
510-
511-
def test__convolve_over_sample_size__sparse_operator_guard():
512-
data = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
513-
noise_map = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
514-
kernel_fine = aa.Array2D.no_mask(values=np.ones((9, 9)), pixel_scales=0.5)
515-
psf = aa.Convolver(kernel=kernel_fine)
516-
517-
dataset = aa.Imaging(
518-
data=data,
519-
noise_map=noise_map,
520-
psf=psf,
521-
over_sample_size_pixelization=2,
522-
convolve_over_sample_size_pixelization=2,
523-
)
524-
525-
with pytest.raises(aa.exc.DatasetException):
526-
dataset.apply_sparse_operator()
413+
414+
415+
def test__convolve_over_sample_size__validation_and_plumbing():
416+
data = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
417+
noise_map = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
418+
kernel_fine = aa.Array2D.no_mask(values=np.ones((9, 9)), pixel_scales=0.5)
419+
psf = aa.Convolver(kernel=kernel_fine)
420+
421+
# convolve size must be a plain int.
422+
with pytest.raises(TypeError):
423+
aa.Imaging(
424+
data=data,
425+
noise_map=noise_map,
426+
psf=psf,
427+
over_sample_size_lp=2,
428+
convolve_over_sample_size_lp=2.0,
429+
)
430+
431+
# k x s coupling: every over_sample_size entry must be divisible by the
432+
# convolve size — a non-divisible int raises, divisible ints and adaptive
433+
# arrays are legal.
434+
with pytest.raises(aa.exc.DatasetException):
435+
aa.Imaging(
436+
data=data,
437+
noise_map=noise_map,
438+
psf=psf,
439+
over_sample_size_lp=3,
440+
convolve_over_sample_size_lp=2,
441+
)
442+
443+
sub_size_adaptive = np.full(fill_value=2, shape=data.shape_slim)
444+
sub_size_adaptive[0] = 4
445+
446+
dataset_adaptive = aa.Imaging(
447+
data=data,
448+
noise_map=noise_map,
449+
psf=psf,
450+
over_sample_size_lp=aa.Array2D(values=sub_size_adaptive, mask=data.mask),
451+
convolve_over_sample_size_lp=2,
452+
)
453+
assert dataset_adaptive.convolve_over_sample_size_lp == 2
454+
455+
sub_size_bad = np.full(fill_value=2, shape=data.shape_slim)
456+
sub_size_bad[0] = 3
457+
458+
with pytest.raises(aa.exc.DatasetException):
459+
aa.Imaging(
460+
data=data,
461+
noise_map=noise_map,
462+
psf=psf,
463+
over_sample_size_lp=aa.Array2D(values=sub_size_bad, mask=data.mask),
464+
convolve_over_sample_size_lp=2,
465+
)
466+
467+
# Differing lp / pixelization convolve sizes are not supported (single PSF kernel).
468+
with pytest.raises(aa.exc.DatasetException):
469+
aa.Imaging(
470+
data=data,
471+
noise_map=noise_map,
472+
psf=psf,
473+
over_sample_size_lp=2,
474+
over_sample_size_pixelization=4,
475+
convolve_over_sample_size_lp=2,
476+
convolve_over_sample_size_pixelization=4,
477+
)
478+
479+
# Valid dataset: the psf carries the convolve size and apply_mask preserves it,
480+
# precomputing the fine state and building the blurring grid at the fine resolution.
481+
dataset = aa.Imaging(
482+
data=data,
483+
noise_map=noise_map,
484+
psf=psf,
485+
over_sample_size_lp=2,
486+
convolve_over_sample_size_lp=2,
487+
)
488+
489+
assert dataset.convolve_over_sample_size_lp == 2
490+
assert dataset.psf.convolve_over_sample_size == 2
491+
492+
mask = aa.Mask2D.circular(shape_native=(11, 11), pixel_scales=1.0, radius=3.5)
493+
masked = dataset.apply_mask(mask=mask)
494+
495+
assert masked.convolve_over_sample_size_lp == 2
496+
assert masked.psf.convolve_over_sample_size == 2
497+
assert masked.psf._state is not None
498+
assert masked.psf._state.sub_slim_to_fine_slim is not None
499+
500+
# The blurring grid footprint uses the kernel's image-resolution shape (5x5 for a
501+
# 9x9 fine kernel at s=2) and is evaluated at the fine resolution.
502+
blurring_mask = mask.derive_mask.blurring_from(
503+
kernel_shape_native=(5, 5), allow_padding=True
504+
)
505+
assert np.array(masked.grids.blurring.over_sampled).shape == (
506+
blurring_mask.pixels_in_mask * 4,
507+
2,
508+
)
509+
510+
511+
def test__convolve_over_sample_size__sparse_operator_guard():
512+
data = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
513+
noise_map = aa.Array2D.no_mask(values=np.ones((11, 11)), pixel_scales=1.0)
514+
kernel_fine = aa.Array2D.no_mask(values=np.ones((9, 9)), pixel_scales=0.5)
515+
psf = aa.Convolver(kernel=kernel_fine)
516+
517+
dataset = aa.Imaging(
518+
data=data,
519+
noise_map=noise_map,
520+
psf=psf,
521+
over_sample_size_pixelization=2,
522+
convolve_over_sample_size_pixelization=2,
523+
)
524+
525+
with pytest.raises(aa.exc.DatasetException):
526+
dataset.apply_sparse_operator()

test_autoarray/mask/test_mask_2d.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,11 @@ def test__circular__small_datasets_env__oversized_radius_clamped_to_disc(monkeyp
134134

135135
mask = aa.Mask2D.circular(shape_native=(100, 100), pixel_scales=0.1, radius=6.0)
136136

137-
assert mask.shape_native == (15, 15)
137+
assert mask.shape_native == (16, 16)
138138
assert mask.pixel_scales == (0.6, 0.6)
139139
assert mask.is_circular
140140
n_unmasked = int((~mask.array).sum())
141-
assert 100 < n_unmasked < 225
141+
assert 100 < n_unmasked < 256
142142

143143

144144
def test__circular__small_datasets_env__in_bounds_radius_unchanged(monkeypatch):
@@ -161,7 +161,7 @@ def test__circular__small_datasets_env__tuple_pixel_scales_oversized_radius_clam
161161
shape_native=(200, 200), pixel_scales=(0.1, 0.1), radius=7.5
162162
)
163163

164-
assert mask.shape_native == (15, 15)
164+
assert mask.shape_native == (16, 16)
165165
assert mask.is_circular
166166

167167

test_autoarray/util/test_dataset_util.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ def test__env_set__shape_above_cap__center_crops_and_overrides_pixel_scales(
5959
assert result.shape_native == SMALL_DATASETS_SHAPE_NATIVE
6060
assert pixel_scales == SMALL_DATASETS_PIXEL_SCALES
6161

62-
h0 = (150 - 15) // 2
63-
expected = raw[h0:h0 + 15, h0:h0 + 15]
62+
cap_h, cap_w = SMALL_DATASETS_SHAPE_NATIVE
63+
h0, w0 = (150 - cap_h) // 2, (150 - cap_w) // 2
64+
expected = raw[h0:h0 + cap_h, w0:w0 + cap_w]
6465
assert (result.native.array == expected).all()
6566

6667

67-
def test__env_set__non_square_above_cap__center_crops_to_15x15(monkeypatch):
68+
def test__env_set__non_square_above_cap__center_crops_to_16x16(monkeypatch):
6869
monkeypatch.setenv("PYAUTO_SMALL_DATASETS", "1")
6970

7071
array = _array_2d((100, 50))

0 commit comments

Comments
 (0)