Skip to content

Commit bcd15cd

Browse files
authored
Merge pull request #529 from PyAutoLabs/claude/ci-test-timing-epic-ke2lul
Small-datasets cap: read SMALLSHP so capped datasets stop re-simulating, honour PYAUTO_DISABLE_JAX in the interferometer sparse operator, cap mesh shapes with the data
2 parents a0e5c61 + 499e087 commit bcd15cd

12 files changed

Lines changed: 799 additions & 33 deletions

File tree

autoarray/dataset/imaging/dataset.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,20 @@ def apply_sparse_operator(
602602
Imaging
603603
A new `Imaging` dataset with the precomputed `ImagingSparseOperator` attached, enabling
604604
efficient pixelized source reconstruction via the sparse linear algebra formalism.
605+
606+
Notes
607+
-----
608+
`PYAUTO_DISABLE_JAX=1` is *not* honoured here, unlike
609+
`Interferometer.apply_sparse_operator`, and the asymmetry is deliberate. There the
610+
variable overrides a `use_jax` argument that already selects between two backends
611+
computing the same operator. Here there is no such argument: this method is the JAX
612+
implementation, and the NumPy/CPU alternative is the separately named
613+
`apply_sparse_operator_cpu`, which returns a different operator class
614+
(`SparseLinAlgImagingNumba`) and requires numba. Silently returning that under an
615+
environment variable would change the type of the returned object based on the
616+
environment, which is a larger change than honouring a switch -- and an unmeasured
617+
one: every JIT cost the phase-8 workspace timings attribute to this variable
618+
(2.3-3.2 s per script) was on the interferometer path.
605619
"""
606620

607621
if self.psf is not None and self.psf.convolve_over_sample_size > 1:

autoarray/dataset/interferometer/dataset.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@
55
from autonerves.fitsable import ndarray_via_fits_from
66
from autonerves import cached_property
77

8+
try:
9+
from autonerves.test_mode import disable_jax
10+
except ImportError:
11+
# `disable_jax()` arrives in the autonerves release that closes
12+
# PyAutoNerves#159. Importing it unconditionally would make an autonerves
13+
# older than that release an `ImportError` at module load -- and a
14+
# `--no-deps` install, an editable checkout or a hand-built virtualenv can
15+
# all put one on the path regardless of the floor in `pyproject.toml`,
16+
# which constrains resolution only. This is the same trade `dataset_util`
17+
# records against `SMALL_DATASETS_HEADER_KEY`: degrade to the predicate's
18+
# own one-line body rather than fail hard to avoid restating it. Delete the
19+
# fallback when the floor names a release carrying the predicate.
20+
import os
21+
22+
def disable_jax():
23+
return os.environ.get("PYAUTO_DISABLE_JAX") == "1"
24+
25+
826
from autoarray.dataset.abstract.dataset import AbstractDataset
927
from autoarray.dataset.grids import GridsDataset
1028
from autoarray.inversion.inversion.interferometer.inversion_interferometer_util import (
@@ -266,6 +284,14 @@ def apply_sparse_operator(
266284
use_jax
267285
If `True`, JAX is used to accelerate the NUFFT precision matrix computation.
268286
287+
`PYAUTO_DISABLE_JAX=1` overrides this to `False`. That variable is a
288+
harness-level switch, not a preference: it is the documented way to force the
289+
NumPy path (the workspace `start_here` guides name it beside `use_jax=False`),
290+
and the smoke profiles set it so a fast run does not pay a JIT compile. An
291+
explicit `use_jax=True` in a script -- which is the right thing for a script
292+
demonstrating the production path to say -- must therefore not defeat it, or
293+
the harness pays 2.3-3.2 s of compile for a backend it asked to disable.
294+
269295
Precondition
270296
------------
271297
Every visibility must have equal real and imaginary noise sigma
@@ -289,6 +315,9 @@ def apply_sparse_operator(
289315
If any visibility has unequal real and imaginary noise sigma.
290316
"""
291317

318+
if disable_jax():
319+
use_jax = False
320+
292321
noise_map_real = np.asarray(self.noise_map.real)
293322
noise_map_imag = np.asarray(self.noise_map.imag)
294323

autoarray/inversion/mesh/image_mesh/overlay.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from autoarray.geometry import geometry_util
1010
from autoarray.structures.grids import grid_2d_util
11+
from autoarray.util.dataset_util import cap_mesh_shape_for_small_datasets
1112
from autoarray import numba_util
1213

1314

@@ -158,7 +159,7 @@ def overlay_via_unmasked_overlaid_from(
158159

159160

160161
class Overlay(AbstractImageMesh):
161-
def __init__(self, shape=(3, 3)):
162+
def __init__(self, shape=(3, 3), respect_small_datasets: bool = True):
162163
"""
163164
Computes an image-mesh by overlaying a uniform grid of (y,x) coordinates over the masked image that the
164165
pixelization is fitting.
@@ -176,10 +177,18 @@ def __init__(self, shape=(3, 3)):
176177
----------
177178
shape
178179
The 2D shape of the grid which is overlaid over the grid to determine the image mesh.
180+
respect_small_datasets
181+
When `PYAUTO_SMALL_DATASETS=1` is set, `shape` is capped per axis to the small-datasets
182+
cap, matching the cap `Grid2D.uniform` and `Mask2D.circular` apply to the data. Pass
183+
`False` to opt out for an image mesh whose resolution is load-bearing for the script.
179184
"""
180185

181186
super().__init__()
182187

188+
shape = cap_mesh_shape_for_small_datasets(
189+
shape, respect_small_datasets=respect_small_datasets
190+
)
191+
183192
self.shape = (int(shape[0]), int(shape[1]))
184193

185194
def image_plane_mesh_grid_from(

autoarray/inversion/mesh/mesh/rectangular_bilinear_adapt_density.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class RectangularBilinearAdaptDensity(RectangularRTUAdaptDensity):
1010
def __init__(
1111
self,
1212
shape: Tuple[int, int] = (3, 3),
13+
respect_small_datasets: bool = True,
1314
):
1415
"""
1516
A rectangular mesh of pixels used to reconstruct a source on a regular
@@ -71,7 +72,9 @@ def __init__(
7172
If either dimension is less than 3, as a minimum of 3×3 pixels
7273
is required to define interior and boundary structure.
7374
"""
74-
super().__init__(shape=shape)
75+
super().__init__(
76+
shape=shape, respect_small_datasets=respect_small_datasets
77+
)
7578

7679
@property
7780
def interpolator_kwargs(self) -> dict:

autoarray/inversion/mesh/mesh/rectangular_bilinear_adapt_image.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def __init__(
1212
shape: Tuple[int, int] = (3, 3),
1313
weight_power: float = 1.0,
1414
weight_floor: float = 0.0,
15+
respect_small_datasets: bool = True,
1516
):
1617
"""
1718
A rectangular mesh of pixels used to reconstruct a source on a regular
@@ -69,6 +70,7 @@ def __init__(
6970
shape=shape,
7071
weight_power=weight_power,
7172
weight_floor=weight_floor,
73+
respect_small_datasets=respect_small_datasets,
7274
)
7375

7476
@property

autoarray/inversion/mesh/mesh/rectangular_rtu_adapt_density.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from autoarray.inversion.mesh.border_relocator import BorderRelocator
1010

1111
from autoarray.structures.grids import grid_2d_util
12+
from autoarray.util.dataset_util import cap_mesh_shape_for_small_datasets
1213

1314
from autoarray import exc
1415

@@ -71,6 +72,7 @@ def __init__(
7172
shape: Tuple[int, int] = (3, 3),
7273
bandwidth: Optional[float] = None,
7374
n_knots: Optional[int] = None,
75+
respect_small_datasets: bool = True,
7476
):
7577
"""
7678
A rectangular mesh of pixels used to reconstruct a source on a regular
@@ -132,6 +134,11 @@ def __init__(
132134
n_knots
133135
Size of the fixed knot table used to invert the CDF. Defaults to
134136
the kernel default.
137+
respect_small_datasets
138+
When ``PYAUTO_SMALL_DATASETS=1`` is set, `shape` is capped per axis
139+
to the small-datasets cap, matching the cap `Grid2D.uniform` and
140+
`Mask2D.circular` apply to the data. Pass ``False`` to opt out for a
141+
mesh whose resolution is load-bearing for the script.
135142
136143
Raises
137144
------
@@ -144,6 +151,10 @@ def __init__(
144151
KERNEL_CDF_DEFAULT_KNOTS,
145152
)
146153

154+
shape = cap_mesh_shape_for_small_datasets(
155+
shape, respect_small_datasets=respect_small_datasets
156+
)
157+
147158
if shape[0] <= 2 or shape[1] <= 2:
148159
raise exc.MeshException(
149160
"The rectangular pixelization must be at least dimensions 3x3"

autoarray/inversion/mesh/mesh/rectangular_rtu_adapt_image.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def __init__(
1515
weight_floor: float = 0.0,
1616
bandwidth: Optional[float] = None,
1717
n_knots: Optional[int] = None,
18+
respect_small_datasets: bool = True,
1819
):
1920
"""
2021
A uniform rectangular mesh of pixels used to reconstruct a source on a
@@ -82,7 +83,12 @@ def __init__(
8283
the kernel default.
8384
"""
8485

85-
super().__init__(shape=shape, bandwidth=bandwidth, n_knots=n_knots)
86+
super().__init__(
87+
shape=shape,
88+
bandwidth=bandwidth,
89+
n_knots=n_knots,
90+
respect_small_datasets=respect_small_datasets,
91+
)
8692

8793
self.weight_power = weight_power
8894
self.weight_floor = weight_floor

0 commit comments

Comments
 (0)