diff --git a/autoarray/__init__.py b/autoarray/__init__.py index 5ddfb22a..e467e4a7 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -29,6 +29,7 @@ from .settings import Settings from .inversion.inversion.abstract import AbstractInversion from .preloads import AbstractPreloads +from .preloads import PreloadsImaging from .preloads import PreloadsInterferometer from .inversion.regularization.abstract import AbstractRegularization from .inversion.inversion.factory import inversion_from as Inversion diff --git a/autoarray/preloads/__init__.py b/autoarray/preloads/__init__.py index 4cea66dd..8a4a0493 100644 --- a/autoarray/preloads/__init__.py +++ b/autoarray/preloads/__init__.py @@ -1,2 +1,3 @@ from .abstract import AbstractPreloads +from .imaging import PreloadsImaging from .interferometer import PreloadsInterferometer diff --git a/autoarray/preloads/abstract.py b/autoarray/preloads/abstract.py index c3576bca..0e83e6be 100644 --- a/autoarray/preloads/abstract.py +++ b/autoarray/preloads/abstract.py @@ -1,5 +1,11 @@ class AbstractPreloads: - def __init__(self, curvature_matrix=None, mapper_galaxy_dict=None): + def __init__( + self, + curvature_matrix=None, + mapper_galaxy_dict=None, + source_plane_mesh_grid=None, + image_plane_mesh_grid=None, + ): """ Container for quantities that are *preloaded* into a fit / inversion: computed once and injected so that repeated evaluations reuse them instead of recomputing them. @@ -42,7 +48,24 @@ def __init__(self, curvature_matrix=None, mapper_galaxy_dict=None): plane); when invariant across evaluations it is built once and reused here, so the `mapper` (and therefore the `mapping_matrix` and `regularization_matrix`) is not rebuilt. Stored opaquely — the consumer (e.g. PyAutoLens's `TracerToInversion`) populates and - interprets it. + interprets it. Valid ONLY when the datasets sharing it have identical grids (e.g. the + channels of a datacube) — a mapper embeds a dataset's own data-grid mappings. + source_plane_mesh_grid + The pre-computed source-plane mesh geometry (e.g. the ray-traced centres a Delaunay + triangulation is built over), for consumers whose datasets share a lens model but NOT + their grids (e.g. multi-exposure imaging with per-exposure pixel offsets and PSFs). + Unlike `mapper_galaxy_dict`, this preloads only the mesh: each dataset still builds its + own mapper by mapping its own (offset) data grid onto this shared mesh. Stored opaquely + in the consumer's plane-grouped structure (PyAutoLens: the `traced_mesh_grid_pg_list` + of the lead dataset). The mapping matrix, curvature matrix, blurred mapping matrix and + regularization matrix are all deliberately NOT preloadable this way — offsets/PSFs make + the first three per-dataset, and regularization may be data-adaptive. + image_plane_mesh_grid + The image-plane counterpart of `source_plane_mesh_grid` (the mesh centres before + ray-tracing, in the lead dataset's frame), carried alongside it as metadata for + downstream consumers (e.g. mapper plotting). Same opaque plane-grouped structure. """ self.curvature_matrix = curvature_matrix self.mapper_galaxy_dict = mapper_galaxy_dict + self.source_plane_mesh_grid = source_plane_mesh_grid + self.image_plane_mesh_grid = image_plane_mesh_grid diff --git a/autoarray/preloads/imaging.py b/autoarray/preloads/imaging.py new file mode 100644 index 00000000..337d076e --- /dev/null +++ b/autoarray/preloads/imaging.py @@ -0,0 +1,35 @@ +from autoarray.preloads.abstract import AbstractPreloads + + +class PreloadsImaging(AbstractPreloads): + def __init__(self, source_plane_mesh_grid=None, image_plane_mesh_grid=None): + """ + Preloaded quantities for an imaging fit / inversion (see `AbstractPreloads`). + + This is the consumer-facing preloads container for imaging data — for example the object + returned by `AnalysisImaging.shared_state_from` for the multi-exposure shared-state path, + where exposures of the same lens (at the same or different wavelengths, with per-exposure + pixel offsets) share one source-plane mesh. + + The invariance contract differs from the datacube (`PreloadsInterferometer`) case, whose + channels share a single real-space grid and can therefore preload the whole mapper and + curvature matrix. Imaging exposures have per-exposure PSFs and pixel offsets, so: + + - the source-plane mesh geometry IS shareable (a pure function of the shared lens model and + the lead exposure's image-mesh) — preloaded here; + - the mapper, mapping matrix, blurred mapping matrix and curvature matrix are NOT (each + exposure maps its own offset grid onto the shared mesh and blurs with its own PSF); + - the regularization matrix is NOT (regularization may adapt to per-exposure data). + + Parameters + ---------- + source_plane_mesh_grid + The shared source-plane mesh geometry, traced once from the lead exposure. See + `AbstractPreloads`. + image_plane_mesh_grid + Its image-plane counterpart in the lead exposure's frame. See `AbstractPreloads`. + """ + super().__init__( + source_plane_mesh_grid=source_plane_mesh_grid, + image_plane_mesh_grid=image_plane_mesh_grid, + ) diff --git a/test_autoarray/preloads/__init__.py b/test_autoarray/preloads/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test_autoarray/preloads/test_preloads.py b/test_autoarray/preloads/test_preloads.py new file mode 100644 index 00000000..81dcc72d --- /dev/null +++ b/test_autoarray/preloads/test_preloads.py @@ -0,0 +1,29 @@ +import autoarray as aa + + +def test__abstract_preloads__all_fields_default_none(): + preloads = aa.AbstractPreloads() + + assert preloads.curvature_matrix is None + assert preloads.mapper_galaxy_dict is None + assert preloads.source_plane_mesh_grid is None + assert preloads.image_plane_mesh_grid is None + + +def test__preloads_imaging__carries_mesh_geometry_only(): + preloads = aa.PreloadsImaging( + source_plane_mesh_grid=[["mesh"]], image_plane_mesh_grid=[["image_mesh"]] + ) + + assert preloads.source_plane_mesh_grid == [["mesh"]] + assert preloads.image_plane_mesh_grid == [["image_mesh"]] + assert preloads.curvature_matrix is None + assert preloads.mapper_galaxy_dict is None + + +def test__preloads_interferometer__mesh_fields_available_via_abstract(): + preloads = aa.PreloadsInterferometer(curvature_matrix="F") + + assert preloads.curvature_matrix == "F" + assert preloads.source_plane_mesh_grid is None + assert preloads.image_plane_mesh_grid is None