diff --git a/autoarray/__init__.py b/autoarray/__init__.py index baae4010f..bff30a659 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -1,9 +1,13 @@ +from autoconf.dictable import register_parser +from autofit import conf + +conf.instance.register(__file__) + from . import exc from . import type from . import util from . import fixtures from . import mock as m -from .numba_util import profile_func from .dataset import preprocess from .dataset.abstract.dataset import AbstractDataset from .dataset.abstract.w_tilde import AbstractWTilde diff --git a/autoarray/config/general.yaml b/autoarray/config/general.yaml index a6cb8d5dc..7b9112e81 100644 --- a/autoarray/config/general.yaml +++ b/autoarray/config/general.yaml @@ -1,3 +1,5 @@ +jax: + use_jax: true # If True, uses JAX internally, whereas False uses normal Numpy. fits: flip_for_ds9: false # If True, the image is flipped before output to a .fits file, which is useful for viewing in DS9. inversion: diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index c460bf820..b46abeb79 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -9,6 +9,8 @@ from autoarray.inversion.pixelization.border_relocator import BorderRelocator from autoconf import cached_property +from autoarray import exc + class GridsDataset: def __init__( @@ -24,7 +26,7 @@ def __init__( The following grids are contained: - - `uniform`: A grids of (y,x) coordinates which aligns with the centre of every image pixel of the image data, + - `lp`: A grids of (y,x) coordinates which aligns with the centre of every image pixel of the image data, which is used for most normal calculations (e.g. evaluating the amount of light that falls in an pixel from a light profile). @@ -60,72 +62,30 @@ def __init__( self.over_sample_size_pixelization = over_sample_size_pixelization self.psf = psf - @cached_property - def lp(self) -> Union[Grid1D, Grid2D]: - """ - Returns the grid of (y,x) Cartesian coordinates at the centre of every pixel in the masked data, which is used - to perform most normal calculations (e.g. evaluating the amount of light that falls in an pixel from a light - profile). - - This grid is computed based on the mask, in particular its pixel-scale and sub-grid size. - - Returns - ------- - The (y,x) coordinates of every pixel in the data. - """ - return Grid2D.from_mask( + self.lp = Grid2D.from_mask( mask=self.mask, over_sample_size=self.over_sample_size_lp, ) + self.lp.over_sampled - @cached_property - def pixelization(self) -> Grid2D: - """ - Returns the grid of (y,x) Cartesian coordinates of every pixel in the masked data which is used - specifically for calculations associated with a pixelization. - - The `pixelization` grid is identical to the `uniform` grid but often uses a different over sampling scheme - when performing calculations. For example, the pixelization may benefit from using a a higher `sub_size` than - the `uniform` grid, in order to better prevent aliasing effects. - - This grid is computed based on the mask, in particular its pixel-scale and sub-grid size. - - Returns - ------- - The (y,x) coordinates of every pixel in the data, used for pixelization / inversion calculations. - """ - return Grid2D.from_mask( + self.pixelization = Grid2D.from_mask( mask=self.mask, over_sample_size=self.over_sample_size_pixelization, ) - - @cached_property - def blurring(self) -> Optional[Grid2D]: - """ - Returns a blurring-grid from a mask and the 2D shape of the PSF kernel. - - A blurring grid consists of all pixels that are masked (and therefore have their values set to (0.0, 0.0)), - but are close enough to the unmasked pixels that their values will be convolved into the unmasked those pixels. - This when computing images from light profile objects. - - This uses lazy allocation such that the calculation is only performed when the blurring grid is used, ensuring - efficient set up of the `Imaging` class. - - Returns - ------- - The blurring grid given the mask of the imaging data. - """ + self.pixelization.over_sampled if self.psf is None: - return None - - return self.lp.blurring_grid_via_kernel_shape_from( - kernel_shape_native=self.psf.shape_native, - ) - - @cached_property - def border_relocator(self) -> BorderRelocator: - return BorderRelocator( + self.blurring = None + else: + try: + self.blurring = self.lp.blurring_grid_via_kernel_shape_from( + kernel_shape_native=self.psf.shape_native, + ) + self.blurring.over_sampled + except exc.MaskException: + self.blurring = None + + self.border_relocator = BorderRelocator( mask=self.mask, sub_size=self.over_sample_size_pixelization ) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 3779a30d6..75e3c042d 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -170,9 +170,7 @@ def __init__( if psf.mask.shape[0] % 2 == 0 or psf.mask.shape[1] % 2 == 0: raise exc.KernelException("Kernel2D Kernel2D must be odd") - @cached_property - def grids(self): - return GridsDataset( + self.grids = GridsDataset( mask=self.data.mask, over_sample_size_lp=self.over_sample_size_lp, over_sample_size_pixelization=self.over_sample_size_pixelization, @@ -511,7 +509,7 @@ def apply_over_sampling( passed into the calculations performed in the `inversion` module. """ - return Imaging( + dataset = Imaging( data=self.data, noise_map=self.noise_map, psf=self.psf, @@ -522,6 +520,8 @@ def apply_over_sampling( check_noise_map=False, ) + return dataset + def output_to_fits( self, data_path: Union[Path, str], diff --git a/autoarray/dataset/interferometer/dataset.py b/autoarray/dataset/interferometer/dataset.py index 0a2d5bbdb..647d05852 100644 --- a/autoarray/dataset/interferometer/dataset.py +++ b/autoarray/dataset/interferometer/dataset.py @@ -101,9 +101,7 @@ def __init__( else None ) - @cached_property - def grids(self): - return GridsDataset( + 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, diff --git a/autoarray/exc.py b/autoarray/exc.py index eed76b04e..3929820eb 100644 --- a/autoarray/exc.py +++ b/autoarray/exc.py @@ -104,11 +104,3 @@ class PlottingException(Exception): """ pass - - -class ProfilingException(Exception): - """ - Raises exceptions associated with in-built profiling tools (e.g. the `profile_func` decorator). - """ - - pass diff --git a/autoarray/fit/fit_dataset.py b/autoarray/fit/fit_dataset.py index a21b98885..bd63a8274 100644 --- a/autoarray/fit/fit_dataset.py +++ b/autoarray/fit/fit_dataset.py @@ -13,7 +13,6 @@ from autoarray.inversion.inversion.abstract import AbstractInversion from autoarray.mask.mask_2d import Mask2D -from autoarray.numba_util import profile_func from autoarray import type as ty @@ -116,7 +115,6 @@ def __init__( dataset, use_mask_in_fit: bool = False, dataset_model: DatasetModel = None, - run_time_dict: Optional[Dict] = None, ): """Class to fit a masked dataset where the dataset's data structures are any dimension. @@ -149,7 +147,6 @@ def __init__( self.dataset = dataset self.use_mask_in_fit = use_mask_in_fit self.dataset_model = dataset_model or DatasetModel() - self.run_time_dict = run_time_dict @property def mask(self) -> Mask2D: @@ -320,7 +317,6 @@ def log_evidence(self) -> float: ) @property - @profile_func def figure_of_merit(self) -> float: if self.inversion is not None: return self.log_evidence diff --git a/autoarray/fit/fit_imaging.py b/autoarray/fit/fit_imaging.py index aa55b4e35..a8b1f2297 100644 --- a/autoarray/fit/fit_imaging.py +++ b/autoarray/fit/fit_imaging.py @@ -14,7 +14,6 @@ def __init__( dataset: Imaging, use_mask_in_fit: bool = False, dataset_model: DatasetModel = None, - run_time_dict: Optional[Dict] = None, ): """ Class to fit a masked imaging dataset. @@ -50,7 +49,6 @@ def __init__( dataset=dataset, use_mask_in_fit=use_mask_in_fit, dataset_model=dataset_model, - run_time_dict=run_time_dict, ) @property diff --git a/autoarray/fit/fit_interferometer.py b/autoarray/fit/fit_interferometer.py index 40a713bc4..ee7a534d4 100644 --- a/autoarray/fit/fit_interferometer.py +++ b/autoarray/fit/fit_interferometer.py @@ -18,7 +18,6 @@ def __init__( dataset: Interferometer, dataset_model: DatasetModel = None, use_mask_in_fit: bool = False, - run_time_dict: Optional[Dict] = None, ): """ Class to fit a masked interferometer dataset. @@ -59,7 +58,6 @@ def __init__( dataset=dataset, dataset_model=dataset_model, use_mask_in_fit=use_mask_in_fit, - run_time_dict=run_time_dict, ) @property diff --git a/autoarray/fit/mock/mock_fit_imaging.py b/autoarray/fit/mock/mock_fit_imaging.py index 181d3d56e..ff36797d5 100644 --- a/autoarray/fit/mock/mock_fit_imaging.py +++ b/autoarray/fit/mock/mock_fit_imaging.py @@ -15,13 +15,11 @@ def __init__( model_data=None, inversion=None, blurred_image=None, - run_time_dict: Optional[Dict] = None, ): super().__init__( dataset=dataset or MockDataset(), dataset_model=dataset_model, use_mask_in_fit=use_mask_in_fit, - run_time_dict=run_time_dict, ) self._noise_map = noise_map diff --git a/autoarray/geometry/geometry_util.py b/autoarray/geometry/geometry_util.py index 4cf32a082..16ee5c460 100644 --- a/autoarray/geometry/geometry_util.py +++ b/autoarray/geometry/geometry_util.py @@ -2,8 +2,6 @@ import numpy as np from typing import Tuple, Union - -from autoarray import numba_util from autoarray import type as ty @@ -178,70 +176,6 @@ def convert_pixel_scales_2d(pixel_scales: ty.PixelScales) -> Tuple[float, float] return pixel_scales -@numba_util.jit() -def central_pixel_coordinates_2d_numba_from( - shape_native: Tuple[int, int], -) -> Tuple[float, float]: - """ - Returns the central pixel coordinates of a 2D geometry (and therefore a 2D data structure like an ``Array2D``) - from the shape of that data structure. - - Examples of the central pixels are as follows: - - - For a 3x3 image, the central pixel is pixel [1, 1]. - - For a 4x4 image, the central pixel is [1.5, 1.5]. - - Parameters - ---------- - shape_native - The dimensions of the data structure, which can be in 1D, 2D or higher dimensions. - - Returns - ------- - The central pixel coordinates of the data structure. - """ - return (float(shape_native[0] - 1) / 2, float(shape_native[1] - 1) / 2) - - -@numba_util.jit() -def central_scaled_coordinate_2d_numba_from( - shape_native: Tuple[int, int], - pixel_scales: ty.PixelScales, - origin: Tuple[float, float] = (0.0, 0.0), -) -> Tuple[float, float]: - """ - Returns the central scaled coordinates of a 2D geometry (and therefore a 2D data structure like an ``Array2D``) - from the shape of that data structure. - - This is computed by using the data structure's shape and converting it to scaled units using an input - pixel-coordinates to scaled-coordinate conversion factor `pixel_scales`. - - The origin of the scaled grid can also be input and moved from (0.0, 0.0). - - Parameters - ---------- - shape_native - The 2D shape of the data structure whose central scaled coordinates are computed. - pixel_scales - The (y,x) scaled units to pixel units conversion factor of the 2D data structure. - origin - The (y,x) scaled units origin of the coordinate system the central scaled coordinate is computed on. - - Returns - ------- - The central coordinates of the 2D data structure in scaled units. - """ - - central_pixel_coordinates = central_pixel_coordinates_2d_numba_from( - shape_native=shape_native - ) - - y_pixel = central_pixel_coordinates[0] + (origin[0] / pixel_scales[0]) - x_pixel = central_pixel_coordinates[1] - (origin[1] / pixel_scales[1]) - - return (y_pixel, x_pixel) - - def central_pixel_coordinates_2d_from( shape_native: Tuple[int, int], ) -> Tuple[float, float]: @@ -294,7 +228,7 @@ def central_scaled_coordinate_2d_from( The central coordinates of the 2D data structure in scaled units. """ - central_pixel_coordinates = central_pixel_coordinates_2d_numba_from( + central_pixel_coordinates = central_pixel_coordinates_2d_from( shape_native=shape_native ) @@ -367,7 +301,6 @@ def pixel_coordinates_2d_from( return (y_pixel, x_pixel) -@numba_util.jit() def scaled_coordinates_2d_from( pixel_coordinates_2d: Tuple[float, float], shape_native: Tuple[int, int], @@ -411,7 +344,7 @@ def scaled_coordinates_2d_from( origin=(0.0, 0.0) ) """ - central_scaled_coordinates = central_scaled_coordinate_2d_numba_from( + central_scaled_coordinates = central_scaled_coordinate_2d_from( shape_native=shape_native, pixel_scales=pixel_scales, origin=origins ) diff --git a/autoarray/inversion/inversion/abstract.py b/autoarray/inversion/inversion/abstract.py index bec0b1ce2..b321cfdb2 100644 --- a/autoarray/inversion/inversion/abstract.py +++ b/autoarray/inversion/inversion/abstract.py @@ -8,7 +8,6 @@ from typing import Dict, List, Optional, Type, Union from autoconf import cached_property -from autoarray.numba_util import profile_func from autoarray.dataset.imaging.dataset import Imaging from autoarray.dataset.interferometer.dataset import Interferometer @@ -32,7 +31,6 @@ def __init__( dataset: Union[Imaging, Interferometer, DatasetInterface], linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ An `Inversion` reconstructs an input dataset using a list of linear objects (e.g. a list of analytic functions @@ -70,18 +68,25 @@ def __init__( input dataset's data and whose values are solved for via the inversion. settings Settings controlling how an inversion is fitted for example which linear algebra formalism is used. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ + try: + import numba + except ModuleNotFoundError: + raise exc.InversionException( + "Inversion functionality (linear light profiles, 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" + ) + self.dataset = dataset self.linear_obj_list = linear_obj_list self.settings = settings - self.run_time_dict = run_time_dict - @property def data(self): return self.dataset.data @@ -271,7 +276,6 @@ def mask(self) -> Array2D: return self.data.mask @cached_property - @profile_func def mapping_matrix(self) -> np.ndarray: """ The `mapping_matrix` of a linear object describes the mappings between the observed data's data-points / pixels @@ -296,7 +300,6 @@ def operated_mapping_matrix_list(self) -> np.ndarray: raise NotImplementedError @cached_property - @profile_func def operated_mapping_matrix(self) -> np.ndarray: """ The `operated_mapping_matrix` of a linear object describes the mappings between the observed data's values and @@ -310,17 +313,14 @@ def operated_mapping_matrix(self) -> np.ndarray: return jnp.hstack(self.operated_mapping_matrix_list) @cached_property - @profile_func def data_vector(self) -> np.ndarray: raise NotImplementedError @cached_property - @profile_func def curvature_matrix(self) -> np.ndarray: raise NotImplementedError @cached_property - @profile_func def regularization_matrix(self) -> Optional[np.ndarray]: """ The regularization matrix H is used to impose smoothness on our inversion's reconstruction. This enters the @@ -342,7 +342,6 @@ def regularization_matrix(self) -> Optional[np.ndarray]: ) @cached_property - @profile_func def regularization_matrix_reduced(self) -> Optional[np.ndarray]: """ The regularization matrix H is used to impose smoothness on our inversion's reconstruction. This enters the @@ -372,7 +371,6 @@ def regularization_matrix_reduced(self) -> Optional[np.ndarray]: return regularization_matrix @cached_property - @profile_func def curvature_reg_matrix(self) -> np.ndarray: """ The linear system of equations solves for F + regularization_coefficient*H, which is computed below. @@ -396,7 +394,6 @@ def curvature_reg_matrix(self) -> np.ndarray: return np.add(self.curvature_matrix, self.regularization_matrix) @cached_property - @profile_func def curvature_reg_matrix_reduced(self) -> np.ndarray: """ The linear system of equations solves for F + regularization_coefficient*H, which is computed below. @@ -437,7 +434,6 @@ def mapper_zero_pixel_list(self) -> np.ndarray: return mapper_zero_pixel_list @cached_property - @profile_func def reconstruction(self) -> np.ndarray: """ Solve the linear system [F + reg_coeff*H] S = D -> S = [F + reg_coeff*H]^-1 D given by equation (12) @@ -517,7 +513,6 @@ def reconstruction(self) -> np.ndarray: ) @cached_property - @profile_func def reconstruction_reduced(self) -> np.ndarray: """ Solve the linear system [F + reg_coeff*H] S = D -> S = [F + reg_coeff*H]^-1 D given by equation (12) @@ -574,7 +569,6 @@ def source_quantity_dict_from( return source_quantity_dict @property - @profile_func def mapped_reconstructed_data_dict(self) -> Dict[LinearObj, Array2D]: raise NotImplementedError @@ -595,7 +589,6 @@ def mapped_reconstructed_image_dict(self) -> Dict[LinearObj, Array2D]: return self.mapped_reconstructed_data_dict @cached_property - @profile_func def mapped_reconstructed_data(self) -> Union[Array2D, Visibilities]: """ Using the reconstructed source pixel fluxes we map each source pixel flux back to the image plane and @@ -656,7 +649,6 @@ def data_subtracted_dict(self) -> Dict[LinearObj, Array2D]: return data_subtracted_dict @cached_property - @profile_func def regularization_term(self) -> float: """ Returns the regularization term of an inversion. This term represents the sum of the difference in flux @@ -681,7 +673,6 @@ def regularization_term(self) -> float: ) @cached_property - @profile_func def log_det_curvature_reg_matrix_term(self) -> float: """ The log determinant of [F + reg_coeff*H] is used to determine the Bayesian evidence of the solution. @@ -699,7 +690,6 @@ def log_det_curvature_reg_matrix_term(self) -> float: raise exc.InversionException() from e @cached_property - @profile_func def log_det_regularization_matrix_term(self) -> float: """ The Bayesian evidence of an inversion which quantifies its overall goodness-of-fit uses the log determinant @@ -810,12 +800,10 @@ def regularization_weights_mapper_dict(self) -> Dict[LinearObj, np.ndarray]: return regularization_weights_dict @property - @profile_func def _data_vector_mapper(self) -> np.ndarray: raise NotImplementedError @property - @profile_func def _curvature_matrix_mapper_diag(self) -> Optional[np.ndarray]: raise NotImplementedError diff --git a/autoarray/inversion/inversion/factory.py b/autoarray/inversion/inversion/factory.py index 350f19e65..1e14d1e10 100644 --- a/autoarray/inversion/inversion/factory.py +++ b/autoarray/inversion/inversion/factory.py @@ -21,7 +21,6 @@ def inversion_from( dataset: Union[Imaging, Interferometer, DatasetInterface], linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Factory which given an input dataset and list of linear objects, creates an `Inversion`. @@ -46,8 +45,6 @@ def inversion_from( input dataset's data and whose values are solved for via the inversion. settings Settings controlling how an inversion is fitted for example which linear algebra formalism is used. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. Returns ------- @@ -58,14 +55,12 @@ def inversion_from( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) return inversion_interferometer_from( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) @@ -73,7 +68,6 @@ def inversion_imaging_from( dataset, linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Factory which given an input `Imaging` dataset and list of linear objects, creates an `InversionImaging`. @@ -102,8 +96,6 @@ def inversion_imaging_from( input dataset's data and whose values are solved for via the inversion. settings Settings controlling how an inversion is fitted for example which linear algebra formalism is used. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. Returns ------- @@ -128,14 +120,12 @@ def inversion_imaging_from( w_tilde=w_tilde, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) return InversionImagingMapping( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) @@ -143,7 +133,6 @@ def inversion_interferometer_from( dataset: Union[Interferometer, DatasetInterface], linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Factory which given an input `Interferometer` dataset and list of linear objects, creates @@ -175,8 +164,6 @@ def inversion_interferometer_from( input dataset's data and whose values are solved for via the inversion. settings Settings controlling how an inversion is fitted for example which linear algebra formalism is used. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. Returns ------- @@ -199,7 +186,6 @@ def inversion_interferometer_from( w_tilde=w_tilde, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) else: @@ -207,5 +193,4 @@ def inversion_interferometer_from( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) diff --git a/autoarray/inversion/inversion/imaging/abstract.py b/autoarray/inversion/inversion/imaging/abstract.py index f17e8fa4f..09bab7dc3 100644 --- a/autoarray/inversion/inversion/imaging/abstract.py +++ b/autoarray/inversion/inversion/imaging/abstract.py @@ -3,8 +3,6 @@ from autoconf import cached_property -from autoarray.numba_util import profile_func - from autoarray.dataset.imaging.dataset import Imaging from autoarray.inversion.inversion.dataset_interface import DatasetInterface from autoarray.inversion.linear_obj.func_list import AbstractLinearObjFuncList @@ -22,7 +20,6 @@ def __init__( dataset: Union[Imaging, DatasetInterface], linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ An `Inversion` reconstructs an input dataset using a list of linear objects (e.g. a list of analytic functions @@ -63,15 +60,12 @@ def __init__( input dataset's data and whose values are solved for via the inversion. settings Settings controlling how an inversion is fitted for example which linear algebra formalism is used. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ super().__init__( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) @property @@ -116,7 +110,6 @@ def _updated_cls_key_dict_from(self, cls: Type, preload_dict: Dict) -> Dict: return cls_dict @cached_property - @profile_func def linear_func_operated_mapping_matrix_dict(self) -> Dict: """ The `operated_mapping_matrix` of a linear object describes the mappings between the observed data's values and @@ -202,7 +195,6 @@ def data_linear_func_matrix_dict(self): return data_linear_func_matrix_dict @cached_property - @profile_func def mapper_operated_mapping_matrix_dict(self) -> Dict: """ The `operated_mapping_matrix` of a `Mapper` object describes the mappings between the observed data's values diff --git a/autoarray/inversion/inversion/imaging/mapping.py b/autoarray/inversion/inversion/imaging/mapping.py index 2ec0df160..9af3faa28 100644 --- a/autoarray/inversion/inversion/imaging/mapping.py +++ b/autoarray/inversion/inversion/imaging/mapping.py @@ -1,11 +1,8 @@ -import copy import numpy as np from typing import Dict, List, Optional, Union from autoconf import cached_property -from autoarray.numba_util import profile_func - from autoarray.dataset.imaging.dataset import Imaging from autoarray.inversion.inversion.dataset_interface import DatasetInterface from autoarray.inversion.inversion.imaging.abstract import AbstractInversionImaging @@ -24,7 +21,6 @@ def __init__( dataset: Union[Imaging, DatasetInterface], linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Constructs linear equations (via vectors and matrices) which allow for sets of simultaneous linear equations @@ -44,19 +40,15 @@ def __init__( linear_obj_list The linear objects used to reconstruct the data's observed values. If multiple linear objects are passed the simultaneous linear equations are combined and solved simultaneously. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ super().__init__( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) @property - @profile_func def _data_vector_mapper(self) -> np.ndarray: """ Returns the `data_vector` of all mappers, a 1D vector whose values are solved for by the simultaneous @@ -95,7 +87,6 @@ def _data_vector_mapper(self) -> np.ndarray: return data_vector @cached_property - @profile_func def data_vector(self) -> np.ndarray: """ The `data_vector` is a 1D vector whose values are solved for by the simultaneous linear equations constructed @@ -116,7 +107,6 @@ def data_vector(self) -> np.ndarray: ) @property - @profile_func def _curvature_matrix_mapper_diag(self) -> Optional[np.ndarray]: """ Returns the diagonal regions of the `curvature_matrix`, a 2D matrix which uses the mappings between the data @@ -163,7 +153,6 @@ def _curvature_matrix_mapper_diag(self) -> Optional[np.ndarray]: return curvature_matrix @cached_property - @profile_func def curvature_matrix(self): """ The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to @@ -191,7 +180,6 @@ def curvature_matrix(self): ) @property - @profile_func def mapped_reconstructed_data_dict(self) -> Dict[LinearObj, Array2D]: """ When constructing the simultaneous linear equations (via vectors and matrices) the quantities of each individual diff --git a/autoarray/inversion/inversion/imaging/w_tilde.py b/autoarray/inversion/inversion/imaging/w_tilde.py index 97fc9eb8f..ca04b9fc3 100644 --- a/autoarray/inversion/inversion/imaging/w_tilde.py +++ b/autoarray/inversion/inversion/imaging/w_tilde.py @@ -4,8 +4,6 @@ from autoconf import cached_property -from autoarray.numba_util import profile_func - from autoarray.dataset.imaging.dataset import Imaging from autoarray.dataset.imaging.w_tilde import WTildeImaging from autoarray.inversion.inversion.dataset_interface import DatasetInterface @@ -27,7 +25,6 @@ def __init__( w_tilde: WTildeImaging, linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Constructs linear equations (via vectors and matrices) which allow for sets of simultaneous linear equations @@ -50,15 +47,12 @@ def __init__( linear_obj_list The linear objects used to reconstruct the data's observed values. If multiple linear objects are passed the simultaneous linear equations are combined and solved simultaneously. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ super().__init__( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) if self.settings.use_w_tilde: @@ -68,7 +62,6 @@ def __init__( self.w_tilde = None @cached_property - @profile_func def w_tilde_data(self): return inversion_imaging_util.w_tilde_data_imaging_from( image_native=np.array(self.data.native.array).astype("float"), @@ -80,7 +73,6 @@ def w_tilde_data(self): ) @property - @profile_func def _data_vector_mapper(self) -> np.ndarray: """ Returns the `data_vector` of all mappers, a 1D vector whose values are solved for by the simultaneous @@ -115,7 +107,6 @@ def _data_vector_mapper(self) -> np.ndarray: return data_vector @cached_property - @profile_func def data_vector(self) -> np.ndarray: """ Returns the `data_vector`, a 1D vector whose values are solved for by the simultaneous linear equations @@ -136,7 +127,6 @@ def data_vector(self) -> np.ndarray: return self._data_vector_multi_mapper @property - @profile_func def _data_vector_x1_mapper(self) -> np.ndarray: """ Returns the `data_vector`, a 1D vector whose values are solved for by the simultaneous linear equations @@ -157,7 +147,6 @@ def _data_vector_x1_mapper(self) -> np.ndarray: ) @property - @profile_func def _data_vector_multi_mapper(self) -> np.ndarray: """ Returns the `data_vector`, a 1D vector whose values are solved for by the simultaneous linear equations @@ -181,7 +170,6 @@ def _data_vector_multi_mapper(self) -> np.ndarray: ) @property - @profile_func def _data_vector_func_list_and_mapper(self) -> np.ndarray: """ Returns the `data_vector`, a 1D vector whose values are solved for by the simultaneous linear equations @@ -220,7 +208,6 @@ def _data_vector_func_list_and_mapper(self) -> np.ndarray: return data_vector @cached_property - @profile_func def curvature_matrix(self) -> np.ndarray: """ Returns the `curvature_matrix`, a 2D matrix which uses the mappings between the data and the linear objects to @@ -263,7 +250,6 @@ def curvature_matrix(self) -> np.ndarray: return curvature_matrix @property - @profile_func def _curvature_matrix_mapper_diag(self) -> Optional[np.ndarray]: """ Returns the diagonal regions of the `curvature_matrix`, a 2D matrix which uses the mappings between the data @@ -306,7 +292,6 @@ def _curvature_matrix_mapper_diag(self) -> Optional[np.ndarray]: return curvature_matrix - @profile_func def _curvature_matrix_off_diag_from( self, mapper_0: AbstractMapper, mapper_1: AbstractMapper ) -> np.ndarray: @@ -351,7 +336,6 @@ def _curvature_matrix_off_diag_from( return curvature_matrix_off_diag_0 + curvature_matrix_off_diag_1.T @property - @profile_func def _curvature_matrix_x1_mapper(self) -> np.ndarray: """ Returns the `curvature_matrix`, a 2D matrix which uses the mappings between the data and the linear objects to @@ -363,7 +347,6 @@ def _curvature_matrix_x1_mapper(self) -> np.ndarray: return self._curvature_matrix_mapper_diag @property - @profile_func def _curvature_matrix_multi_mapper(self) -> np.ndarray: """ Returns the `curvature_matrix`, a 2D matrix which uses the mappings between the data and the linear objects to @@ -401,7 +384,6 @@ def _curvature_matrix_multi_mapper(self) -> np.ndarray: return curvature_matrix @property - @profile_func def _curvature_matrix_func_list_and_mapper(self) -> np.ndarray: """ The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to @@ -480,7 +462,6 @@ def _curvature_matrix_func_list_and_mapper(self) -> np.ndarray: return curvature_matrix @property - @profile_func def mapped_reconstructed_data_dict(self) -> Dict[LinearObj, Array2D]: """ When constructing the simultaneous linear equations (via vectors and matrices) the quantities of each individual diff --git a/autoarray/inversion/inversion/interferometer/abstract.py b/autoarray/inversion/inversion/interferometer/abstract.py index 47e1c84bf..09e2a01e7 100644 --- a/autoarray/inversion/inversion/interferometer/abstract.py +++ b/autoarray/inversion/inversion/interferometer/abstract.py @@ -11,8 +11,6 @@ from autoarray.inversion.inversion import inversion_util -from autoarray.numba_util import profile_func - class AbstractInversionInterferometer(AbstractInversion): def __init__( @@ -20,7 +18,6 @@ def __init__( dataset: Union[Interferometer, DatasetInterface], linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Constructs linear equations (via vectors and matrices) which allow for sets of simultaneous linear equations @@ -41,15 +38,12 @@ def __init__( linear_obj_list The linear objects used to reconstruct the data's observed values. If multiple linear objects are passed the simultaneous linear equations are combined and solved simultaneously. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ super().__init__( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) @property @@ -78,7 +72,6 @@ def operated_mapping_matrix_list(self) -> List[np.ndarray]: ] @property - @profile_func def mapped_reconstructed_image_dict( self, ) -> Dict[LinearObj, Array2D]: diff --git a/autoarray/inversion/inversion/interferometer/mapping.py b/autoarray/inversion/inversion/interferometer/mapping.py index 77ec2576c..2a4e4f316 100644 --- a/autoarray/inversion/inversion/interferometer/mapping.py +++ b/autoarray/inversion/inversion/interferometer/mapping.py @@ -16,8 +16,6 @@ from autoarray.inversion.inversion.interferometer import inversion_interferometer_util from autoarray.inversion.inversion import inversion_util -from autoarray.numba_util import profile_func - class InversionInterferometerMapping(AbstractInversionInterferometer): def __init__( @@ -25,7 +23,6 @@ def __init__( dataset: Union[Interferometer, DatasetInterface], linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Constructs linear equations (via vectors and matrices) which allow for sets of simultaneous linear equations @@ -49,19 +46,15 @@ def __init__( linear_obj_list The linear objects used to reconstruct the data's observed values. If multiple linear objects are passed the simultaneous linear equations are combined and solved simultaneously. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ super().__init__( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) @cached_property - @profile_func def data_vector(self) -> np.ndarray: """ The `data_vector` is a 1D vector whose values are solved for by the simultaneous linear equations constructed @@ -83,7 +76,6 @@ def data_vector(self) -> np.ndarray: ) @cached_property - @profile_func def curvature_matrix(self) -> np.ndarray: """ The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to @@ -119,7 +111,6 @@ def curvature_matrix(self) -> np.ndarray: return curvature_matrix @property - @profile_func def mapped_reconstructed_data_dict( self, ) -> Dict[LinearObj, Visibilities]: diff --git a/autoarray/inversion/inversion/interferometer/w_tilde.py b/autoarray/inversion/inversion/interferometer/w_tilde.py index e538e8779..bac21b883 100644 --- a/autoarray/inversion/inversion/interferometer/w_tilde.py +++ b/autoarray/inversion/inversion/interferometer/w_tilde.py @@ -17,8 +17,6 @@ from autoarray.inversion.inversion import inversion_util from autoarray.inversion.inversion.interferometer import inversion_interferometer_util -from autoarray.numba_util import profile_func - class InversionInterferometerWTilde(AbstractInversionInterferometer): def __init__( @@ -27,7 +25,6 @@ def __init__( w_tilde: WTildeInterferometer, linear_obj_list: List[LinearObj], settings: SettingsInversion = SettingsInversion(), - run_time_dict: Optional[Dict] = None, ): """ Constructs linear equations (via vectors and matrices) which allow for sets of simultaneous linear equations @@ -54,8 +51,6 @@ def __init__( linear_obj_list The linear objects used to reconstruct the data's observed values. If multiple linear objects are passed the simultaneous linear equations are combined and solved simultaneously. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ self.w_tilde = w_tilde @@ -65,13 +60,11 @@ def __init__( dataset=dataset, linear_obj_list=linear_obj_list, settings=settings, - run_time_dict=run_time_dict, ) self.settings = settings @cached_property - @profile_func def data_vector(self) -> np.ndarray: """ The `data_vector` is a 1D vector whose values are solved for by the simultaneous linear equations constructed @@ -88,7 +81,6 @@ def data_vector(self) -> np.ndarray: return np.dot(self.mapping_matrix.T, self.w_tilde.dirty_image) @cached_property - @profile_func def curvature_matrix(self) -> np.ndarray: """ The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to @@ -104,7 +96,6 @@ def curvature_matrix(self) -> np.ndarray: return self.curvature_matrix_diag @property - @profile_func def curvature_matrix_diag(self) -> np.ndarray: """ The `curvature_matrix` is a 2D matrix which uses the mappings between the data and the linear objects to @@ -153,7 +144,6 @@ def curvature_matrix_diag(self) -> np.ndarray: ) @property - @profile_func def mapped_reconstructed_data_dict( self, ) -> Dict[LinearObj, Visibilities]: diff --git a/autoarray/inversion/linear_obj/func_list.py b/autoarray/inversion/linear_obj/func_list.py index ce72bdcbe..9f30f3c60 100644 --- a/autoarray/inversion/linear_obj/func_list.py +++ b/autoarray/inversion/linear_obj/func_list.py @@ -9,15 +9,12 @@ from autoarray.inversion.regularization.abstract import AbstractRegularization from autoarray.type import Grid1D2DLike -from autoarray.numba_util import profile_func - class AbstractLinearObjFuncList(LinearObj): def __init__( self, grid: Grid1D2DLike, regularization: Optional[AbstractRegularization], - run_time_dict: Optional[Dict] = None, ): """ A linear object which reconstructs a dataset based on mapping between the data points of that dataset and @@ -42,11 +39,9 @@ def __init__( is evaluated. regularization The regularization scheme which may be applied to this linear object in order to smooth its solution. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ - super().__init__(regularization=regularization, run_time_dict=run_time_dict) + super().__init__(regularization=regularization) self.grid = grid @@ -83,7 +78,6 @@ def neighbors(self) -> Neighbors: ) @cached_property - @profile_func def unique_mappings(self) -> UniqueMappings: """ Returns the unique mappings of every unmasked data pixel's (e.g. `grid_slim`) sub-pixels (e.g. `grid_sub_slim`) diff --git a/autoarray/inversion/linear_obj/linear_obj.py b/autoarray/inversion/linear_obj/linear_obj.py index 1e0bacc85..402658039 100644 --- a/autoarray/inversion/linear_obj/linear_obj.py +++ b/autoarray/inversion/linear_obj/linear_obj.py @@ -6,14 +6,11 @@ from autoarray.inversion.linear_obj.neighbors import Neighbors from autoarray.inversion.regularization.abstract import AbstractRegularization -from autoarray.numba_util import profile_func - class LinearObj: def __init__( self, regularization: Optional[AbstractRegularization], - run_time_dict: Optional[Dict] = None, ): """ A linear object which reconstructs a dataset based on mapping between the data points of that dataset and @@ -33,11 +30,8 @@ def __init__( ---------- regularization The regularization scheme which may be applied to this linear object in order to smooth its solution. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ self.regularization = regularization - self.run_time_dict = run_time_dict @property def params(self) -> int: @@ -75,7 +69,6 @@ def neighbors(self) -> Neighbors: raise NotImplementedError @cached_property - @profile_func def unique_mappings(self): """ An object describing the unique mappings between data points / pixels in the data and the parameters of the diff --git a/autoarray/inversion/mock/mock_mesh.py b/autoarray/inversion/mock/mock_mesh.py index def02657a..c8f7527a3 100644 --- a/autoarray/inversion/mock/mock_mesh.py +++ b/autoarray/inversion/mock/mock_mesh.py @@ -23,7 +23,6 @@ def mapper_grids_from( source_plane_mesh_grid: Optional[Abstract2DMesh] = None, image_plane_mesh_grid: Optional[Grid2DIrregular] = None, adapt_data: Optional[np.ndarray] = None, - run_time_dict: Optional[Dict] = None, ) -> MapperGrids: return MapperGrids( mask=mask, @@ -32,7 +31,6 @@ def mapper_grids_from( source_plane_mesh_grid=source_plane_mesh_grid, image_plane_mesh_grid=self.image_plane_mesh_grid, adapt_data=adapt_data, - run_time_dict=run_time_dict, ) def image_plane_mesh_grid_from( diff --git a/autoarray/inversion/mock/mock_pixelization.py b/autoarray/inversion/mock/mock_pixelization.py index 72ced89e5..68a74141a 100644 --- a/autoarray/inversion/mock/mock_pixelization.py +++ b/autoarray/inversion/mock/mock_pixelization.py @@ -29,7 +29,6 @@ def mapper_grids_from( image_plane_mesh_grid=None, adapt_data=None, settings=None, - run_time_dict=None, ): return self.mapper diff --git a/autoarray/inversion/pixelization/mappers/abstract.py b/autoarray/inversion/pixelization/mappers/abstract.py index 08004b564..291f5bed6 100644 --- a/autoarray/inversion/pixelization/mappers/abstract.py +++ b/autoarray/inversion/pixelization/mappers/abstract.py @@ -15,8 +15,6 @@ from autoarray.structures.grids.uniform_2d import Grid2D from autoarray.structures.mesh.abstract_2d import Abstract2DMesh - -from autoarray.numba_util import profile_func from autoarray.inversion.pixelization.mappers import mapper_util @@ -26,7 +24,6 @@ def __init__( mapper_grids: MapperGrids, regularization: Optional[AbstractRegularization], border_relocator: BorderRelocator, - run_time_dict: Optional[Dict] = None, ): """ To understand a `Mapper` one must be familiar `Mesh` objects and the `mesh` and `pixelization` packages, where @@ -83,11 +80,9 @@ def __init__( border_relocator The border relocator, which relocates coordinates outside the border of the source-plane data grid to its edge. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ - super().__init__(regularization=regularization, run_time_dict=run_time_dict) + super().__init__(regularization=regularization) self.border_relocator = border_relocator self.mapper_grids = mapper_grids @@ -213,7 +208,6 @@ def sub_slim_indexes_for_pix_index(self) -> List[List]: return sub_slim_indexes_for_pix_index @property - @profile_func def sub_slim_indexes_for_pix_index_arr( self, ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: @@ -236,7 +230,6 @@ def sub_slim_indexes_for_pix_index_arr( ) @cached_property - @profile_func def unique_mappings(self) -> UniqueMappings: """ Returns the unique mappings of every unmasked data pixel's (e.g. `grid_slim`) sub-pixels (e.g. `grid_sub_slim`) @@ -270,7 +263,6 @@ def unique_mappings(self) -> UniqueMappings: ) @cached_property - @profile_func def mapping_matrix(self) -> np.ndarray: """ The `mapping_matrix` of a linear object describes the mappings between the observed data's data-points / pixels diff --git a/autoarray/inversion/pixelization/mappers/delaunay.py b/autoarray/inversion/pixelization/mappers/delaunay.py index 737247b0b..30c9dc7b2 100644 --- a/autoarray/inversion/pixelization/mappers/delaunay.py +++ b/autoarray/inversion/pixelization/mappers/delaunay.py @@ -5,7 +5,6 @@ from autoarray.inversion.pixelization.mappers.abstract import AbstractMapper from autoarray.inversion.pixelization.mappers.abstract import PixSubWeights -from autoarray.numba_util import profile_func from autoarray.inversion.pixelization.mappers import mapper_util @@ -56,8 +55,6 @@ class MapperDelaunay(AbstractMapper): regularization The regularization scheme which may be applied to this linear object in order to smooth its solution, which for a mapper smooths neighboring pixels on the mesh. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ @property @@ -65,7 +62,6 @@ def delaunay(self): return self.source_plane_mesh_grid.delaunay @cached_property - @profile_func def pix_sub_weights(self) -> PixSubWeights: """ Computes the following three quantities describing the mappings between of every sub-pixel in the masked data diff --git a/autoarray/inversion/pixelization/mappers/factory.py b/autoarray/inversion/pixelization/mappers/factory.py index 310133202..965213b03 100644 --- a/autoarray/inversion/pixelization/mappers/factory.py +++ b/autoarray/inversion/pixelization/mappers/factory.py @@ -12,7 +12,6 @@ def mapper_from( mapper_grids: MapperGrids, regularization: Optional[AbstractRegularization], border_relocator: Optional[BorderRelocator] = None, - run_time_dict: Optional[Dict] = None, ): """ Factory which given input `MapperGrids` and `Regularization` objects creates a `Mapper`. @@ -32,8 +31,6 @@ def mapper_from( regularization The regularization scheme which may be applied to this linear object in order to smooth its solution, which for a mapper smooths neighboring pixels on the mesh. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. Returns ------- @@ -50,19 +47,16 @@ def mapper_from( mapper_grids=mapper_grids, border_relocator=border_relocator, regularization=regularization, - run_time_dict=run_time_dict, ) elif isinstance(mapper_grids.source_plane_mesh_grid, Mesh2DDelaunay): return MapperDelaunay( mapper_grids=mapper_grids, border_relocator=border_relocator, regularization=regularization, - run_time_dict=run_time_dict, ) elif isinstance(mapper_grids.source_plane_mesh_grid, Mesh2DVoronoi): return MapperVoronoi( mapper_grids=mapper_grids, border_relocator=border_relocator, regularization=regularization, - run_time_dict=run_time_dict, ) diff --git a/autoarray/inversion/pixelization/mappers/mapper_grids.py b/autoarray/inversion/pixelization/mappers/mapper_grids.py index 260266c5c..86074b043 100644 --- a/autoarray/inversion/pixelization/mappers/mapper_grids.py +++ b/autoarray/inversion/pixelization/mappers/mapper_grids.py @@ -19,7 +19,6 @@ def __init__( source_plane_mesh_grid: Optional[Abstract2DMesh] = None, image_plane_mesh_grid: Optional[Grid2DIrregular] = None, adapt_data: Optional[np.ndarray] = None, - run_time_dict: Optional[Dict] = None, ): """ Groups the different grids used by `Mesh` objects, the `mesh` package and the `pixelization` package, which @@ -55,8 +54,6 @@ def __init__( adapt_data An image which is used to determine the `image_plane_mesh_grid` and therefore adapt the distribution of pixels of the Delaunay grid to the data it discretizes. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ self.mask = mask @@ -64,7 +61,6 @@ def __init__( self.source_plane_mesh_grid = source_plane_mesh_grid self.image_plane_mesh_grid = image_plane_mesh_grid self.adapt_data = adapt_data - self.run_time_dict = run_time_dict @property def image_plane_data_grid(self): diff --git a/autoarray/inversion/pixelization/mappers/rectangular.py b/autoarray/inversion/pixelization/mappers/rectangular.py index 9a78c1b8a..357ee9956 100644 --- a/autoarray/inversion/pixelization/mappers/rectangular.py +++ b/autoarray/inversion/pixelization/mappers/rectangular.py @@ -6,7 +6,6 @@ from autoarray.inversion.pixelization.mappers.abstract import AbstractMapper from autoarray.inversion.pixelization.mappers.abstract import PixSubWeights -from autoarray.numba_util import profile_func from autoarray.geometry import geometry_util @@ -56,8 +55,6 @@ class MapperRectangular(AbstractMapper): regularization The regularization scheme which may be applied to this linear object in order to smooth its solution, which for a mapper smooths neighboring pixels on the mesh. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ @property @@ -65,7 +62,6 @@ def shape_native(self) -> Tuple[int, ...]: return self.source_plane_mesh_grid.shape_native @cached_property - @profile_func def pix_sub_weights(self) -> PixSubWeights: """ Computes the following three quantities describing the mappings between of every sub-pixel in the masked data diff --git a/autoarray/inversion/pixelization/mappers/voronoi.py b/autoarray/inversion/pixelization/mappers/voronoi.py index 1ebae8ad9..56921b2b2 100644 --- a/autoarray/inversion/pixelization/mappers/voronoi.py +++ b/autoarray/inversion/pixelization/mappers/voronoi.py @@ -7,7 +7,6 @@ from autoarray.inversion.pixelization.mappers.abstract import PixSubWeights from autoarray.structures.arrays.uniform_2d import Array2D -from autoarray.numba_util import profile_func from autoarray.inversion.pixelization.mappers import mapper_util @@ -57,8 +56,6 @@ class MapperVoronoi(AbstractMapper): regularization The regularization scheme which may be applied to this linear object in order to smooth its solution, which for a mapper smooths neighboring pixels on the mesh. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ @property @@ -86,7 +83,6 @@ def pix_sub_weights_split_cross(self) -> PixSubWeights: return PixSubWeights(mappings=mappings, sizes=sizes, weights=weights) @cached_property - @profile_func def pix_sub_weights(self) -> PixSubWeights: """ Computes the following three quantities describing the mappings between of every sub-pixel in the masked data diff --git a/autoarray/inversion/pixelization/mesh/abstract.py b/autoarray/inversion/pixelization/mesh/abstract.py index 95d3d1ce3..d23a11cd7 100644 --- a/autoarray/inversion/pixelization/mesh/abstract.py +++ b/autoarray/inversion/pixelization/mesh/abstract.py @@ -6,14 +6,11 @@ from autoarray.structures.grids.uniform_2d import Grid2D from autoarray.structures.grids.irregular_2d import Grid2DIrregular -from autoarray.numba_util import profile_func - class AbstractMesh: def __eq__(self, other): return self.__dict__ == other.__dict__ and self.__class__ is other.__class__ - @profile_func def relocated_grid_from( self, border_relocator: BorderRelocator, @@ -46,7 +43,6 @@ def relocated_grid_from( return border_relocator.relocated_grid_from(grid=source_plane_data_grid) return source_plane_data_grid - @profile_func def relocated_mesh_grid_from( self, border_relocator: Optional[BorderRelocator], @@ -95,7 +91,6 @@ def mapper_grids_from( source_plane_mesh_grid: Optional[Grid2DIrregular] = None, image_plane_mesh_grid: Optional[Grid2DIrregular] = None, adapt_data: np.ndarray = None, - run_time_dict: Optional[Dict] = None, ) -> MapperGrids: raise NotImplementedError diff --git a/autoarray/inversion/pixelization/mesh/delaunay.py b/autoarray/inversion/pixelization/mesh/delaunay.py index ca8142b38..93675f023 100644 --- a/autoarray/inversion/pixelization/mesh/delaunay.py +++ b/autoarray/inversion/pixelization/mesh/delaunay.py @@ -1,8 +1,6 @@ from autoarray.structures.mesh.delaunay_2d import Mesh2DDelaunay from autoarray.inversion.pixelization.mesh.triangulation import Triangulation -from autoarray.numba_util import profile_func - class Delaunay(Triangulation): def __init__(self): @@ -31,7 +29,6 @@ def __init__(self): """ super().__init__() - @profile_func def mesh_grid_from( self, source_plane_data_grid=None, diff --git a/autoarray/inversion/pixelization/mesh/rectangular.py b/autoarray/inversion/pixelization/mesh/rectangular.py index b1b5a7017..e95a05ec6 100644 --- a/autoarray/inversion/pixelization/mesh/rectangular.py +++ b/autoarray/inversion/pixelization/mesh/rectangular.py @@ -10,7 +10,6 @@ from autoarray.inversion.pixelization.border_relocator import BorderRelocator from autoarray import exc -from autoarray.numba_util import profile_func class Rectangular(AbstractMesh): @@ -52,8 +51,6 @@ def __init__(self, shape: Tuple[int, int] = (3, 3)): self.pixels = self.shape[0] * self.shape[1] super().__init__() - self.run_time_dict = {} - def mapper_grids_from( self, mask, @@ -62,7 +59,6 @@ def mapper_grids_from( source_plane_mesh_grid: Grid2D = None, image_plane_mesh_grid: Grid2D = None, adapt_data: np.ndarray = None, - run_time_dict: Optional[Dict] = None, ) -> MapperGrids: """ Mapper objects describe the mappings between pixels in the masked 2D data and the pixels in a pixelization, @@ -94,12 +90,8 @@ def mapper_grids_from( Not used for a rectangular pixelization. adapt_data Not used for a rectangular pixelization. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ - self.run_time_dict = run_time_dict - relocated_grid = self.relocated_grid_from( border_relocator=border_relocator, source_plane_data_grid=source_plane_data_grid, @@ -113,10 +105,8 @@ def mapper_grids_from( source_plane_mesh_grid=mesh_grid, image_plane_mesh_grid=image_plane_mesh_grid, adapt_data=adapt_data, - run_time_dict=run_time_dict, ) - @profile_func def mesh_grid_from( self, source_plane_data_grid: Optional[Grid2D] = None, diff --git a/autoarray/inversion/pixelization/mesh/triangulation.py b/autoarray/inversion/pixelization/mesh/triangulation.py index a72236e50..3e7d6cd2c 100644 --- a/autoarray/inversion/pixelization/mesh/triangulation.py +++ b/autoarray/inversion/pixelization/mesh/triangulation.py @@ -17,7 +17,6 @@ def mapper_grids_from( source_plane_mesh_grid: Optional[Grid2DIrregular] = None, image_plane_mesh_grid: Optional[Grid2DIrregular] = None, adapt_data: np.ndarray = None, - run_time_dict: Optional[Dict] = None, ) -> MapperGrids: """ Mapper objects describe the mappings between pixels in the masked 2D data and the pixels in a mesh, @@ -59,12 +58,8 @@ def mapper_grids_from( transformation applied to it to create the `source_plane_mesh_grid`. adapt_data Not used for a rectangular mesh. - run_time_dict - A dictionary which contains timing of certain functions calls which is used for profiling. """ - self.run_time_dict = run_time_dict - relocated_grid = self.relocated_grid_from( border_relocator=border_relocator, source_plane_data_grid=source_plane_data_grid, @@ -90,5 +85,4 @@ def mapper_grids_from( source_plane_mesh_grid=source_plane_mesh_grid, image_plane_mesh_grid=image_plane_mesh_grid, adapt_data=adapt_data, - run_time_dict=run_time_dict, ) diff --git a/autoarray/inversion/pixelization/mesh/voronoi.py b/autoarray/inversion/pixelization/mesh/voronoi.py index dc8d9310f..99954d850 100644 --- a/autoarray/inversion/pixelization/mesh/voronoi.py +++ b/autoarray/inversion/pixelization/mesh/voronoi.py @@ -1,8 +1,6 @@ from autoarray.structures.mesh.voronoi_2d import Mesh2DVoronoi from autoarray.inversion.pixelization.mesh.triangulation import Triangulation -from autoarray.numba_util import profile_func - class Voronoi(Triangulation): def __init__(self): @@ -33,7 +31,6 @@ def __init__(self): """ super().__init__() - @profile_func def mesh_grid_from( self, source_plane_data_grid=None, diff --git a/autoarray/numba_util.py b/autoarray/numba_util.py index 9e0298b73..475777f31 100644 --- a/autoarray/numba_util.py +++ b/autoarray/numba_util.py @@ -69,86 +69,3 @@ def wrapper(func): return func return wrapper - - -def profile_func(func: Callable): - """ - Time every function called in a class and averages over repeated calls for profiling likelihood functions. - - The timings are stored in the variable `_run_time_dict` of the class(s) from which each function is called, - which are collected at the end of the profiling process via recursion. - - Parameters - ---------- - func : (obj, grid, *args, **kwargs) -> Object - A function which is used in the likelihood function.. - - Returns - ------- - A function that times the function being called. - """ - - @wraps(func) - def wrapper(obj, *args, **kwargs): - """ - Time a function and average over repeated calls for profiling an `Analysis` class's likelihood function. The - time is stored in a `run_time_dict` attribute. - - It is possible for multiple functions with the `profile_func` decorator to be called. In this circumstance, - we risk repeated profiling of the same functionality in these nested functions. Thus, before added - the time to the run_time_dict, the keys of the dictionary are iterated over in reverse, subtracting off the - times of nested functions (which will already have been added to the profiling dict). - - Returns - ------- - The result of the function being timed. - """ - - if not hasattr(obj, "run_time_dict"): - return func(obj, *args, **kwargs) - - if obj.run_time_dict is None: - return func(obj, *args, **kwargs) - - repeats = conf.instance["general"]["profiling"]["repeats"] - - last_key_before_call = ( - list(obj.run_time_dict)[-1] if obj.run_time_dict else None - ) - - start = time.time() - for i in range(repeats): - result = func(obj, *args, **kwargs) - - time_func = (time.time() - start) / repeats - - last_key_after_call = list(obj.run_time_dict)[-1] if obj.run_time_dict else None - - profile_call_max = 5 - - for i in range(profile_call_max): - key_func = f"{func.__name__}_{i}" - - if key_func not in obj.run_time_dict: - if last_key_before_call == last_key_after_call: - obj.run_time_dict[key_func] = time_func - else: - for key, value in reversed(list(obj.run_time_dict.items())): - if last_key_before_call == key: - obj.run_time_dict[key_func] = time_func - break - - time_func -= obj.run_time_dict[key] - - break - - if i == 5: - raise exc.ProfilingException( - f"Attempt to make profiling dict failed, because a function has been" - f"called more than {profile_call_max} times, exceed the number of times" - f"a profiled function may be called" - ) - - return result - - return wrapper diff --git a/autoarray/numpy_wrapper.py b/autoarray/numpy_wrapper.py index 3f534d995..9a23ba5ed 100644 --- a/autoarray/numpy_wrapper.py +++ b/autoarray/numpy_wrapper.py @@ -2,7 +2,9 @@ from os import environ -use_jax = environ.get("USE_JAX", "0") == "1" +from autoconf import conf + +use_jax = conf.instance["general"]["jax"]["use_jax"] if use_jax: try: diff --git a/autoarray/operators/over_sampling/over_sample_util.py b/autoarray/operators/over_sampling/over_sample_util.py index d3488a457..75465bd3f 100644 --- a/autoarray/operators/over_sampling/over_sample_util.py +++ b/autoarray/operators/over_sampling/over_sample_util.py @@ -8,10 +8,8 @@ if TYPE_CHECKING: from autoarray.structures.grids.uniform_2d import Grid2D -from autoarray.geometry import geometry_util from autoarray.mask.mask_2d import Mask2D -from autoarray import numba_util from autoarray import type as ty @@ -49,7 +47,6 @@ def over_sample_size_convert_to_array_2d_from( return Array2D(values=np.array(over_sample_size).astype("int"), mask=mask) -@numba_util.jit() def total_sub_pixels_2d_from(sub_size: np.ndarray) -> int: """ Returns the total number of sub-pixels in unmasked pixels in a mask. @@ -78,90 +75,6 @@ def total_sub_pixels_2d_from(sub_size: np.ndarray) -> int: return int(np.sum(sub_size**2)) -@numba_util.jit() -def native_sub_index_for_slim_sub_index_2d_from( - mask_2d: np.ndarray, sub_size: np.ndarray -) -> np.ndarray: - """ - Returns an array of shape [total_unmasked_pixels*sub_size] that maps every unmasked sub-pixel to its - corresponding native 2D pixel using its (y,x) pixel indexes. - - For example, for the following ``Mask2D`` for ``sub_size=1``: - - :: - [[True, True, True, True] - [True, False, False, True], - [True, False, True, True], - [True, True, True, True]] - - This has three unmasked (``False`` values) which have the ``slim`` indexes: - - :: - [0, 1, 2] - - The array ``native_index_for_slim_index_2d`` is therefore: - - :: - [[1,1], [1,2], [2,1]] - - For a ``Mask2D`` with ``sub_size=2`` each unmasked ``False`` entry is split into a sub-pixel of size 2x2 and - there are therefore 12 ``slim`` indexes: - - :: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - - The array ``native_index_for_slim_index_2d`` is therefore: - - :: - [[2,2], [2,3], [2,4], [2,5], [3,2], [3,3], [3,4], [3,5], [4,2], [4,3], [5,2], [5,3]] - - Parameters - ---------- - mask_2d - A 2D array of bools, where `False` values are unmasked. - sub_size - The size of the sub-grid in each mask pixel. - - Returns - ------- - ndarray - An array that maps pixels from a slimmed array of shape [total_unmasked_pixels*sub_size] to its native array - of shape [total_pixels*sub_size, total_pixels*sub_size]. - - Examples - -------- - mask_2d = np.array([[True, True, True], - [True, False, True] - [True, True, True]]) - - sub_native_index_for_sub_slim_index_2d = sub_native_index_for_sub_slim_index_via_mask_2d_from(mask_2d=mask_2d, sub_size=1) - """ - - total_sub_pixels = total_sub_pixels_2d_from(sub_size=sub_size) - sub_native_index_for_sub_slim_index_2d = np.zeros(shape=(total_sub_pixels, 2)) - - slim_index = 0 - sub_slim_index = 0 - - for y in range(mask_2d.shape[0]): - for x in range(mask_2d.shape[1]): - if not mask_2d[y, x]: - sub = sub_size[slim_index] - - for y1 in range(sub): - for x1 in range(sub): - sub_native_index_for_sub_slim_index_2d[sub_slim_index, :] = ( - (y * sub) + y1, - (x * sub) + x1, - ) - sub_slim_index += 1 - - slim_index += 1 - - return sub_native_index_for_sub_slim_index_2d - - -@numba_util.jit() def slim_index_for_sub_slim_index_via_mask_2d_from( mask_2d: np.ndarray, sub_size: np.ndarray ) -> np.ndarray: @@ -194,131 +107,20 @@ def slim_index_for_sub_slim_index_via_mask_2d_from( slim_index_for_sub_slim_index = slim_index_for_sub_slim_index_via_mask_2d_from(mask_2d=mask_2d, sub_size=2) """ - total_sub_pixels = total_sub_pixels_2d_from(sub_size=sub_size) - - slim_index_for_sub_slim_index = np.zeros(shape=total_sub_pixels) - slim_index = 0 - sub_slim_index = 0 - - for y in range(mask_2d.shape[0]): - for x in range(mask_2d.shape[1]): - if not mask_2d[y, x]: - sub = sub_size[slim_index] + # Step 1: Identify unmasked (False) pixels + unmasked_indices = np.argwhere(~mask_2d) + n_unmasked = unmasked_indices.shape[0] - for y1 in range(sub): - for x1 in range(sub): - slim_index_for_sub_slim_index[sub_slim_index] = slim_index - sub_slim_index += 1 + # Step 2: Compute total number of sub-pixels + sub_pixels_per_pixel = sub_size**2 - slim_index += 1 + # Step 3: Repeat slim indices for each sub-pixel + slim_indices = np.arange(n_unmasked) + slim_index_for_sub_slim_index = np.repeat(slim_indices, sub_pixels_per_pixel) return slim_index_for_sub_slim_index -@numba_util.jit() -def sub_slim_index_for_sub_native_index_from(sub_mask_2d: np.ndarray): - """ - Returns a 2D array which maps every `False` entry of a 2D mask to its sub slim mask array. Every - True entry is given a value -1. - - This is used as a convenience tool for creating structures util between different grids and structures. - - For example, if we had a 3x4 mask: - - [[False, True, False, False], - [False, True, False, False], - [False, False, False, True]]] - - The sub_slim_index_for_sub_native_index array would be: - - [[0, -1, 2, 3], - [4, -1, 5, 6], - [7, 8, 9, -1]] - - Parameters - ---------- - sub_mask_2d - The 2D mask that the util array is created for. - - Returns - ------- - ndarray - The 2D array mapping 2D mask entries to their 1D masked array indexes. - - Examples - -------- - mask = np.full(fill_value=False, shape=(9,9)) - sub_two_to_one = mask_to_mask_1d_index_from(mask=mask) - """ - - sub_slim_index_for_sub_native_index = -1 * np.ones(shape=sub_mask_2d.shape) - - sub_mask_1d_index = 0 - - for sub_mask_y in range(sub_mask_2d.shape[0]): - for sub_mask_x in range(sub_mask_2d.shape[1]): - if sub_mask_2d[sub_mask_y, sub_mask_x] == False: - sub_slim_index_for_sub_native_index[sub_mask_y, sub_mask_x] = ( - sub_mask_1d_index - ) - sub_mask_1d_index += 1 - - return sub_slim_index_for_sub_native_index - - -@numba_util.jit() -def oversample_mask_2d_from(mask: np.ndarray, sub_size: int) -> np.ndarray: - """ - Returns a new mask of shape (mask.shape[0] * sub_size, mask.shape[1] * sub_size) where all boolean values are - expanded according to the `sub_size`. - - For example, if the input mask is: - - mask = np.array([ - [True, True, True], - [True, False, True], - [True, True, True] - ]) - - and the sub_size is 2, the output mask would be: - - expanded_mask = np.array([ - [True, True, True, True, True, True], - [True, True, True, True, True, True], - [True, True, False, False, True, True], - [True, True, False, False, True, True], - [True, True, True, True, True, True], - [True, True, True, True, True, True] - ]) - - This is used throughout the code to handle uniform oversampling calculations. - - Parameters - ---------- - mask - The mask from which the over sample mask is computed. - sub_size - The factor by which the mask is oversampled. - - Returns - ------- - The mask oversampled by the input sub_size. - """ - oversample_mask = np.full( - (mask.shape[0] * sub_size, mask.shape[1] * sub_size), True - ) - - for y in range(mask.shape[0]): - for x in range(mask.shape[1]): - if not mask[y, x]: - oversample_mask[ - y * sub_size : (y + 1) * sub_size, x * sub_size : (x + 1) * sub_size - ] = False - - return oversample_mask - - -@numba_util.jit() def sub_size_radial_bins_from( radial_grid: np.ndarray, sub_size_list: np.ndarray, @@ -356,22 +158,18 @@ def sub_size_radial_bins_from( the centre of the mask. """ - sub_size = sub_size_list[-1] * np.ones(radial_grid.shape) + # Use np.searchsorted to find the first index where radial_grid[i] < radial_list[j] + bin_indices = np.searchsorted(radial_list, radial_grid, side="left") + + # Clip indices to stay within bounds of sub_size_list + bin_indices = np.clip(bin_indices, 0, len(sub_size_list) - 1) - for i in range(radial_grid.shape[0]): - for j in range(len(radial_list)): - if radial_grid[i] < radial_list[j]: - # if use_jax: - # # while this makes it run, it is very, very slow - # sub_size = sub_size.at[i].set(sub_size_list[j]) - # else: - sub_size[i] = sub_size_list[j] - break + return sub_size_list[bin_indices] - return sub_size + +from autoarray.geometry import geometry_util -@numba_util.jit() def grid_2d_slim_over_sampled_via_mask_from( mask_2d: np.ndarray, pixel_scales: ty.PixelScales, @@ -417,11 +215,16 @@ def grid_2d_slim_over_sampled_via_mask_from( grid_slim = grid_2d_slim_over_sampled_via_mask_from(mask=mask, pixel_scales=(0.5, 0.5), sub_size=1, origin=(0.0, 0.0)) """ + pixels_in_mask = (np.size(mask_2d) - np.sum(mask_2d)).astype(int) + + if isinstance(sub_size, int): + sub_size = np.full(fill_value=sub_size, shape=pixels_in_mask) + total_sub_pixels = np.sum(sub_size**2) grid_slim = np.zeros(shape=(total_sub_pixels, 2)) - centres_scaled = geometry_util.central_scaled_coordinate_2d_numba_from( + centres_scaled = geometry_util.central_scaled_coordinate_2d_from( shape_native=mask_2d.shape, pixel_scales=pixel_scales, origin=origin ) @@ -457,80 +260,97 @@ def grid_2d_slim_over_sampled_via_mask_from( return grid_slim -@numba_util.jit() -def binned_array_2d_from( - array_2d: np.ndarray, - mask_2d: np.ndarray, - sub_size: np.ndarray, -) -> np.ndarray: - """ - For a sub-grid, every unmasked pixel of its 2D mask with shape (total_y_pixels, total_x_pixels) is divided into - a finer uniform grid of shape (total_y_pixels*sub_size, total_x_pixels*sub_size). This routine computes the (y,x) - scaled coordinates a the centre of every sub-pixel defined by this 2D mask array. - - The sub-grid is returned on an array of shape (total_unmasked_pixels*sub_size**2, 2). y coordinates are - stored in the 0 index of the second dimension, x coordinates in the 1 index. Masked coordinates are therefore - removed and not included in the slimmed grid. - - Grid2D are defined from the top-left corner, where the first unmasked sub-pixel corresponds to index 0. - Sub-pixels that are part of the same mask array pixel are indexed next to one another, such that the second - sub-pixel in the first pixel has index 1, its next sub-pixel has index 2, and so forth. - - Parameters - ---------- - mask_2d - A 2D array of bools, where `False` values are unmasked and therefore included as part of the calculated - sub-grid. - pixel_scales - The (y,x) scaled units to pixel units conversion factor of the 2D mask array. - sub_size - The size of the sub-grid that each pixel of the 2D mask array is divided into. - origin - The (y,x) origin of the 2D array, which the sub-grid is shifted around. - - Returns - ------- - ndarray - A slimmed sub grid of (y,x) scaled coordinates at the centre of every pixel unmasked pixel on the 2D mask - array. The sub grid array has dimensions (total_unmasked_pixels*sub_size**2, 2). - - Examples - -------- - mask = np.array([[True, False, True], - [False, False, False] - [True, False, True]]) - grid_slim = grid_2d_slim_over_sampled_via_mask_from(mask=mask, pixel_scales=(0.5, 0.5), sub_size=1, origin=(0.0, 0.0)) - """ - - total_pixels = np.sum(~mask_2d) - - sub_fraction = 1.0 / sub_size**2 - - binned_array_2d_slim = np.zeros(shape=total_pixels) - - index = 0 - sub_index = 0 - - for y in range(mask_2d.shape[0]): - for x in range(mask_2d.shape[1]): - if not mask_2d[y, x]: - sub = sub_size[index] - - for y1 in range(sub): - for x1 in range(sub): - # if use_jax: - # binned_array_2d_slim = binned_array_2d_slim.at[index].add( - # array_2d[sub_index] * sub_fraction[index] - # ) - # else: - binned_array_2d_slim[index] += ( - array_2d[sub_index] * sub_fraction[index] - ) - sub_index += 1 - - index += 1 - - return binned_array_2d_slim +# + +# def grid_2d_slim_over_sampled_via_mask_from( +# mask_2d: np.ndarray, +# pixel_scales: ty.PixelScales, +# sub_size: np.ndarray, +# origin: Tuple[float, float] = (0.0, 0.0), +# ) -> np.ndarray: +# """ +# For a sub-grid, every unmasked pixel of its 2D mask with shape (total_y_pixels, total_x_pixels) is divided into +# a finer uniform grid of shape (total_y_pixels*sub_size, total_x_pixels*sub_size). This routine computes the (y,x) +# scaled coordinates at the centre of every sub-pixel defined by this 2D mask array. +# +# The sub-grid is returned on an array of shape (total_unmasked_pixels*sub_size**2, 2). y coordinates are +# stored in the 0 index of the second dimension, x coordinates in the 1 index. Masked coordinates are therefore +# removed and not included in the slimmed grid. +# +# Grid2D are defined from the top-left corner, where the first unmasked sub-pixel corresponds to index 0. +# Sub-pixels that are part of the same mask array pixel are indexed next to one another, such that the second +# sub-pixel in the first pixel has index 1, its next sub-pixel has index 2, and so forth. +# +# Parameters +# ---------- +# mask_2d +# A 2D array of bools, where `False` values are unmasked and therefore included as part of the calculated +# sub-grid. +# pixel_scales +# The (y,x) scaled units to pixel units conversion factor of the 2D mask array. +# sub_size +# The size of the sub-grid that each pixel of the 2D mask array is divided into. +# origin +# The (y,x) origin of the 2D array, which the sub-grid is shifted around. +# +# Returns +# ------- +# ndarray +# A slimmed sub grid of (y,x) scaled coordinates at the centre of every pixel unmasked pixel on the 2D mask +# array. The sub grid array has dimensions (total_unmasked_pixels*sub_size**2, 2). +# +# Examples +# -------- +# mask = np.array([[True, False, True], +# [False, False, False] +# [True, False, True]]) +# grid_slim = grid_2d_slim_over_sampled_via_mask_from(mask=mask, pixel_scales=(0.5, 0.5), sub_size=1, origin=(0.0, 0.0)) +# """ +# +# H, W = mask_2d.shape +# sy, sx = pixel_scales +# oy, ox = origin +# +# # 1) Find unmasked pixel indices in row-major order +# rows, cols = np.nonzero(~mask_2d) +# Npix = rows.size +# +# # 2) Broadcast or validate sub_size array +# sub_arr = np.asarray(sub_size) +# sub_arr = np.full(Npix, sub_arr, dtype=int) if sub_arr.size == 1 else sub_arr +# +# # 3) Compute pixel centers (y ↑ up, x → right) +# cy = (H - 1) / 2.0 +# cx = (W - 1) / 2.0 +# y_pix = (cy - rows) * sy + oy +# x_pix = (cols - cx) * sx + ox +# +# # 4) For each pixel, generate its sub-pixel coords and collect +# coords_list = [] +# for i in range(Npix): +# s = sub_arr[i] +# dy = sy / s +# dx = sx / s +# +# # y offsets: from top (+sy/2 - dy/2) down to bottom (-sy/2 + dy/2) +# y_off = np.linspace(+sy/2 - dy/2, -sy/2 + dy/2, s) +# # x offsets: left to right +# x_off = np.linspace(-sx/2 + dx/2, +sx/2 - dx/2, s) +# +# # build subgrid +# y_sub, x_sub = np.meshgrid(y_off, x_off, indexing="ij") +# y_sub = y_sub.ravel() +# x_sub = x_sub.ravel() +# +# # center + offsets +# y_center = y_pix[i] +# x_center = x_pix[i] +# coords = np.stack([y_center + y_sub, x_center + x_sub], axis=1) +# +# coords_list.append(coords) +# +# # 5) Concatenate all sub-pixel blocks in row-major pixel order +# return np.vstack(coords_list) def over_sample_size_via_radial_bins_from( diff --git a/autoarray/operators/over_sampling/over_sampler.py b/autoarray/operators/over_sampling/over_sampler.py index 5d9a99871..29c93aa7e 100644 --- a/autoarray/operators/over_sampling/over_sampler.py +++ b/autoarray/operators/over_sampling/over_sampler.py @@ -128,7 +128,7 @@ def __init__(self, mask: Mask2D, sub_size: Union[int, Array2D]): based on the sub-grid sizes. The over sampling class has functions dedicated to mapping between the sub-grid and pixel-grid, for example - `sub_mask_native_for_sub_mask_slim` and `slim_for_sub_slim`. + `slim_for_sub_slim`. The class `OverSampling` is used for the high level API, whereby this is where users input their preferred over-sampling configuration. This class, `OverSampler`, contains the functionality @@ -230,6 +230,20 @@ def binned_array_2d_from(self, array: Array2D) -> "Array2D": In **PyAutoCTI** all `Array2D` objects are used in their `native` representation without sub-gridding. Significant memory can be saved by only store this format, thus the `native_binned_only` config override can force this behaviour. It is recommended users do not use this option to avoid unexpected behaviour. + + Old docstring: + + For a sub-grid, every unmasked pixel of its 2D mask with shape (total_y_pixels, total_x_pixels) is divided into + a finer uniform grid of shape (total_y_pixels*sub_size, total_x_pixels*sub_size). This routine computes the (y,x) + scaled coordinates a the centre of every sub-pixel defined by this 2D mask array. + + The sub-grid is returned on an array of shape (total_unmasked_pixels*sub_size**2, 2). y coordinates are + stored in the 0 index of the second dimension, x coordinates in the 1 index. Masked coordinates are therefore + removed and not included in the slimmed grid. + + Grid2D are defined from the top-left corner, where the first unmasked sub-pixel corresponds to index 0. + Sub-pixels that are part of the same mask array pixel are indexed next to one another, such that the second + sub-pixel in the first pixel has index 1, its next sub-pixel has index 2, and so forth. """ if conf.instance["general"]["structures"]["native_binned_only"]: return self @@ -258,64 +272,6 @@ def binned_array_2d_from(self, array: Array2D) -> "Array2D": mask=self.mask, ) - @cached_property - def sub_mask_native_for_sub_mask_slim(self) -> np.ndarray: - """ - Derives a 1D ``ndarray`` which maps every subgridded 1D ``slim`` index of the ``Mask2D`` to its - subgridded 2D ``native`` index. - - For example, for the following ``Mask2D`` for ``sub_size=1``: - - :: - [[True, True, True, True] - [True, False, False, True], - [True, False, True, True], - [True, True, True, True]] - - This has three unmasked (``False`` values) which have the ``slim`` indexes: - - :: - [0, 1, 2] - - The array ``sub_mask_native_for_sub_mask_slim`` is therefore: - - :: - [[1,1], [1,2], [2,1]] - - For a ``Mask2D`` with ``sub_size=2`` each unmasked ``False`` entry is split into a sub-pixel of size 2x2 and - there are therefore 12 ``slim`` indexes: - - :: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - - The array ``native_for_slim`` is therefore: - - :: - [[2,2], [2,3], [2,4], [2,5], [3,2], [3,3], [3,4], [3,5], [4,2], [4,3], [5,2], [5,3]] - - Examples - -------- - - .. code-block:: python - - import autoarray as aa - - mask_2d = aa.Mask2D( - mask=[[True, True, True, True] - [True, False, False, True], - [True, False, True, True], - [True, True, True, True]] - pixel_scales=1.0, - ) - - derive_indexes_2d = aa.DeriveIndexes2D(mask=mask_2d) - - print(derive_indexes_2d.sub_mask_native_for_sub_mask_slim) - """ - return over_sample_util.native_sub_index_for_slim_sub_index_2d_from( - mask_2d=self.mask.array, sub_size=self.sub_size.array - ).astype("int") - @cached_property def slim_for_sub_slim(self) -> np.ndarray: """ diff --git a/autoarray/operators/transformer.py b/autoarray/operators/transformer.py index c3d94f686..ad861f0fb 100644 --- a/autoarray/operators/transformer.py +++ b/autoarray/operators/transformer.py @@ -418,22 +418,22 @@ def image_from( def transform_mapping_matrix(self, mapping_matrix: np.ndarray) -> np.ndarray: """ - Applies the NUFFT forward transform to each column of a mapping matrix, producing transformed visibilities. + Applies the NUFFT forward transform to each column of a mapping matrix, producing transformed visibilities. - Parameters - ---------- - mapping_matrix : np.ndarray - A 2D array where each column corresponds to a source-plane pixel intensity distribution flattened into image space. + Parameters + ---------- + mapping_matrix + A 2D array where each column corresponds to a source-plane pixel intensity distribution flattened into image space. - Returns + Returns ------- - np.ndarray - A complex-valued 2D array where each column contains the visibilities corresponding to the respective column in the input mapping matrix. + A complex-valued 2D array where each column contains the visibilities corresponding to the respective column + in the input mapping matrix. - Notes - ----- - - Each column of the input mapping matrix is reshaped into the native 2D image grid before transformation. - - This method repeatedly calls `visibilities_from` for each column, which may be computationally intensive. + Notes + ----- + - Each column of the input mapping matrix is reshaped into the native 2D image grid before transformation. + - This method repeatedly calls `visibilities_from` for each column, which may be computationally intensive. """ transformed_mapping_matrix = 0 + 0j * np.zeros( (self.uv_wavelengths.shape[0], mapping_matrix.shape[1]) diff --git a/autoarray/structures/arrays/array_2d_util.py b/autoarray/structures/arrays/array_2d_util.py index 4efa6583b..aff0872b5 100644 --- a/autoarray/structures/arrays/array_2d_util.py +++ b/autoarray/structures/arrays/array_2d_util.py @@ -6,7 +6,6 @@ if TYPE_CHECKING: from autoarray.mask.mask_2d import Mask2D -from autoarray import numba_util from autoarray.mask import mask_2d_util from autoarray import exc @@ -272,11 +271,10 @@ def extracted_array_2d_from( return resized_array -@numba_util.jit() def resized_array_2d_from( array_2d: np.ndarray, resized_shape: Tuple[int, int], - origin: Tuple[int, int] = (-1, -1), + origin: Tuple[int, int] = None, pad_value: int = 0.0, ) -> np.ndarray: """ @@ -312,106 +310,40 @@ def resized_array_2d_from( resize_array = resize_array_2d(array_2d=array_2d, new_shape=(2,2), origin=(2, 2)) """ - y_is_even = int(array_2d.shape[0]) % 2 == 0 - x_is_even = int(array_2d.shape[1]) % 2 == 0 - - if origin == (-1, -1): - if y_is_even: - y_centre = int(array_2d.shape[0] / 2) - elif not y_is_even: - y_centre = int(array_2d.shape[0] / 2) - - if x_is_even: - x_centre = int(array_2d.shape[1] / 2) - elif not x_is_even: - x_centre = int(array_2d.shape[1] / 2) - + if origin is None: + y_centre = array_2d.shape[0] // 2 + x_centre = array_2d.shape[1] // 2 origin = (y_centre, x_centre) - resized_array = np.zeros(shape=resized_shape) - - if y_is_even: - y_min = origin[0] - int(resized_shape[0] / 2) - y_max = origin[0] + int((resized_shape[0] / 2)) + 1 - elif not y_is_even: - y_min = origin[0] - int(resized_shape[0] / 2) - y_max = origin[0] + int((resized_shape[0] / 2)) + 1 - - if x_is_even: - x_min = origin[1] - int(resized_shape[1] / 2) - x_max = origin[1] + int((resized_shape[1] / 2)) + 1 - elif not x_is_even: - x_min = origin[1] - int(resized_shape[1] / 2) - x_max = origin[1] + int((resized_shape[1] / 2)) + 1 - - for y_resized, y in enumerate(range(y_min, y_max)): - for x_resized, x in enumerate(range(x_min, x_max)): - if y >= 0 and y < array_2d.shape[0] and x >= 0 and x < array_2d.shape[1]: - if ( - y_resized >= 0 - and y_resized < resized_shape[0] - and x_resized >= 0 - and x_resized < resized_shape[1] - ): - resized_array[y_resized, x_resized] = array_2d[y, x] - else: - if ( - y_resized >= 0 - and y_resized < resized_shape[0] - and x_resized >= 0 - and x_resized < resized_shape[1] - ): - resized_array[y_resized, x_resized] = pad_value - - return resized_array - - -@numba_util.jit() -def replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d: np.ndarray, noise_map_2d: np.ndarray, target_signal_to_noise: float = 2.0 -) -> np.ndarray: - """ - If the values of a 2D image array are negative, this function replaces the corresponding 2D noise-map array - values to meet a specified target to noise value. + # Define window edges so that length == resized_shape dimension exactly + y_min = origin[0] - resized_shape[0] // 2 + y_max = y_min + resized_shape[0] - This routine is necessary because of anomolous values in images which come from our HST ACS data_type-reduction - pipeline, where image-pixels with negative values (e.g. due to the background sky subtraction) have extremely - small noise values, which inflate their signal-to-noise values and chi-squared contributions in the modeling. + x_min = origin[1] - resized_shape[1] // 2 + x_max = x_min + resized_shape[1] - Parameters - ---------- - image_2d - The 2D image array used to locate the pixel indexes in the noise-map which are replaced. - noise_map_2d - The 2D noise-map array whose values are replaced. - target_signal_to_noise - The target signal-to-noise the noise-map valueus are changed to. + resized_array = np.full(resized_shape, pad_value, dtype=array_2d.dtype) - Returns - ------- - ndarray - The 2D noise-map with values changed. + # Calculate source indices clipped to array bounds + src_y_start = max(y_min, 0) + src_y_end = min(y_max, array_2d.shape[0]) + src_x_start = max(x_min, 0) + src_x_end = min(x_max, array_2d.shape[1]) - Examples - -------- - image_2d = np.ones((5,5)) - image_2d[2,2] = -1.0 - noise_map_2d = np.ones((5,5)) + # Calculate destination indices corresponding to source indices + dst_y_start = max(0, -y_min) + dst_y_end = dst_y_start + (src_y_end - src_y_start) + dst_x_start = max(0, -x_min) + dst_x_end = dst_x_start + (src_x_end - src_x_start) - noise_map_2d_replaced = replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=2.0): - """ - for y in range(image_2d.shape[0]): - for x in range(image_2d.shape[1]): - if image_2d[y, x] < 0.0: - absolute_signal_to_noise = np.abs(image_2d[y, x]) / noise_map_2d[y, x] - if absolute_signal_to_noise >= target_signal_to_noise: - noise_map_2d[y, x] = np.abs(image_2d[y, x]) / target_signal_to_noise + # Copy overlapping region from source to destination + resized_array[dst_y_start:dst_y_end, dst_x_start:dst_x_end] = array_2d[ + src_y_start:src_y_end, src_x_start:src_x_end + ] - return noise_map_2d + return resized_array -@numba_util.jit() def index_2d_for_index_slim_from(indexes_slim: np.ndarray, shape_native) -> np.ndarray: """ For pixels on a native 2D array of shape (total_y_pixels, total_x_pixels), this array maps the slimmed 1D pixel @@ -444,16 +376,18 @@ def index_2d_for_index_slim_from(indexes_slim: np.ndarray, shape_native) -> np.n indexes_slim = np.array([0, 1, 2, 5]) indexes_2d = index_2d_for_index_slim_from(indexes_slim=indexes_slim, shape=(3,3)) """ - index_2d_for_index_slim = np.zeros((indexes_slim.shape[0], 2)) + # Calculate row indices by integer division by number of columns + rows = indexes_slim // shape_native[1] + + # Calculate column indices by modulo number of columns + cols = indexes_slim % shape_native[1] - for i, index_slim in enumerate(indexes_slim): - index_2d_for_index_slim[i, 0] = int(index_slim / shape_native[1]) - index_2d_for_index_slim[i, 1] = int(index_slim % shape_native[1]) + # Stack rows and cols horizontally into shape (N, 2) + index_2d_for_index_slim = np.vstack((rows, cols)).T return index_2d_for_index_slim -@numba_util.jit() def index_slim_for_index_2d_from(indexes_2d: np.ndarray, shape_native) -> np.ndarray: """ For pixels on a native 2D array of shape (total_y_pixels, total_x_pixels), this array maps the 2D pixel indexes to @@ -486,12 +420,10 @@ def index_slim_for_index_2d_from(indexes_2d: np.ndarray, shape_native) -> np.nda indexes_2d = np.array([[0,0], [1,0], [2,0], [2,2]]) indexes_flat = index_flat_for_index_2d_from(indexes_2d=indexes_2d, shape=(3,3)) """ - index_slim_for_index_native_2d = np.zeros(indexes_2d.shape[0]) - - for i in range(indexes_2d.shape[0]): - index_slim_for_index_native_2d[i] = int( - (indexes_2d[i, 0]) * shape_native[1] + indexes_2d[i, 1] - ) + # Calculate 1D indexes as row_index * number_of_columns + col_index + index_slim_for_index_native_2d = ( + indexes_2d[:, 0] * shape_native[1] + indexes_2d[:, 1] + ) return index_slim_for_index_native_2d @@ -629,66 +561,3 @@ def array_2d_via_indexes_from( return ( jnp.zeros(shape).at[tuple(native_index_for_slim_index_2d.T)].set(array_2d_slim) ) - - -@numba_util.jit() -def array_2d_slim_complex_from( - array_2d_native: np.ndarray, - mask: np.ndarray, -) -> np.ndarray: - """ - For a 2D array and mask, map the values of all unmasked pixels to a 1D array. - - The pixel coordinate origin is at the top left corner of the 2D array and goes right-wards and downwards. - - For example, for an array of shape (3,3) and where all pixels are unmasked: - - - pixel [0,0] of the 2D array will correspond to index 0 of the 1D array. - - pixel [0,1] of the 2D array will correspond to index 1 of the 1D array. - - pixel [1,0] of the 2D array will correspond to index 3 of the 1D array. - - pixel [2,0] of the 2D array will correspond to index 6 of the 1D array. - - Parameters - ---------- - array_2d_native - A 2D array of values on the dimensions of the grid. - mask - A 2D array of bools, where `False` values mean unmasked and are included in the mapping. - array_2d - The 2D array of values which are mapped to a 1D array. - - Returns - ------- - ndarray - A 1D array of values mapped from the 2D array with dimensions (total_unmasked_pixels). - """ - - total_pixels = np.sum(~mask) - - array_1d = 0 + 0j * np.zeros(shape=total_pixels) - index = 0 - - for y in range(mask.shape[0]): - for x in range(mask.shape[1]): - if not mask[y, x]: - array_1d[index] = array_2d_native[y, x] - index += 1 - - return array_1d - - -@numba_util.jit() -def array_2d_native_complex_via_indexes_from( - array_2d_slim: np.ndarray, - shape_native: Tuple[int, int], - native_index_for_slim_index_2d: np.ndarray, -) -> np.ndarray: - array_2d = 0 + 0j * np.zeros(shape_native) - - for slim_index in range(len(native_index_for_slim_index_2d)): - array_2d[ - native_index_for_slim_index_2d[slim_index, 0], - native_index_for_slim_index_2d[slim_index, 1], - ] = array_2d_slim[slim_index] - - return array_2d diff --git a/autoarray/structures/grids/grid_2d_util.py b/autoarray/structures/grids/grid_2d_util.py index b547fcb94..ea298bce2 100644 --- a/autoarray/structures/grids/grid_2d_util.py +++ b/autoarray/structures/grids/grid_2d_util.py @@ -396,7 +396,6 @@ def grid_2d_via_shape_native_from( ) -@numba_util.jit() def _radial_projected_shape_slim_from( extent: np.ndarray, centre: Tuple[float, float], @@ -471,7 +470,6 @@ def _radial_projected_shape_slim_from( return int((scaled_distance / pixel_scale)) + 1 -@numba_util.jit() def grid_scaled_2d_slim_radial_projected_from( extent: np.ndarray, centre: Tuple[float, float], @@ -562,9 +560,11 @@ def grid_scaled_2d_slim_radial_projected_from( radii = centre[1] - for slim_index in range(shape_slim): - grid_scaled_2d_slim_radii[slim_index, 1] = radii - radii += pixel_scale + # Create an array of radii values spaced by pixel_scale + radii_array = radii + pixel_scale * np.arange(shape_slim) + + # Assign all values at once to the second column (index 1) + grid_scaled_2d_slim_radii[:, 1] = radii_array return grid_scaled_2d_slim_radii + 1e-6 @@ -739,58 +739,6 @@ def grid_2d_native_from( return jnp.stack((grid_2d_native_y, grid_2d_native_x), axis=-1) -@numba_util.jit() -def grid_2d_slim_upscaled_from( - grid_slim: np.ndarray, upscale_factor: int, pixel_scales: ty.PixelScales -) -> np.ndarray: - """ - From an input slimmed 2D grid, return an upscaled slimmed 2D grid where (y,x) coordinates are added at an - upscaled resolution to each grid coordinate. - - Parameters - ---------- - grid_slim - The slimmed grid of (y,x) coordinates over which a square uniform grid is overlaid. - upscale_factor - The upscaled resolution at which the new grid coordinates are computed. - pixel_scales - The pixel scale of the uniform grid that laid over the irregular grid of (y,x) coordinates. - """ - - grid_2d_slim_upscaled = np.zeros(shape=(grid_slim.shape[0] * upscale_factor**2, 2)) - - upscale_index = 0 - - y_upscale_half = pixel_scales[0] / 2 - y_upscale_step = pixel_scales[0] / upscale_factor - - x_upscale_half = pixel_scales[1] / 2 - x_upscale_step = pixel_scales[1] / upscale_factor - - for slim_index in range(grid_slim.shape[0]): - y_grid = grid_slim[slim_index, 0] - x_grid = grid_slim[slim_index, 1] - - for y in range(upscale_factor): - for x in range(upscale_factor): - grid_2d_slim_upscaled[upscale_index, 0] = ( - y_grid - + y_upscale_half - - y * y_upscale_step - - (y_upscale_step / 2.0) - ) - grid_2d_slim_upscaled[upscale_index, 1] = ( - x_grid - - x_upscale_half - + x * x_upscale_step - + (x_upscale_step / 2.0) - ) - - upscale_index += 1 - - return grid_2d_slim_upscaled - - def grid_2d_of_points_within_radius( radius: float, centre: Tuple[float, float], grid_2d: np.ndarray ): diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index addfb2ec1..5ac71e9f3 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -180,18 +180,24 @@ def __init__( self.over_sampler = OverSampler(sub_size=over_sample_size, mask=mask) - if over_sampled is None: - over_sampled = over_sample_util.grid_2d_slim_over_sampled_via_mask_from( - mask_2d=np.array(self.mask), - pixel_scales=self.mask.pixel_scales, - sub_size=self.over_sampler.sub_size.array.astype("int"), - origin=self.mask.origin, - ) + self._over_sampled = over_sampled - self.over_sampled = Grid2DIrregular(values=over_sampled) + @property + def over_sampled(self): - else: - self.over_sampled = over_sampled + if self._over_sampled is not None: + return self._over_sampled + + over_sampled = over_sample_util.grid_2d_slim_over_sampled_via_mask_from( + mask_2d=np.array(self.mask), + pixel_scales=self.mask.pixel_scales, + sub_size=self.over_sampler.sub_size.array.astype("int"), + origin=self.mask.origin, + ) + + self._over_sampled = Grid2DIrregular(values=over_sampled) + + return self._over_sampled @classmethod def no_mask( @@ -696,6 +702,7 @@ def subtracted_from(self, offset: Tuple[(float, float), np.ndarray]) -> "Grid2D" values=self - np.array(offset), mask=mask, over_sample_size=self.over_sample_size, + over_sampled=self.over_sampled - np.array(offset), ) @property diff --git a/autoarray/util/cholesky_funcs.py b/autoarray/util/cholesky_funcs.py deleted file mode 100644 index bd211eeb5..000000000 --- a/autoarray/util/cholesky_funcs.py +++ /dev/null @@ -1,100 +0,0 @@ -import numpy as np -from scipy import linalg -import math -import time -from autoarray import numba_util - - -@numba_util.jit() -def _choldowndate(U, x): - n = x.size - for k in range(n - 1): - Ukk = U[k, k] - xk = x[k] - r = math.sqrt(Ukk**2 - xk**2) - c = r / Ukk - s = xk / Ukk - U[k, k] = r - U[k, k + 1 :] = (U[k, (k + 1) :] - s * x[k + 1 :]) / c - x[k + 1 :] = c * x[k + 1 :] - s * U[k, k + 1 :] - - k = n - 1 - U[k, k] = math.sqrt(U[k, k] ** 2 - x[k] ** 2) - return U - - -@numba_util.jit() -def _cholupdate(U, x): - n = x.size - for k in range(n - 1): - Ukk = U[k, k] - xk = x[k] - - r = np.sqrt(Ukk**2 + xk**2) - - c = r / Ukk - s = xk / Ukk - U[k, k] = r - - U[k, k + 1 :] = (U[k, (k + 1) :] + s * x[k + 1 :]) / c - x[k + 1 :] = c * x[k + 1 :] - s * U[k, k + 1 :] - - k = n - 1 - U[k, k] = np.sqrt(U[k, k] ** 2 + x[k] ** 2) - - return U - - -def cholinsert(U, index, x): - S = np.insert(np.insert(U, index, 0, axis=0), index, 0, axis=1) - - S[:index, index] = S12 = linalg.solve_triangular( - U[:index, :index], x[:index], trans=1, lower=False, overwrite_b=True - ) - - S[index, index] = s22 = math.sqrt(x[index] - S12.dot(S12)) - - if index == U.shape[0]: - return S - else: - S[index, index + 1 :] = S23 = (x[index + 1 :] - S12.T @ U[:index, index:]) / s22 - _choldowndate(S[index + 1 :, index + 1 :], S23) # S33 - return S - - -def cholinsertlast(U, x): - """ - Update the Cholesky matrix U by inserting a vector at the end of the matrix - Inserting a vector to the end of U doesn't require _cholupdate, so save some time. - It's a special case of `cholinsert` (as shown above, if index == U.shape[0]) - As in current Cholesky scheme implemented in fnnls, we only use this kind of insertion, so I - separate it out from the `cholinsert`. - """ - index = U.shape[0] - - S = np.insert(np.insert(U, index, 0, axis=0), index, 0, axis=1) - - S[:index, index] = S12 = linalg.solve_triangular( - U[:index, :index], x[:index], trans=1, lower=False, overwrite_b=True - ) - - S[index, index] = s22 = math.sqrt(x[index] - S12.dot(S12)) - - return S - - -def choldeleteindexes(U, indexes): - indexes = sorted(indexes, reverse=True) - - for index in indexes: - L = np.delete(np.delete(U, index, axis=0), index, axis=1) - - # If the deleted index is at the end of matrix, then we do not need to update the U. - - if index == L.shape[0]: - U = L - else: - _cholupdate(L[index:, index:], U[index, index + 1 :]) - U = L - - return U diff --git a/autoarray/util/fnnls.py b/autoarray/util/fnnls.py deleted file mode 100644 index 3f49c1f2d..000000000 --- a/autoarray/util/fnnls.py +++ /dev/null @@ -1,155 +0,0 @@ -import numpy as np -from scipy import linalg as slg - -from autoarray.util.cholesky_funcs import cholinsertlast, choldeleteindexes - -from autoarray import exc - -""" - This file contains functions use the Bro & Jong (1997) algorithm to solve the non-negative least - square problem. The `fnnls and fix_constraint` is orginally copied from - "https://github.com/jvendrow/fnnls". - For our purpose in PyAutoArray, we create `fnnls_modefied` to take ZTZ and ZTx as inputs directly. - Furthermore, we add two functions `fnnls_Cholesky and fix_constraint_Cholesky` to realize a scheme - that solves the lstsq problem in the algorithm by Cholesky factorisation. For ~ 1000 free - parameters, we see a speed up by 2 times and should be more for more parameters. - We have also noticed that by setting the P_initial to be `sla.solve(ZTZ, ZTx, assume_a='pos') > 0` - will speed up our task (~ 1000 free parameters) by ~ 3 times as it significantly reduces the - iteration time. -""" - - -def fnnls_cholesky( - ZTZ, - ZTx, - P_initial=np.zeros(0, dtype=int), -): - """ - Similar to fnnls, but use solving the lstsq problem by updating Cholesky factorisation. - """ - - lstsq = lambda A, x: slg.solve( - A, - x, - assume_a="pos", - overwrite_a=True, - overwrite_b=True, - ) - - n = np.shape(ZTZ)[0] - epsilon = 2.2204e-16 - tolerance = epsilon * n - max_repetitions = 3 - no_update = 0 - loop_count = 0 - loop_count2 = 0 - - P = np.zeros(n, dtype=bool) - P[P_initial] = True - d = np.zeros(n) - w = ZTx - (ZTZ) @ d - s_chol = np.zeros(n) - - if P_initial.shape[0] != 0: - P_number = np.arange(len(P), dtype="int") - P_inorder = P_number[P_initial] - s_chol[P] = lstsq((ZTZ)[P][:, P], (ZTx)[P]) - d = s_chol.clip(min=0) - else: - P_inorder = np.array([], dtype="int") - - # P_inorder is similar as P. They are both used to select solutions in the passive set. - # P_inorder saves the `indexes` of those passive solutions. - # P saves [True/False] for all solutions. True indicates a solution in the passive set while False - # indicates it's in the active set. - # The benifit of P_inorder is that we are able to not only select out solutions in the passive set - # and can sort them in the order of added to the passive set. This will make updating the - # Cholesky factorisation simpler and thus save time. - - while (not np.all(P)) and np.max(w[~P]) > tolerance: - # make copy of passive set to check for change at end of loop - - current_P = P.copy() - idmax = np.argmax(w * ~P) - P_inorder = np.append(P_inorder, int(idmax)) - - if loop_count == 0: - # We need to initialize the Cholesky factorisation, U, for the first loop. - U = slg.cholesky(ZTZ[P_inorder][:, P_inorder]) - else: - U = cholinsertlast(U, ZTZ[idmax][P_inorder]) - - # solve the lstsq problem by cho_solve - - s_chol[P_inorder] = slg.cho_solve((U, False), ZTx[P_inorder]) - - P[idmax] = True - while np.any(P) and np.min(s_chol[P]) <= tolerance: - s_chol, d, P, P_inorder, U = fix_constraint_cholesky( - ZTx=ZTx, - s_chol=s_chol, - d=d, - P=P, - P_inorder=P_inorder, - U=U, - tolerance=tolerance, - ) - - loop_count2 += 1 - if loop_count2 > 10000: - raise RuntimeError - - d = s_chol.copy() - w = ZTx - (ZTZ) @ d - loop_count += 1 - - if loop_count > 10000: - raise RuntimeError - - if np.all(current_P == P): - no_update += 1 - else: - no_update = 0 - - if no_update >= max_repetitions: - break - - return d - - -def fix_constraint_cholesky(ZTx, s_chol, d, P, P_inorder, U, tolerance): - """ - Similar to fix_constraint, but solve the lstsq by Cholesky factorisation. - If this function is called, it means some solutions in the current passive sets needed to be - taken out and put into the active set. - So, this function involves 3 procedure: - 1. Identifying what solutions should be taken out of the current passive set. - 2. Updating the P, P_inorder and the Cholesky factorisation U. - 3. Solving the lstsq by using the new Cholesky factorisation U. - As some solutions are taken out from the passive set, the Cholesky factorisation needs to be - updated by choldeleteindexes. To realize that, we call the `choldeleteindexes` from - cholesky_funcs. - """ - q = P * (s_chol <= tolerance) - alpha = np.min(d[q] / (d[q] - s_chol[q])) - - # set d as close to s as possible while maintaining non-negativity - d = d + alpha * (s_chol - d) - - id_delete = np.where(d[P_inorder] <= tolerance)[0] - - U = choldeleteindexes(U, id_delete) # update the Cholesky factorisation - - P_inorder = np.delete(P_inorder, id_delete) # update the P_inorder - - P[d <= tolerance] = False # update the P - - # solve the lstsq problem by cho_solve - - if len(P_inorder): - # there could be a case where P_inorder is empty. - s_chol[P_inorder] = slg.cho_solve((U, False), ZTx[P_inorder]) - - s_chol[~P] = 0.0 # set solutions taken out of the passive set to be 0 - - return s_chol, d, P, P_inorder, U diff --git a/test_autoarray/fit/test_fit_imaging.py b/test_autoarray/fit/test_fit_imaging.py index 5b7e49446..c90a453ac 100644 --- a/test_autoarray/fit/test_fit_imaging.py +++ b/test_autoarray/fit/test_fit_imaging.py @@ -102,28 +102,3 @@ def test__data_and_model_are_identical__inversion_included__changes_certain_prop fit.chi_squared + 2.0 + 3.0 - 4.0 + fit.noise_normalization ) assert fit.figure_of_merit == fit.log_evidence - - -def test__run_time_dict__profiles_appropriate_functions(): - mask = aa.Mask2D(mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0)) - - data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) - noise_map = aa.Array2D(values=[2.0, 2.0, 2.0, 2.0], mask=mask) - - dataset = aa.Imaging(data=data, noise_map=noise_map) - - dataset = dataset.apply_mask(mask=mask) - - model_data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) - - run_time_dict = {} - - fit = aa.m.MockFitImaging( - dataset=dataset, - use_mask_in_fit=False, - model_data=model_data, - run_time_dict=run_time_dict, - ) - fit.figure_of_merit - - assert "figure_of_merit_0" in fit.run_time_dict diff --git a/test_autoarray/operators/over_sample/test_decorator.py b/test_autoarray/operators/over_sample/test_decorator.py index 47dae1e88..72af0215a 100644 --- a/test_autoarray/operators/over_sample/test_decorator.py +++ b/test_autoarray/operators/over_sample/test_decorator.py @@ -28,9 +28,23 @@ def test__in_grid_2d__over_sample_uniform__out_ndarray_1d(): over_sample_uniform = aa.OverSampler(mask=mask, sub_size=2) - mask_sub_2 = aa.util.over_sample.oversample_mask_2d_from( - mask=np.array(mask), sub_size=2 - ) + def oversample_mask_2d_from(mask: np.ndarray, sub_size: int) -> np.ndarray: + + oversample_mask = np.full( + (mask.shape[0] * sub_size, mask.shape[1] * sub_size), True + ) + + for y in range(mask.shape[0]): + for x in range(mask.shape[1]): + if not mask[y, x]: + oversample_mask[ + y * sub_size : (y + 1) * sub_size, + x * sub_size : (x + 1) * sub_size, + ] = False + + return oversample_mask + + mask_sub_2 = oversample_mask_2d_from(mask=np.array(mask), sub_size=2) mask_sub_2 = aa.Mask2D(mask=mask_sub_2, pixel_scales=(0.5, 0.5)) diff --git a/test_autoarray/operators/over_sample/test_over_sample_util.py b/test_autoarray/operators/over_sample/test_over_sample_util.py index c0552d8a0..fb68da898 100644 --- a/test_autoarray/operators/over_sample/test_over_sample_util.py +++ b/test_autoarray/operators/over_sample/test_over_sample_util.py @@ -12,77 +12,6 @@ def test__total_sub_pixels_2d_from(): ) -def test__native_sub_index_for_slim_sub_index_2d_from(): - mask = np.array([[True, True, True], [True, False, True], [True, True, True]]) - - sub_mask_index_for_sub_mask_1d_index = ( - util.over_sample.native_sub_index_for_slim_sub_index_2d_from( - mask_2d=mask, sub_size=np.array([2]) - ) - ) - - assert ( - sub_mask_index_for_sub_mask_1d_index - == np.array([[2, 2], [2, 3], [3, 2], [3, 3]]) - ).all() - - mask = np.array([[True, False, True], [False, False, False], [True, False, True]]) - - sub_mask_index_for_sub_mask_1d_index = ( - util.over_sample.native_sub_index_for_slim_sub_index_2d_from( - mask_2d=mask, sub_size=np.array([2, 2, 2, 2, 2]) - ) - ) - - assert ( - sub_mask_index_for_sub_mask_1d_index - == np.array( - [ - [0, 2], - [0, 3], - [1, 2], - [1, 3], - [2, 0], - [2, 1], - [3, 0], - [3, 1], - [2, 2], - [2, 3], - [3, 2], - [3, 3], - [2, 4], - [2, 5], - [3, 4], - [3, 5], - [4, 2], - [4, 3], - [5, 2], - [5, 3], - ] - ) - ).all() - - mask = np.array( - [ - [True, True, True], - [True, False, True], - [True, True, True], - [True, True, False], - ] - ) - - sub_mask_index_for_sub_mask_1d_index = ( - util.over_sample.native_sub_index_for_slim_sub_index_2d_from( - mask_2d=mask, sub_size=np.array([2, 2]) - ) - ) - - assert ( - sub_mask_index_for_sub_mask_1d_index - == np.array([[2, 2], [2, 3], [3, 2], [3, 3], [6, 4], [6, 5], [7, 4], [7, 5]]) - ).all() - - def test__slim_index_for_sub_slim_index_via_mask_2d_from(): mask = np.array([[True, True, True], [True, False, True], [True, True, True]]) @@ -150,119 +79,11 @@ def test__slim_index_for_sub_slim_index_via_mask_2d_from(): ).all() -def test__sub_slim_index_for_sub_native_index_from(): - mask = np.full(fill_value=False, shape=(3, 3)) - - sub_mask_1d_index_for_sub_mask_index = ( - util.over_sample.sub_slim_index_for_sub_native_index_from(sub_mask_2d=mask) - ) - - assert ( - sub_mask_1d_index_for_sub_mask_index - == np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) - ).all() - - mask = np.full(fill_value=False, shape=(2, 3)) - - sub_mask_1d_index_for_sub_mask_index = ( - util.over_sample.sub_slim_index_for_sub_native_index_from(sub_mask_2d=mask) - ) - - assert ( - sub_mask_1d_index_for_sub_mask_index == np.array([[0, 1, 2], [3, 4, 5]]) - ).all() - - mask = np.full(fill_value=False, shape=(3, 2)) - - sub_mask_1d_index_for_sub_mask_index = ( - util.over_sample.sub_slim_index_for_sub_native_index_from(sub_mask_2d=mask) - ) - - assert ( - sub_mask_1d_index_for_sub_mask_index == np.array([[0, 1], [2, 3], [4, 5]]) - ).all() - - mask = np.array([[False, True, False], [True, True, False], [False, False, True]]) - - sub_mask_1d_index_for_sub_mask_index = ( - util.over_sample.sub_slim_index_for_sub_native_index_from(sub_mask_2d=mask) - ) - - assert ( - sub_mask_1d_index_for_sub_mask_index - == np.array([[0, -1, 1], [-1, -1, 2], [3, 4, -1]]) - ).all() - - mask = np.array( - [ - [False, True, True, False], - [True, True, False, False], - [False, False, True, False], - ] - ) - - sub_mask_1d_index_for_sub_mask_index = ( - util.over_sample.sub_slim_index_for_sub_native_index_from(sub_mask_2d=mask) - ) - - assert ( - sub_mask_1d_index_for_sub_mask_index - == np.array([[0, -1, -1, 1], [-1, -1, 2, 3], [4, 5, -1, 6]]) - ).all() - - mask = np.array( - [ - [False, True, False], - [True, True, False], - [False, False, True], - [False, False, True], - ] - ) - - sub_mask_1d_index_for_sub_mask_index = ( - util.over_sample.sub_slim_index_for_sub_native_index_from(sub_mask_2d=mask) - ) - - assert ( - sub_mask_1d_index_for_sub_mask_index - == np.array([[0, -1, 1], [-1, -1, 2], [3, 4, -1], [5, 6, -1]]) - ).all() - - -def test__oversample_mask_from(): - mask = np.array( - [ - [True, True, True, True], - [True, False, False, True], - [True, False, False, True], - [True, True, True, True], - ] - ) - - oversample_mask = util.over_sample.oversample_mask_2d_from(mask=mask, sub_size=2) - - assert ( - oversample_mask - == np.array( - [ - [True, True, True, True, True, True, True, True], - [True, True, True, True, True, True, True, True], - [True, True, False, False, False, False, True, True], - [True, True, False, False, False, False, True, True], - [True, True, False, False, False, False, True, True], - [True, True, False, False, False, False, True, True], - [True, True, True, True, True, True, True, True], - [True, True, True, True, True, True, True, True], - ] - ) - ).all() - - def test__grid_2d_slim_over_sampled_via_mask_from(): mask = np.array([[True, True, False], [False, False, False], [True, True, False]]) grid = aa.util.over_sample.grid_2d_slim_over_sampled_via_mask_from( - mask_2d=mask, pixel_scales=(3.0, 3.0), sub_size=np.array([2, 2, 2, 2, 2]) + mask_2d=mask, pixel_scales=(3.0, 3.0), sub_size=2 ) assert ( diff --git a/test_autoarray/operators/over_sample/test_over_sampler.py b/test_autoarray/operators/over_sample/test_over_sampler.py index a32b11e8f..e7d601614 100644 --- a/test_autoarray/operators/over_sample/test_over_sampler.py +++ b/test_autoarray/operators/over_sample/test_over_sampler.py @@ -91,26 +91,6 @@ def test__binned_array_2d_from(): assert binned_array_2d.slim == pytest.approx(np.array([1.0, 8.0]), 1.0e-4) -def test__sub_mask_index_for_sub_mask_1d_index(): - mask = aa.Mask2D( - mask=[[True, True, True], [True, False, False], [True, True, False]], - pixel_scales=1.0, - sub_size=2, - ) - - over_sampling = aa.OverSampler(mask=mask, sub_size=2) - - sub_mask_index_for_sub_mask_1d_index = ( - aa.util.over_sample.native_sub_index_for_slim_sub_index_2d_from( - mask_2d=np.array(mask), sub_size=np.array([2, 2, 2]) - ) - ) - - assert over_sampling.sub_mask_native_for_sub_mask_slim == pytest.approx( - sub_mask_index_for_sub_mask_1d_index, 1e-4 - ) - - def test__slim_index_for_sub_slim_index(): mask = aa.Mask2D( mask=[[True, False, True], [False, False, False], [True, False, False]], diff --git a/test_autoarray/structures/arrays/test_array_2d_util.py b/test_autoarray/structures/arrays/test_array_2d_util.py index 9468dd510..0c6ae8bc7 100644 --- a/test_autoarray/structures/arrays/test_array_2d_util.py +++ b/test_autoarray/structures/arrays/test_array_2d_util.py @@ -287,96 +287,6 @@ def test__resized_array_2d_from__padding_with_new_origin(): ).all() -def test__replace_noise_map_2d_values_where_image_2d_values_are_negative(): - image_2d = np.ones(shape=(2, 2)) - - noise_map_2d = np.array([[1.0, 2.0], [3.0, 4.0]]) - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=1.0 - ) - ) - - assert (noise_map_2d == noise_map_2d).all() - - image_2d = -1.0 * np.ones(shape=(2, 2)) - - noise_map_2d = np.array([[1.0, 0.5], [0.25, 0.125]]) - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=10.0 - ) - ) - - assert (noise_map_2d == noise_map_2d).all() - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=4.0 - ) - ) - - assert (noise_map_2d == np.array([[1.0, 0.5], [0.25, 0.25]])).all() - - noise_map_2d = np.array([[1.0, 0.5], [0.25, 0.125]]) - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=2.0 - ) - ) - - assert (noise_map_2d == np.array([[1.0, 0.5], [0.5, 0.5]])).all() - - noise_map_2d = np.array([[1.0, 0.5], [0.25, 0.125]]) - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=1.0 - ) - ) - - assert (noise_map_2d == np.array([[1.0, 1.0], [1.0, 1.0]])).all() - - noise_map_2d = np.array([[1.0, 0.5], [0.25, 0.125]]) - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=0.5 - ) - ) - - assert (noise_map_2d == np.array([[2.0, 2.0], [2.0, 2.0]])).all() - - -def test__same_as_above__image_not_all_negative(): - image_2d = np.array([[1.0, -2.0], [5.0, -4.0]]) - - noise_map_2d = np.array([[3.0, 1.0], [4.0, 8.0]]) - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=1.0 - ) - ) - - assert (noise_map_2d == np.array([[3.0, 2.0], [4.0, 8.0]])).all() - - image_2d = np.array([[-10.0, -20.0], [100.0, -30.0]]) - - noise_map_2d = np.array([[1.0, 2.0], [40.0, 3.0]]) - - noise_map_2d = ( - util.array_2d.replace_noise_map_2d_values_where_image_2d_values_are_negative( - image_2d=image_2d, noise_map_2d=noise_map_2d, target_signal_to_noise=5.0 - ) - ) - - assert (noise_map_2d == np.array([[2.0, 4.0], [40.0, 6.0]])).all() - - def test__index_2d_for_index_slim_from(): indexes_1d = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8]) @@ -521,25 +431,6 @@ def test__array_2d_slim_from(): assert (array_2d_slim == np.array([2, 4, 5, 6, 8])).all() -def test__array_2d_slim_from__complex_array(): - array_2d = np.array( - [ - [1 + 1j, 2 + 2j, 3 + 3], - [4 + 4j, 5 + 5j, 6 + 6j], - [7 + 7j, 8 + 8j, 9 + 9j], - ] - ) - - mask = np.array([[True, True, True], [True, False, True], [True, True, True]]) - - array_2d_slim = util.array_2d.array_2d_slim_complex_from( - mask=mask, - array_2d_native=array_2d, - ) - - assert (array_2d_slim == np.array([5 + 5j])).all() - - def test__array_2d_native_from(): array_2d_slim = np.array([1.0, 2.0, 3.0, 4.0]) @@ -584,19 +475,3 @@ def test__array_2d_native_from(): [[1.0, 2.0, 0.0, 0.0], [3.0, 0.0, 0.0, 0.0], [-1.0, -2.0, 0.0, -3.0]] ) ).all() - - -def test__array_2d_native_from__compelx_array(): - array_2d_slim = np.array( - [1.0 + 1j, 2.0 + 2j, 3.0 + 3j, 4.0 + 4j], dtype="complex128" - ) - - array_2d = util.array_2d.array_2d_native_complex_via_indexes_from( - array_2d_slim=array_2d_slim, - shape_native=(2, 2), - native_index_for_slim_index_2d=np.array( - [[0, 0], [0, 1], [1, 0], [1, 1]], dtype="int" - ), - ) - - assert (array_2d == np.array([[1.0 + 1j, 2.0 + 2j], [3.0 + 3j, 4.0 + 4j]])).all() diff --git a/test_autoarray/structures/grids/test_grid_2d_util.py b/test_autoarray/structures/grids/test_grid_2d_util.py index 463aa4e7b..034f267e5 100644 --- a/test_autoarray/structures/grids/test_grid_2d_util.py +++ b/test_autoarray/structures/grids/test_grid_2d_util.py @@ -461,107 +461,3 @@ def test__grid_2d_native_from(): ] ) ).all() - - grid_slim = np.array( - [ - [1.0, 1.0], - [1.0, 1.0], - [1.0, 1.0], - [1.0, 1.0], - [2.0, 2.0], - [2.0, 2.0], - [2.0, 2.0], - [2.0, 2.0], - [3.0, 3.0], - [3.0, 3.0], - [3.0, 3.0], - [4.0, 4.0], - ] - ) - - -def test__grid_2d_slim_upscaled_from(): - grid_slim = np.array([[1.0, 1.0]]) - - grid_upscaled_2d = aa.util.grid_2d.grid_2d_slim_upscaled_from( - grid_slim=grid_slim, upscale_factor=1, pixel_scales=(2.0, 2.0) - ) - - assert (grid_upscaled_2d == np.array([[1.0, 1.0]])).all() - - grid_upscaled_2d = aa.util.grid_2d.grid_2d_slim_upscaled_from( - grid_slim=grid_slim, upscale_factor=2, pixel_scales=(2.0, 2.0) - ) - - assert ( - grid_upscaled_2d == np.array([[1.5, 0.5], [1.5, 1.5], [0.5, 0.5], [0.5, 1.5]]) - ).all() - - grid_slim = np.array([[1.0, 1.0], [1.0, 3.0]]) - - grid_upscaled_2d = aa.util.grid_2d.grid_2d_slim_upscaled_from( - grid_slim=grid_slim, upscale_factor=2, pixel_scales=(2.0, 2.0) - ) - - assert ( - grid_upscaled_2d - == np.array( - [ - [1.5, 0.5], - [1.5, 1.5], - [0.5, 0.5], - [0.5, 1.5], - [1.5, 2.5], - [1.5, 3.5], - [0.5, 2.5], - [0.5, 3.5], - ] - ) - ).all() - - grid_slim = np.array([[1.0, 1.0], [3.0, 1.0]]) - - grid_upscaled_2d = aa.util.grid_2d.grid_2d_slim_upscaled_from( - grid_slim=grid_slim, upscale_factor=2, pixel_scales=(2.0, 2.0) - ) - - assert ( - grid_upscaled_2d - == np.array( - [ - [1.5, 0.5], - [1.5, 1.5], - [0.5, 0.5], - [0.5, 1.5], - [3.5, 0.5], - [3.5, 1.5], - [2.5, 0.5], - [2.5, 1.5], - ] - ) - ).all() - - grid_slim = np.array([[1.0, 1.0]]) - - grid_upscaled_2d = aa.util.grid_2d.grid_2d_slim_upscaled_from( - grid_slim=grid_slim, upscale_factor=2, pixel_scales=(3.0, 2.0) - ) - - assert ( - grid_upscaled_2d - == np.array([[1.75, 0.5], [1.75, 1.5], [0.25, 0.5], [0.25, 1.5]]) - ).all() - - grid_upscaled_2d = aa.util.grid_2d.grid_2d_slim_upscaled_from( - grid_slim=grid_slim, upscale_factor=3, pixel_scales=(2.0, 2.0) - ) - - assert grid_upscaled_2d[0] == pytest.approx(np.array([1.666, 0.333]), 1.0e-2) - assert grid_upscaled_2d[1] == pytest.approx(np.array([1.666, 1.0]), 1.0e-2) - assert grid_upscaled_2d[2] == pytest.approx(np.array([1.666, 1.666]), 1.0e-2) - assert grid_upscaled_2d[3] == pytest.approx(np.array([1.0, 0.333]), 1.0e-2) - assert grid_upscaled_2d[4] == pytest.approx(np.array([1.0, 1.0]), 1.0e-2) - assert grid_upscaled_2d[5] == pytest.approx(np.array([1.0, 1.666]), 1.0e-2) - assert grid_upscaled_2d[6] == pytest.approx(np.array([0.333, 0.333]), 1.0e-2) - assert grid_upscaled_2d[7] == pytest.approx(np.array([0.333, 1.0]), 1.0e-2) - assert grid_upscaled_2d[8] == pytest.approx(np.array([0.333, 1.666]), 1.0e-2) diff --git a/test_autoarray/test_decorators.py b/test_autoarray/test_decorators.py index 85ea8b3fe..e2652d27c 100644 --- a/test_autoarray/test_decorators.py +++ b/test_autoarray/test_decorators.py @@ -2,18 +2,9 @@ class MockClass: - def __init__(self, value, run_time_dict=None): + def __init__(self, value): self._value = value - self.run_time_dict = run_time_dict @property - @aa.profile_func def value(self): return self._value - - -def test__profile_decorator_times_decorated_function(): - cls = MockClass(value=1.0, run_time_dict={}) - cls.value - - assert "value_0" in cls.run_time_dict