Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions autoarray/dataset/abstract/w_tilde.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


class AbstractWTilde:
def __init__(self, curvature_preload, noise_map_value):
def __init__(self, curvature_preload):
"""
Packages together all derived data quantities necessary to fit `data (e.g. `Imaging`, Interferometer`) using
an ` Inversion` via the w_tilde formalism.
Expand All @@ -16,20 +16,5 @@ def __init__(self, curvature_preload, noise_map_value):
curvature_preload
A matrix which uses the imaging's noise-map and PSF to preload as much of the computation of the
curvature matrix as possible.
noise_map_value
The first value of the noise-map used to construct the curvature preload, which is used as a sanity
check when performing the inversion to ensure the preload corresponds to the data being fitted.
"""
self.curvature_preload = curvature_preload
self.noise_map_value = noise_map_value

def check_noise_map(self, noise_map):
if noise_map[0] != self.noise_map_value:
raise exc.InversionException(
"The preloaded values of WTildeImaging are not consistent with the noise-map passed to them, thus "
"they cannot be used for the inversion."
""
f"The value of the noise map is {noise_map[0]} whereas in WTildeImaging it is {self.noise_map_value}"
""
"Update WTildeImaging or do not use the w_tilde formalism to perform the Inversion."
)
121 changes: 69 additions & 52 deletions autoarray/dataset/imaging/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(
disable_fft_pad: bool = True,
use_normalized_psf: Optional[bool] = True,
check_noise_map: bool = True,
w_tilde: Optional[WTildeImaging] = None,
):
"""
An imaging dataset, containing the image data, noise-map, PSF and associated quantities
Expand Down Expand Up @@ -85,6 +86,10 @@ def __init__(
the PSF kernel does not change the overall normalization of the image when it is convolved with it.
check_noise_map
If True, the noise-map is checked to ensure all values are above zero.
w_tilde
The w_tilde formalism of the linear algebra equations precomputes the convolution of every pair of masked
noise-map values given the PSF (see `inversion.inversion_util`). Pass the `WTildeImaging` object here to
enable this linear algebra formalism for pixelized reconstructions.
"""

self.disable_fft_pad = disable_fft_pad
Expand Down Expand Up @@ -193,58 +198,7 @@ def __init__(
psf=self.psf,
)

@cached_property
def w_tilde(self):
"""
The w_tilde formalism of the linear algebra equations precomputes the convolution of every pair of masked
noise-map values given the PSF (see `inversion.inversion_util`).

The `WTilde` object stores these precomputed values in the imaging dataset ensuring they are only computed once
per analysis.

This uses lazy allocation such that the calculation is only performed when the wtilde matrices are used,
ensuring efficient set up of the `Imaging` class.

Returns
-------
WTildeImaging
Precomputed values used for the w tilde formalism of linear algebra calculations.
"""

logger.info("IMAGING - Computing W-Tilde... May take a moment.")

try:
import numba
except ModuleNotFoundError:
raise exc.InversionException(
"Inversion w-tilde functionality (pixelized reconstructions) is "
"disabled if numba is not installed.\n\n"
"This is because the run-times without numba are too slow.\n\n"
"Please install numba, which is described at the following web page:\n\n"
"https://pyautolens.readthedocs.io/en/latest/installation/overview.html"
)

(
curvature_preload,
indexes,
lengths,
) = inversion_imaging_numba_util.w_tilde_curvature_preload_imaging_from(
noise_map_native=np.array(self.noise_map.native.array).astype("float64"),
kernel_native=np.array(self.psf.native.array).astype("float64"),
native_index_for_slim_index=np.array(
self.mask.derive_indexes.native_for_slim
).astype("int"),
)

return WTildeImaging(
curvature_preload=curvature_preload,
indexes=indexes.astype("int"),
lengths=lengths.astype("int"),
noise_map_value=self.noise_map[0],
noise_map=self.noise_map,
psf=self.psf,
mask=self.mask,
)
self.w_tilde = w_tilde

@classmethod
def from_fits(
Expand Down Expand Up @@ -517,6 +471,69 @@ def apply_over_sampling(

return dataset

def apply_w_tilde(self, disable_fft_pad: bool = False):
"""
The w_tilde formalism of the linear algebra equations precomputes the convolution of every pair of masked
noise-map values given the PSF (see `inversion.inversion_util`).

The `WTilde` object stores these precomputed values in the imaging dataset ensuring they are only computed once
per analysis.

This uses lazy allocation such that the calculation is only performed when the wtilde matrices are used,
ensuring efficient set up of the `Imaging` class.

Returns
-------
WTildeImaging
Precomputed values used for the w tilde formalism of linear algebra calculations.
"""

logger.info("IMAGING - Computing W-Tilde... May take a moment.")

try:
import numba
except ModuleNotFoundError:
raise exc.InversionException(
"Inversion w-tilde functionality (pixelized reconstructions) is "
"disabled if numba is not installed.\n\n"
"This is because the run-times without numba are too slow.\n\n"
"Please install numba, which is described at the following web page:\n\n"
"https://pyautolens.readthedocs.io/en/latest/installation/overview.html"
)

(
curvature_preload,
indexes,
lengths,
) = inversion_imaging_numba_util.w_tilde_curvature_preload_imaging_from(
noise_map_native=np.array(self.noise_map.native.array).astype("float64"),
kernel_native=np.array(self.psf.native.array).astype("float64"),
native_index_for_slim_index=np.array(
self.mask.derive_indexes.native_for_slim
).astype("int"),
)

w_tilde = WTildeImaging(
curvature_preload=curvature_preload,
indexes=indexes.astype("int"),
lengths=lengths.astype("int"),
noise_map=self.noise_map,
psf=self.psf,
mask=self.mask,
)

return Imaging(
data=self.data,
noise_map=self.noise_map,
psf=self.psf,
noise_covariance_matrix=self.noise_covariance_matrix,
over_sample_size_lp=self.over_sample_size_lp,
over_sample_size_pixelization=self.over_sample_size_pixelization,
disable_fft_pad=disable_fft_pad,
check_noise_map=False,
w_tilde=w_tilde,
)

def output_to_fits(
self,
data_path: Union[Path, str],
Expand Down
6 changes: 1 addition & 5 deletions autoarray/dataset/imaging/w_tilde.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ def __init__(
noise_map: np.ndarray,
psf: np.ndarray,
mask: np.ndarray,
noise_map_value: float,
):
"""
Packages together all derived data quantities necessary to fit `Imaging` data using an ` Inversion` via the
Expand All @@ -39,12 +38,9 @@ def __init__(
lengths
The lengths of how many indexes each curvature preload contains, again used to compute the curvature
matrix efficienctly.
noise_map_value
The first value of the noise-map used to construct the curvature preload, which is used as a sanity
check when performing the inversion to ensure the preload corresponds to the data being fitted.
"""
super().__init__(
curvature_preload=curvature_preload, noise_map_value=noise_map_value
curvature_preload=curvature_preload,
)

self.indexes = indexes
Expand Down
47 changes: 17 additions & 30 deletions autoarray/dataset/interferometer/dataset.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import numpy as np
from pathlib import Path
from typing import Optional

from autoconf import cached_property

Expand Down Expand Up @@ -30,7 +31,7 @@ def __init__(
real_space_mask: Mask2D,
transformer_class=TransformerNUFFT,
dft_preload_transform: bool = True,
preprocessing_directory=None,
w_tilde: Optional[WTildeInterferometer] = None,
):
"""
An interferometer dataset, containing the visibilities data, noise-map, real-space msk, Fourier transformer and
Expand Down Expand Up @@ -97,18 +98,16 @@ def __init__(
preload_transform=dft_preload_transform,
)

self.preprocessing_directory = (
Path(preprocessing_directory)
if preprocessing_directory is not None
else None
)
self.dft_preload_transform = dft_preload_transform

self.grids = GridsDataset(
mask=self.real_space_mask,
over_sample_size_lp=self.over_sample_size_lp,
over_sample_size_pixelization=self.over_sample_size_pixelization,
)

self.w_tilde = w_tilde

@classmethod
def from_fits(
cls,
Expand Down Expand Up @@ -149,28 +148,7 @@ def from_fits(
dft_preload_transform=dft_preload_transform,
)

def w_tilde_preprocessing(self):

from astropy.io import fits

if self.preprocessing_directory.is_dir():
filename = "{}/curvature_preload.fits".format(self.preprocessing_directory)

if not self.preprocessing_directory.isfile(filename):
print("The file {} does not exist".format(filename))
logger.info("INTERFEROMETER - Computing W-Tilde... May take a moment.")

curvature_preload = inversion_interferometer_util.w_tilde_curvature_preload_interferometer_from(
noise_map_real=self.noise_map.real,
uv_wavelengths=self.uv_wavelengths,
shape_masked_pixels_2d=self.transformer.grid.mask.shape_native_masked_pixels,
grid_radians_2d=self.transformer.grid.mask.unmasked_grid_sub_1.in_radians.native,
)

fits.writeto(filename, data=curvature_preload)

@cached_property
def w_tilde(self):
def apply_w_tilde(self):
"""
The w_tilde formalism of the linear algebra equations precomputes the Fourier Transform of all the visibilities
given the `uv_wavelengths` (see `inversion.inversion_util`).
Expand Down Expand Up @@ -226,12 +204,21 @@ def w_tilde(self):
use_adjoint_scaling=True,
)

return WTildeInterferometer(
w_tilde = WTildeInterferometer(
w_matrix=w_matrix,
curvature_preload=curvature_preload,
dirty_image=np.array(dirty_image.array),
real_space_mask=self.real_space_mask,
noise_map_value=self.noise_map[0],
)

return Interferometer(
real_space_mask=self.real_space_mask,
data=self.data,
noise_map=self.noise_map,
uv_wavelengths=self.uv_wavelengths,
transformer_class=lambda uv_wavelengths, real_space_mask, preload_transform: self.transformer,
dft_preload_transform=self.dft_preload_transform,
w_tilde=w_tilde,
)

@property
Expand Down
6 changes: 1 addition & 5 deletions autoarray/dataset/interferometer/w_tilde.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ def __init__(
curvature_preload: np.ndarray,
dirty_image: np.ndarray,
real_space_mask: Mask2D,
noise_map_value: float,
):
"""
Packages together all derived data quantities necessary to fit `Interferometer` data using an ` Inversion` via
Expand All @@ -35,12 +34,9 @@ def __init__(
real_space_mask
The 2D mask in real-space defining the area where the interferometer data's visibilities are observing
a signal.
noise_map_value
The first value of the noise-map used to construct the curvature preload, which is used as a sanity
check when performing the inversion to ensure the preload corresponds to the data being fitted.
"""
super().__init__(
curvature_preload=curvature_preload, noise_map_value=noise_map_value
curvature_preload=curvature_preload,
)

self.dirty_image = dirty_image
Expand Down
56 changes: 25 additions & 31 deletions autoarray/inversion/inversion/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,20 @@ def inversion_imaging_from(
-------
An `Inversion` whose type is determined by the input `dataset` and `settings`.
"""

use_w_tilde = True

if all(
isinstance(linear_obj, AbstractLinearObjFuncList)
for linear_obj in linear_obj_list
):
use_w_tilde = False
else:
use_w_tilde = settings.use_w_tilde

if not settings.use_w_tilde:
use_w_tilde = False

if use_w_tilde:
w_tilde = dataset.w_tilde
if dataset.w_tilde is not None and use_w_tilde:

return InversionImagingWTilde(
dataset=dataset,
w_tilde=w_tilde,
w_tilde=dataset.w_tilde,
linear_obj_list=linear_obj_list,
settings=settings,
xp=xp,
Expand Down Expand Up @@ -179,30 +176,27 @@ def inversion_interferometer_from(
-------
An `Inversion` whose type is determined by the input `dataset` and `settings`.
"""
if any(
use_w_tilde = True

if all(
isinstance(linear_obj, AbstractLinearObjFuncList)
for linear_obj in linear_obj_list
):
use_w_tilde = False
else:
use_w_tilde = settings.use_w_tilde

if not settings.use_linear_operators:
if use_w_tilde:
w_tilde = dataset.w_tilde

return InversionInterferometerWTilde(
dataset=dataset,
w_tilde=w_tilde,
linear_obj_list=linear_obj_list,
settings=settings,
xp=xp,
)

else:
return InversionInterferometerMapping(
dataset=dataset,
linear_obj_list=linear_obj_list,
settings=settings,
xp=xp,
)

if dataset.w_tilde is not None and use_w_tilde:

return InversionInterferometerWTilde(
dataset=dataset,
w_tilde=dataset.w_tilde,
linear_obj_list=linear_obj_list,
settings=settings,
xp=xp,
)

return InversionInterferometerMapping(
dataset=dataset,
linear_obj_list=linear_obj_list,
settings=settings,
xp=xp,
)
Loading
Loading