99from autoarray .dataset .grids import GridsDataset
1010from autoarray .dataset .imaging .w_tilde import WTildeImaging
1111from autoarray .structures .arrays .uniform_2d import Array2D
12+ from autoarray .operators .convolver import Convolver
1213from autoarray .structures .arrays .kernel_2d import Kernel2D
1314from autoarray .mask .mask_2d import Mask2D
1415from autoarray import type as ty
@@ -29,7 +30,7 @@ def __init__(
2930 noise_covariance_matrix : Optional [np .ndarray ] = None ,
3031 over_sample_size_lp : Union [int , Array2D ] = 4 ,
3132 over_sample_size_pixelization : Union [int , Array2D ] = 4 ,
32- pad_for_psf : bool = False ,
33+ pad_for_convolver : bool = False ,
3334 use_normalized_psf : Optional [bool ] = True ,
3435 check_noise_map : bool = True ,
3536 ):
@@ -76,7 +77,7 @@ def __init__(
7677 over_sample_size_pixelization
7778 How over sampling is performed for the grid which is associated with a pixelization, which is therefore
7879 passed into the calculations performed in the `inversion` module.
79- pad_for_psf
80+ pad_for_convolver
8081 The PSF convolution may extend beyond the edges of the image mask, which can lead to edge effects in the
8182 convolved image. If `True`, the image and noise-map are padded to ensure the PSF convolution does not
8283 extend beyond the edge of the image.
@@ -89,9 +90,9 @@ def __init__(
8990
9091 self .unmasked = None
9192
92- self .pad_for_psf = pad_for_psf
93+ self .pad_for_convolver = pad_for_convolver
9394
94- if pad_for_psf and psf is not None :
95+ if pad_for_convolver and psf is not None :
9596 try :
9697 data .mask .derive_mask .blurring_from (
9798 kernel_shape_native = psf .shape_native
@@ -161,15 +162,11 @@ def __init__(
161162
162163 if psf is not None and use_normalized_psf :
163164 psf = Kernel2D .no_mask (
164- values = psf .native . _array , pixel_scales = psf .pixel_scales , normalize = True
165+ values = psf .native , pixel_scales = psf .pixel_scales , normalize = True
165166 )
166167
167168 self .psf = psf
168169
169- if psf is not None :
170- if psf .mask .shape [0 ] % 2 == 0 or psf .mask .shape [1 ] % 2 == 0 :
171- raise exc .KernelException ("Kernel2D Kernel2D must be odd" )
172-
173170 @cached_property
174171 def grids (self ):
175172 return GridsDataset (
@@ -179,6 +176,25 @@ def grids(self):
179176 psf = self .psf ,
180177 )
181178
179+ @cached_property
180+ def convolver (self ):
181+ """
182+ Returns a `Convolver` from a mask and 2D PSF kernel.
183+
184+ The `Convolver` stores in memory the array indexing between the mask and PSF, enabling efficient 2D PSF
185+ convolution of images and matrices used for linear algebra calculations (see `operators.convolver`).
186+
187+ This uses lazy allocation such that the calculation is only performed when the convolver is used, ensuring
188+ efficient set up of the `Imaging` class.
189+
190+ Returns
191+ -------
192+ Convolver
193+ The convolver given the masked imaging data's mask and PSF.
194+ """
195+
196+ return Convolver (mask = self .mask , kernel = self .psf )
197+
182198 @cached_property
183199 def w_tilde (self ):
184200 """
@@ -204,9 +220,9 @@ def w_tilde(self):
204220 indexes ,
205221 lengths ,
206222 ) = inversion_imaging_util .w_tilde_curvature_preload_imaging_from (
207- noise_map_native = np .array (self .noise_map .native . array ). astype ( "float64" ),
208- kernel_native = np .array (self .psf .native . array ). astype ( "float64" ),
209- native_index_for_slim_index = np . array ( self .mask .derive_indexes .native_for_slim ). astype ( "int" ) ,
223+ noise_map_native = np .array (self .noise_map .native ),
224+ kernel_native = np .array (self .psf .native ),
225+ native_index_for_slim_index = self .mask .derive_indexes .native_for_slim ,
210226 )
211227
212228 return WTildeImaging (
@@ -354,7 +370,7 @@ def apply_mask(self, mask: Mask2D) -> "Imaging":
354370 noise_covariance_matrix = noise_covariance_matrix ,
355371 over_sample_size_lp = over_sample_size_lp ,
356372 over_sample_size_pixelization = over_sample_size_pixelization ,
357- pad_for_psf = True ,
373+ pad_for_convolver = True ,
358374 )
359375
360376 dataset .unmasked = unmasked_dataset
@@ -409,20 +425,20 @@ def apply_noise_scaling(
409425 """
410426
411427 if signal_to_noise_value is None :
412- noise_map = np . array ( self .noise_map .native . array )
413- noise_map [mask . array == False ] = noise_value
428+ noise_map = self .noise_map .native
429+ noise_map [mask == False ] = noise_value
414430 else :
415431 noise_map = np .where (
416432 mask == False ,
417- np .median (self .data .native . array [mask .derive_mask .edge == False ])
433+ np .median (self .data .native [mask .derive_mask .edge == False ])
418434 / signal_to_noise_value ,
419- self .noise_map .native . array ,
435+ self .noise_map .native ,
420436 )
421437
422438 if should_zero_data :
423- data = np .where (np .invert (mask . array ), 0.0 , self .data .native . array )
439+ data = np .where (np .invert (mask ), 0.0 , self .data .native )
424440 else :
425- data = self .data .native . array
441+ data = self .data .native
426442
427443 data_unmasked = Array2D .no_mask (
428444 values = data ,
@@ -447,7 +463,7 @@ def apply_noise_scaling(
447463 noise_covariance_matrix = self .noise_covariance_matrix ,
448464 over_sample_size_lp = self .over_sample_size_lp ,
449465 over_sample_size_pixelization = self .over_sample_size_pixelization ,
450- pad_for_psf = False ,
466+ pad_for_convolver = False ,
451467 check_noise_map = False ,
452468 )
453469
@@ -495,7 +511,7 @@ def apply_over_sampling(
495511 over_sample_size_lp = over_sample_size_lp or self .over_sample_size_lp ,
496512 over_sample_size_pixelization = over_sample_size_pixelization
497513 or self .over_sample_size_pixelization ,
498- pad_for_psf = False ,
514+ pad_for_convolver = False ,
499515 check_noise_map = False ,
500516 )
501517
0 commit comments