From bf1f2b817d3e882940f9c79c81f9ede9f1ee1af0 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sat, 14 Dec 2024 15:15:00 +0000 Subject: [PATCH 01/26] all over samplers dont support None --- autoarray/dataset/grids.py | 17 +++----- autoarray/dataset/over_sampling.py | 9 ++-- autoarray/dataset/plot/imaging_plotters.py | 42 +++++-------------- .../dataset/imaging/test_dataset.py | 2 +- 4 files changed, 23 insertions(+), 47 deletions(-) diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index 52390ae3..16aa90b0 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -70,7 +70,6 @@ def uniform(self) -> Union[Grid1D, Grid2D]: ------- The (y,x) coordinates of every pixel in the data. """ - return Grid2D.from_mask( mask=self.mask, over_sampling=self.over_sampling.uniform, @@ -95,10 +94,6 @@ def non_uniform(self) -> Optional[Union[Grid1D, Grid2D]]: ------- The (y,x) coordinates of every pixel in the data. """ - - if self.over_sampling.non_uniform is None: - return None - return Grid2D.from_mask( mask=self.mask, over_sampling=self.over_sampling.non_uniform, @@ -120,15 +115,9 @@ def pixelization(self) -> Grid2D: ------- The (y,x) coordinates of every pixel in the data, used for pixelization / inversion calculations. """ - - over_sampling = self.over_sampling.pixelization - - if over_sampling is None: - over_sampling = OverSamplingUniform(sub_size=4) - return Grid2D.from_mask( mask=self.mask, - over_sampling=over_sampling, + over_sampling=self.over_sampling.pixelization, ) @cached_property @@ -155,6 +144,10 @@ def blurring(self) -> Optional[Grid2D]: kernel_shape_native=self.psf.shape_native, ) + @cached_property + def over_sampler_uniform(self): + return self.uniform.over_sampling.over_sampler_from(mask=self.mask) + @cached_property def over_sampler_non_uniform(self): return self.non_uniform.over_sampling.over_sampler_from(mask=self.mask) diff --git a/autoarray/dataset/over_sampling.py b/autoarray/dataset/over_sampling.py index e707e325..44d914c7 100644 --- a/autoarray/dataset/over_sampling.py +++ b/autoarray/dataset/over_sampling.py @@ -2,6 +2,9 @@ from typing import Optional from autoarray.operators.over_sampling.abstract import AbstractOverSampling +from autoarray.operators.over_sampling.uniform import ( + OverSamplingUniform, +) logger = logging.getLogger(__name__) @@ -57,6 +60,6 @@ def __init__( by **PyAutoLens** when the grid has been deflected and ray-traced and therefore some of the default over sampling schemes are not appropriate. """ - self.uniform = uniform - self.pixelization = pixelization - self.non_uniform = non_uniform + self.uniform = uniform or OverSamplingUniform(sub_size=4) + self.pixelization = pixelization or OverSamplingUniform(sub_size=4) + self.non_uniform = non_uniform or OverSamplingUniform(sub_size=4) diff --git a/autoarray/dataset/plot/imaging_plotters.py b/autoarray/dataset/plot/imaging_plotters.py index a173675b..f41be685 100644 --- a/autoarray/dataset/plot/imaging_plotters.py +++ b/autoarray/dataset/plot/imaging_plotters.py @@ -126,47 +126,27 @@ def figures_2d( ) if over_sampling: - if self.dataset.over_sampling.uniform is None: - from autoarray.operators.over_sampling.uniform import ( - OverSamplingUniform, - ) - - over_sampling = OverSamplingUniform.from_adaptive_scheme( - grid=self.dataset.grids.uniform, - name="PlotExample", - centre=(0.0, 0.0), - ) - title = title_str or f"Over Sampling (Adaptive)" - - else: - over_sampling = self.dataset.over_sampling.uniform - title = title_str or f"Over Sampling" - - over_sampler = over_sampling.over_sampler_from( - mask=self.dataset.mask, - ) self.mat_plot_2d.plot_array( - array=over_sampler.sub_size, + array=self.dataset.grids.over_sampler_uniform.sub_size, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( - title=title, + title=title_str or f"Over Sampling (Uniform)", filename="over_sampling", cb_unit="", ), ) if over_sampling_non_uniform: - if self.dataset.over_sampling.non_uniform is not None: - self.mat_plot_2d.plot_array( - array=self.dataset.grids.over_sampler_non_uniform.sub_size, - visuals_2d=self.get_visuals_2d(), - auto_labels=AutoLabels( - title=title_str or f"Over Sampling (Non Uniform)", - filename="over_sampling_non_uniform", - cb_unit="", - ), - ) + self.mat_plot_2d.plot_array( + array=self.dataset.grids.over_sampler_non_uniform.sub_size, + visuals_2d=self.get_visuals_2d(), + auto_labels=AutoLabels( + title=title_str or f"Over Sampling (Non Uniform)", + filename="over_sampling_non_uniform", + cb_unit="", + ), + ) if over_sampling_pixelization: self.mat_plot_2d.plot_array( diff --git a/test_autoarray/dataset/imaging/test_dataset.py b/test_autoarray/dataset/imaging/test_dataset.py index 9f43626e..6025733f 100644 --- a/test_autoarray/dataset/imaging/test_dataset.py +++ b/test_autoarray/dataset/imaging/test_dataset.py @@ -171,7 +171,7 @@ def test__apply_noise_scaling__use_signal_to_noise_value(imaging_7x7, mask_2d_7x assert masked_imaging_7x7.data.native[3, 4] == 1.0 assert masked_imaging_7x7.noise_map.native[3, 4] == 10.0 assert masked_imaging_7x7.data.native[3, 3] == 2.0 - assert masked_imaging_7x7.noise_map.native[3, 3] == 20.0 + assert masked_imaging_7x7.noise_map.native[3, 3] == 10.0 def test__apply_mask__noise_covariance_matrix(): From 3ef3d6046d1eae2adf280ff9c4c4505a1ce77020 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sat, 14 Dec 2024 16:41:15 +0000 Subject: [PATCH 02/26] remove Grid2D Iterate and sort bugs --- autoarray/__init__.py | 4 - autoarray/dataset/interferometer/dataset.py | 64 +--- autoarray/dataset/plot/imaging_plotters.py | 1 - autoarray/fixtures.py | 9 - autoarray/mock.py | 1 - .../operators/over_sampling/decorator.py | 57 +--- .../over_sampling/grid_oversampled.py | 9 - autoarray/operators/over_sampling/iterate.py | 295 ------------------ autoarray/operators/over_sampling/uniform.py | 10 - autoarray/structures/grids/uniform_2d.py | 17 +- autoarray/structures/mock/mock_decorators.py | 133 -------- test_autoarray/config/grids.yaml | 7 - .../dataset/abstract/test_dataset.py | 15 - .../operators/over_sample/test_decorator.py | 75 +---- .../operators/over_sample/test_iterate.py | 230 -------------- 15 files changed, 30 insertions(+), 897 deletions(-) delete mode 100644 autoarray/operators/over_sampling/grid_oversampled.py delete mode 100644 autoarray/operators/over_sampling/iterate.py delete mode 100644 test_autoarray/operators/over_sample/test_iterate.py diff --git a/autoarray/__init__.py b/autoarray/__init__.py index 38d57ba9..ed3f1a46 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -69,12 +69,8 @@ from .structures.arrays.irregular import ArrayIrregular from .structures.grids.uniform_1d import Grid1D from .structures.grids.uniform_2d import Grid2D -from .operators.over_sampling.decorator import perform_over_sampling_from -from .operators.over_sampling.grid_oversampled import Grid2DOverSampled from .operators.over_sampling.uniform import OverSamplingUniform -from .operators.over_sampling.iterate import OverSamplingIterate from .operators.over_sampling.uniform import OverSamplerUniform -from .operators.over_sampling.iterate import OverSamplerIterate from .structures.grids.irregular_2d import Grid2DIrregular from .structures.grids.irregular_2d import Grid2DIrregularUniform from .structures.mesh.rectangular_2d import Mesh2DRectangular diff --git a/autoarray/dataset/interferometer/dataset.py b/autoarray/dataset/interferometer/dataset.py index 58023719..caf78796 100644 --- a/autoarray/dataset/interferometer/dataset.py +++ b/autoarray/dataset/interferometer/dataset.py @@ -9,6 +9,10 @@ from autoarray.dataset.grids import GridsDataset from autoarray.dataset.over_sampling import OverSamplingDataset from autoarray.operators.transformer import TransformerNUFFT +from autoarray.operators.over_sampling.uniform import ( + OverSamplingUniform, +) + from autoarray.structures.visibilities import Visibilities from autoarray.structures.visibilities import VisibilitiesNoiseMap @@ -25,7 +29,6 @@ def __init__( uv_wavelengths: np.ndarray, real_space_mask, transformer_class=TransformerNUFFT, - over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(), ): """ An interferometer dataset, containing the visibilities data, noise-map, real-space msk, Fourier transformer and @@ -68,16 +71,18 @@ def __init__( noise_covariance_matrix A noise-map covariance matrix representing the covariance between noise in every `data` value, which can be used via a bespoke fit to account for correlated noise in the data. - over_sampling - The over sampling schemes which divide the grids into sub grids of smaller pixels within their host image - pixels when using the grid to evaluate a function (e.g. images) to better approximate the 2D line integral - This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). transformer_class The class of the Fourier Transform which maps images from real space to Fourier space visibilities and the uv-plane. """ self.real_space_mask = real_space_mask + over_sampling = OverSamplingDataset( + uniform=OverSamplingUniform(sub_size=1), + pixelization=OverSamplingUniform(sub_size=1), + non_uniform=OverSamplingUniform(sub_size=1), + ) + super().__init__( data=data, noise_map=noise_map, @@ -94,53 +99,6 @@ def __init__( def grids(self): return GridsDataset(mask=self.real_space_mask, over_sampling=self.over_sampling) - def apply_over_sampling( - self, - over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(), - ) -> "Interferometer": - """ - Apply new over sampling objects to the grid and grid pixelization of the dataset. - - This method is used to change the over sampling of the grid and grid pixelization, for example when the - user wishes to perform over sampling with a higher sub grid size or with an iterative over sampling strategy. - - The `grid` and grids.pixelization` are cached properties which after use are stored in memory for efficiency. - This function resets the cached properties so that the new over sampling is used in the grid and grid - pixelization. - - The `default_galaxy_mode` parameter is used to set up default over sampling for galaxy light profiles in - the project PyAutoGalaxy. This sets up the over sampling such that there is high over sampling in the centre - of the mask, where the galaxy is located, and lower over sampling in the outer regions of the mask. It - does this based on the pixel scale, which gives a good estimate of how large the central region - requiring over sampling is. - - Parameters - ---------- - over_sampling - The over sampling schemes which divide the grids into sub grids of smaller pixels within their host image - pixels when using the grid to evaluate a function (e.g. images) to better approximate the 2D line integral - This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). - """ - - uniform = over_sampling.uniform or self.over_sampling.uniform - non_uniform = over_sampling.non_uniform or self.over_sampling.non_uniform - pixelization = over_sampling.pixelization or self.over_sampling.pixelization - - over_sampling = OverSamplingDataset( - uniform=uniform, - non_uniform=non_uniform, - pixelization=pixelization, - ) - - return Interferometer( - data=self.data, - noise_map=self.noise_map, - uv_wavelengths=self.uv_wavelengths, - real_space_mask=self.real_space_mask, - transformer_class=self.transformer.__class__, - over_sampling=over_sampling, - ) - @classmethod def from_fits( cls, @@ -152,7 +110,6 @@ def from_fits( noise_map_hdu=0, uv_wavelengths_hdu=0, transformer_class=TransformerNUFFT, - over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(), ): """ Factory for loading the interferometer data_type from .fits files, as well as computing properties like the @@ -178,7 +135,6 @@ def from_fits( noise_map=noise_map, uv_wavelengths=uv_wavelengths, transformer_class=transformer_class, - over_sampling=over_sampling, ) @cached_property diff --git a/autoarray/dataset/plot/imaging_plotters.py b/autoarray/dataset/plot/imaging_plotters.py index f41be685..57fb92d8 100644 --- a/autoarray/dataset/plot/imaging_plotters.py +++ b/autoarray/dataset/plot/imaging_plotters.py @@ -126,7 +126,6 @@ def figures_2d( ) if over_sampling: - self.mat_plot_2d.plot_array( array=self.dataset.grids.over_sampler_uniform.sub_size, visuals_2d=self.get_visuals_2d(), diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 15cec61d..82bb9d9b 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -235,9 +235,6 @@ def make_interferometer_7(): uv_wavelengths=make_uv_wavelengths_7x2(), real_space_mask=make_mask_2d_7x7(), transformer_class=aa.TransformerDFT, - over_sampling=aa.OverSamplingDataset( - pixelization=aa.OverSamplingUniform(sub_size=1) - ), ) @@ -248,9 +245,6 @@ def make_interferometer_7_no_fft(): uv_wavelengths=make_uv_wavelengths_7x2_no_fft(), real_space_mask=make_mask_2d_7x7(), transformer_class=aa.TransformerDFT, - over_sampling=aa.OverSamplingDataset( - pixelization=aa.OverSamplingUniform(sub_size=1) - ), ) @@ -261,9 +255,6 @@ def make_interferometer_7_grid(): uv_wavelengths=make_uv_wavelengths_7x2(), real_space_mask=make_mask_2d_7x7(), transformer_class=aa.TransformerDFT, - over_sampling=aa.OverSamplingDataset( - pixelization=aa.OverSamplingUniform(sub_size=1) - ), ) diff --git a/autoarray/mock.py b/autoarray/mock.py index a9344b51..78a857e7 100644 --- a/autoarray/mock.py +++ b/autoarray/mock.py @@ -21,4 +21,3 @@ from autoarray.structures.mock.mock_decorators import MockGridRadialMinimum from autoarray.structures.mock.mock_decorators import MockGrid1DLikeObj from autoarray.structures.mock.mock_decorators import MockGrid2DLikeObj -from autoarray.structures.mock.mock_decorators import MockGridLikeIteratorObj diff --git a/autoarray/operators/over_sampling/decorator.py b/autoarray/operators/over_sampling/decorator.py index 584baf35..9c32cb29 100644 --- a/autoarray/operators/over_sampling/decorator.py +++ b/autoarray/operators/over_sampling/decorator.py @@ -4,9 +4,6 @@ from typing import List, Union -from autoarray.operators.over_sampling.grid_oversampled import Grid2DOverSampled -from autoarray.operators.over_sampling.uniform import OverSamplingUniform - from autoarray.structures.arrays.irregular import ArrayIrregular from autoarray.structures.arrays.uniform_1d import Array1D from autoarray.structures.arrays.uniform_2d import Array2D @@ -15,27 +12,6 @@ from autoarray.structures.grids.uniform_2d import Grid2D -def perform_over_sampling_from(grid, **kwargs): - if kwargs.get("over_sampling_being_performed"): - return False - - perform_over_sampling = False - - if isinstance(grid, Grid2D): - if grid.over_sampling is not None: - perform_over_sampling = True - - if isinstance(grid.over_sampling, OverSamplingUniform): - try: - if grid.over_sampling.sub_size == 1: - perform_over_sampling = False - except ValueError: - if sum(grid.over_sampling.sub_size) == grid.mask.pixels_in_mask: - perform_over_sampling = False - - return perform_over_sampling - - def over_sample(func): """ Homogenize the inputs and outputs of functions that take 1D or 2D grids of coordinates and return a 1D ndarray @@ -72,33 +48,16 @@ def wrapper( The function values evaluated on the grid with the same structure as the input grid_like object. """ - if isinstance(grid, Grid2DOverSampled): - result = func(obj, grid.grid, *args, **kwargs) - - return grid.over_sampler.binned_array_2d_from(array=result) - - if isinstance(grid, Grid2D): - if grid.over_sampling is None: - if grid.is_uniform: - over_sampling = OverSamplingUniform.from_adaptive_scheme( - grid=grid, - name=obj.__class__.__name__, - centre=obj.centre, - ) - - grid = Grid2D( - values=grid, mask=grid.mask, over_sampling=over_sampling - ) - - perform_over_sampling = perform_over_sampling_from(grid=grid, kwargs=kwargs) - - if not perform_over_sampling: + if isinstance(grid, Grid2DIrregular) or isinstance(grid, Grid1D): return func(obj=obj, grid=grid, *args, **kwargs) - kwargs["over_sampling_being_performed"] = True + over_sampled_grid = grid.over_sampler.over_sampled_grid + + if obj is not None: + values = func(obj, over_sampled_grid, *args, **kwargs) + else: + values = func(over_sampled_grid, *args, **kwargs) - return grid.over_sampler.array_via_func_from( - func=func, obj=obj, *args, **kwargs - ) + return grid.over_sampler.binned_array_2d_from(array=values) return wrapper diff --git a/autoarray/operators/over_sampling/grid_oversampled.py b/autoarray/operators/over_sampling/grid_oversampled.py deleted file mode 100644 index e3b0e666..00000000 --- a/autoarray/operators/over_sampling/grid_oversampled.py +++ /dev/null @@ -1,9 +0,0 @@ -class Grid2DOverSampled: - def __init__(self, grid, over_sampler, pixels_in_mask): - self.grid = grid - self.over_sampler = over_sampler - self.pixels_in_mask = pixels_in_mask - - @property - def mask(self): - return self.grid.mask diff --git a/autoarray/operators/over_sampling/iterate.py b/autoarray/operators/over_sampling/iterate.py deleted file mode 100644 index 57540bfc..00000000 --- a/autoarray/operators/over_sampling/iterate.py +++ /dev/null @@ -1,295 +0,0 @@ -import numpy as np -from typing import Callable, List, Optional - -from autoarray import numba_util -from autoarray.mask.mask_2d import Mask2D -from autoarray.operators.over_sampling.abstract import AbstractOverSampling -from autoarray.operators.over_sampling.abstract import AbstractOverSampler -from autoarray.operators.over_sampling.uniform import OverSamplerUniform -from autoarray.structures.arrays.uniform_2d import Array2D - - -class OverSamplingIterate(AbstractOverSampling): - def __init__( - self, - fractional_accuracy: float = 0.9999, - relative_accuracy: Optional[float] = None, - sub_steps: List[int] = None, - ): - """ - Over samples grid calculations using an iterative sub-grid that increases the sampling until a threshold - accuracy is met. - - When a 2D grid of (y,x) coordinates is input into a function, the result is evaluated at every coordinate - on the grid. When the grid is paired to a 2D image (e.g. an `Array2D`) the solution needs to approximate - the 2D integral of that function in each pixel. Over sample objects define how this over-sampling is performed. - - This object iteratively recomputes the analytic function at increasing sub-grid resolutions until an input - fractional accuracy is reached. The sub-grid is increase in each pixel, therefore it will gradually better - approximate the 2D integral after each iteration. - - Iteration is performed on a per pixel basis, such that the sub-grid size will stop at lower values - in pixels where the fractional accuracy is met quickly. It will only go to high values where high sampling is - required to meet the accuracy. This ensures the function is evaluated accurately in a computationally - efficient manner. - - Parameters - ---------- - fractional_accuracy - The fractional accuracy the function evaluated must meet to be accepted, where this accuracy is the ratio - of the value at a higher sub size to the value computed using the previous sub_size. The fractional - accuracy does not depend on the units or magnitude of the function being evaluated. - relative_accuracy - The relative accuracy the function evaluted must meet to be accepted, where this value is the absolute - difference of the values computed using the higher sub size and lower sub size grids. The relative - accuracy depends on the units / magnitude of the function being evaluated. - sub_steps - The sub-size values used to iteratively evaluated the function at high levels of sub-gridding. If None, - they are setup as the default values [2, 4, 8, 16]. - """ - - if sub_steps is None: - sub_steps = [2, 4, 8, 16] - - self.fractional_accuracy = fractional_accuracy - self.relative_accuracy = relative_accuracy - self.sub_steps = sub_steps - - def over_sampler_from(self, mask: Mask2D) -> "OverSamplerIterate": - return OverSamplerIterate( - mask=mask, - sub_steps=self.sub_steps, - fractional_accuracy=self.fractional_accuracy, - relative_accuracy=self.relative_accuracy, - ) - - -@numba_util.jit() -def threshold_mask_via_arrays_jit_from( - fractional_accuracy_threshold: float, - relative_accuracy_threshold: Optional[float], - threshold_mask: np.ndarray, - array_higher_sub_2d: np.ndarray, - array_lower_sub_2d: np.ndarray, - array_higher_mask: np.ndarray, -) -> np.ndarray: - """ - Jitted function to determine the fractional mask, which is a mask where: - - - ``True`` entries signify the function has been evaluated in that pixel to desired accuracy and - therefore does not need to be iteratively computed at higher levels of sub-gridding. - - - ``False`` entries signify the function has not been evaluated in that pixel to desired fractional accuracy and - therefore must be iterative computed at higher levels of sub-gridding to meet this accuracy. - """ - - if fractional_accuracy_threshold is not None: - for y in range(threshold_mask.shape[0]): - for x in range(threshold_mask.shape[1]): - if not array_higher_mask[y, x]: - if array_lower_sub_2d[y, x] > 0: - fractional_accuracy = ( - array_lower_sub_2d[y, x] / array_higher_sub_2d[y, x] - ) - - if fractional_accuracy > 1.0: - fractional_accuracy = 1.0 / fractional_accuracy - - else: - fractional_accuracy = 0.0 - - if fractional_accuracy < fractional_accuracy_threshold: - threshold_mask[y, x] = False - - if relative_accuracy_threshold is not None: - for y in range(threshold_mask.shape[0]): - for x in range(threshold_mask.shape[1]): - if not array_higher_mask[y, x]: - if ( - abs(array_lower_sub_2d[y, x] - array_higher_sub_2d[y, x]) - > relative_accuracy_threshold - ): - threshold_mask[y, x] = False - - return threshold_mask - - -@numba_util.jit() -def iterated_array_jit_from( - iterated_array: np.ndarray, - threshold_mask_higher_sub: np.ndarray, - threshold_mask_lower_sub: np.ndarray, - array_higher_sub_2d: np.ndarray, -) -> np.ndarray: - """ - Create the iterated array from a result array that is computed at a higher sub size leel than the previous grid. - - The iterated array is only updated for pixels where the fractional accuracy is met. - """ - - for y in range(iterated_array.shape[0]): - for x in range(iterated_array.shape[1]): - if threshold_mask_higher_sub[y, x] and not threshold_mask_lower_sub[y, x]: - iterated_array[y, x] = array_higher_sub_2d[y, x] - - return iterated_array - - -class OverSamplerIterate(AbstractOverSampler): - def __init__( - self, - mask: Mask2D, - fractional_accuracy: float = 0.9999, - relative_accuracy: Optional[float] = None, - sub_steps: List[int] = None, - ): - self.mask = mask - self.fractional_accuracy = fractional_accuracy - self.relative_accuracy = relative_accuracy - self.sub_steps = sub_steps - - def array_at_sub_size_from( - self, func: Callable, cls, mask: Mask2D, sub_size, *args, **kwargs - ) -> Array2D: - over_sample_uniform = OverSamplerUniform(mask=mask, sub_size=sub_size) - - over_sampled_grid = over_sample_uniform.over_sampled_grid - - array_higher_sub = func(cls, over_sampled_grid, *args, **kwargs) - - return over_sample_uniform.binned_array_2d_from(array=array_higher_sub).native - - def threshold_mask_from( - self, array_lower_sub_2d: Array2D, array_higher_sub_2d: Array2D - ) -> Mask2D: - """ - Returns a fractional mask from a result array, where the fractional mask describes whether the evaluated - value in the result array is within the ``OverSamplingIterate``'s specified fractional accuracy. The fractional mask thus - determines whether a pixel on the grid needs to be reevaluated at a higher level of sub-gridding to meet the - specified fractional accuracy. If it must be re-evaluated, the fractional masks's entry is ``False``. - - The fractional mask is computed by comparing the results evaluated at one level of sub-gridding to another - at a higher level of sub-griding. Thus, the sub-grid size in chosen on a per-pixel basis until the function - is evaluated at the specified fractional accuracy. - - Parameters - ---------- - array_lower_sub_2d - The results computed by a function using a lower sub-grid size - array_higher_sub_2d - The results computed by a function using a higher sub-grid size. - """ - - threshold_mask = Mask2D.all_false( - shape_native=array_lower_sub_2d.shape_native, - pixel_scales=array_lower_sub_2d.pixel_scales, - invert=True, - ) - - threshold_mask = threshold_mask_via_arrays_jit_from( - fractional_accuracy_threshold=self.fractional_accuracy, - relative_accuracy_threshold=self.relative_accuracy, - threshold_mask=np.array(threshold_mask), - array_higher_sub_2d=np.array(array_higher_sub_2d), - array_lower_sub_2d=np.array(array_lower_sub_2d), - array_higher_mask=np.array(array_higher_sub_2d.mask), - ) - - return Mask2D( - mask=threshold_mask, - pixel_scales=array_lower_sub_2d.pixel_scales, - origin=array_lower_sub_2d.origin, - ) - - def array_via_func_from( - self, func: Callable, obj: object, *args, **kwargs - ) -> Array2D: - """ - Iterate over a function that returns an array of values until the it meets a specified fractional accuracy. - The function returns a result on a pixel-grid where evaluating it on more points on a higher resolution - sub-grid followed by binning lead to a more precise evaluation of the function. The function is assumed to - belong to a class, which is input into tthe method. - - The function is first called for a sub-grid size of 1 and a higher resolution grid. The ratio of values give - the fractional accuracy of each function evaluation. Pixels which do not meet the fractional accuracy are - iteratively revaluated on higher resolution sub-grids. This is repeated until all pixels meet the fractional - accuracy or the highest sub-size specified in the *sub_steps* attribute is computed. - - If the function return all zeros, the iteration is terminated early given that all levels of sub-gridding will - return zeros. This occurs when a function is missing optional objects that contribute to the calculation. - - An example use case of this function is when a "image_2d_from" methods in **PyAutoGalaxy**'s - ``LightProfile`` module is comomputed, which by evaluating the function on a higher resolution sub-grids sample - the analytic light profile at more points and thus more precisely. - - Iterate over a function that returns an array or grid of values until the it meets a specified fractional - accuracy. The function returns a result on a pixel-grid where evaluating it on more points on a higher - resolution sub-grid followed by binning lead to a more precise evaluation of the function. - - A full description of the iteration method can be found in the functions *array_via_func_from* and - *iterated_grid_from*. This function computes the result on a grid with a sub-size of 1, and uses its - shape to call the correct function. - - Parameters - ---------- - func : func - The function which is iterated over to compute a more precise evaluation. - obj : cls - The class the function belongs to. - grid_lower_sub_2d - The results computed by the function using a lower sub-grid size - """ - - unmasked_grid = self.mask.derive_grid.unmasked - - array_sub_1 = func(obj, unmasked_grid, *args, **kwargs) - - array_sub_1 = Array2D(values=array_sub_1, mask=self.mask).native - - if not np.any(array_sub_1): - return array_sub_1.slim - - iterated_array = np.zeros(shape=self.mask.shape_native) - - threshold_mask_lower_sub = self.mask - - for sub_size in self.sub_steps[:-1]: - array_higher_sub = self.array_at_sub_size_from( - func=func, cls=obj, mask=threshold_mask_lower_sub, sub_size=sub_size - ) - - try: - threshold_mask_higher_sub = self.threshold_mask_from( - array_lower_sub_2d=array_sub_1, - array_higher_sub_2d=array_higher_sub, - ) - - iterated_array = iterated_array_jit_from( - iterated_array=iterated_array, - threshold_mask_higher_sub=np.array(threshold_mask_higher_sub), - threshold_mask_lower_sub=np.array(threshold_mask_lower_sub), - array_higher_sub_2d=np.array(array_higher_sub), - ) - - except ZeroDivisionError: - return Array2D(values=iterated_array, mask=self.mask) - - if threshold_mask_higher_sub.is_all_true: - return Array2D(values=iterated_array, mask=self.mask) - - array_sub_1 = array_higher_sub - threshold_mask_lower_sub = threshold_mask_higher_sub - threshold_mask_higher_sub.pixel_scales = self.mask.pixel_scales - - array_higher_sub = self.array_at_sub_size_from( - func=func, - cls=obj, - mask=threshold_mask_lower_sub, - sub_size=self.sub_steps[-1], - *args, - **kwargs - ) - - iterated_array_2d = iterated_array + array_higher_sub - - return Array2D(values=iterated_array_2d, mask=self.mask) diff --git a/autoarray/operators/over_sampling/uniform.py b/autoarray/operators/over_sampling/uniform.py index 03318859..1bcd10cd 100644 --- a/autoarray/operators/over_sampling/uniform.py +++ b/autoarray/operators/over_sampling/uniform.py @@ -437,16 +437,6 @@ def binned_array_2d_from(self, array: Array2D) -> "Array2D": mask=self.mask, ) - def array_via_func_from(self, func, obj, *args, **kwargs): - over_sampled_grid = self.over_sampled_grid - - if obj is not None: - values = func(obj, over_sampled_grid, *args, **kwargs) - else: - values = func(over_sampled_grid, *args, **kwargs) - - return self.binned_array_2d_from(array=values) - @cached_property def sub_mask_native_for_sub_mask_slim(self) -> np.ndarray: """ diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index dfcec468..61f2d6b2 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -127,10 +127,6 @@ def __init__( - grid[3,4,0] = 1.5 - grid[3,4,1] = -0.5 - - - - **Grid2D Mapping:** Every set of (y,x) coordinates in a pixel of the grid maps to an unmasked pixel in the mask. For a uniform @@ -172,8 +168,17 @@ def __init__( grid_2d_util.check_grid_2d(grid_2d=values) - self.over_sampling = over_sampling - self.over_sampling_non_uniform = over_sampling_non_uniform + # self.over_sampling = over_sampling + # self.over_sampling_non_uniform = over_sampling_non_uniform + + from autoarray.operators.over_sampling.uniform import ( + OverSamplingUniform, + ) + + self.over_sampling = over_sampling or OverSamplingUniform(sub_size=4) + self.over_sampling_non_uniform = ( + over_sampling_non_uniform or OverSamplingUniform(sub_size=4) + ) @classmethod def no_mask( diff --git a/autoarray/structures/mock/mock_decorators.py b/autoarray/structures/mock/mock_decorators.py index 366a7867..876b456d 100644 --- a/autoarray/structures/mock/mock_decorators.py +++ b/autoarray/structures/mock/mock_decorators.py @@ -81,144 +81,11 @@ def ndarray_2d_yx_from(profile, grid, *args, **kwargs): return 2.0 * grid -class MockGridLikeIteratorObj: - def __init__(self): - self.centre = (0.0, 0.0) - - @property - def sersic_constant(self): - return ( - (2 * 2.0) - - (1.0 / 3.0) - + (4.0 / (405.0 * 2.0)) - + (46.0 / (25515.0 * 2.0**2)) - + (131.0 / (1148175.0 * 2.0**3)) - - (2194697.0 / (30690717750.0 * 2.0**4)) - ) - - def radial_grid_from(self, grid): - return np.sqrt(np.add(np.square(grid[:, 0]), np.square(grid[:, 1]))) - - def angle_to_profile_grid_from(self, grid_angles): - """The angle between each (y,x) coordinate on the grid and the profile, in radians. - - Parameters - ---------- - grid_angles - The angle theta counter-clockwise from the positive x-axis to each coordinate in radians. - """ - return np.cos(grid_angles), np.sin(grid_angles) - - def _cartesian_grid_via_radial_from(self, grid, radius): - """ - Convert a grid of (y,x) coordinates with their specified circular radii to their original (y,x) Cartesian - coordinates. - - Parameters - ---------- - grid - The (y, x) coordinates in the reference frame of the profile. - radius - The circular radius of each coordinate from the profile center. - """ - grid_angles = np.arctan2(grid[:, 0], grid[:, 1]) - cos_theta, sin_theta = self.angle_to_profile_grid_from(grid_angles=grid_angles) - return np.multiply(radius[:, None], np.vstack((sin_theta, cos_theta)).T) - - @over_sample - @decorators.to_array - def ndarray_1d_from(self, grid, *args, **kwargs) -> np.ndarray: - """ - Mock function mimicking the behaviour of a class function which given an input 1D grid, returns a 1D ndarray - of shape [total_masked_grid_pixels]. - - Such functions are common in **PyAutoGalaxy** for light and mass profile objects. - """ - grid_radii = self.radial_grid_from(grid=grid) - return np.exp( - np.multiply( - -self.sersic_constant, - np.add(np.power(np.divide(grid_radii, 0.2), 1.0 / 2.0), -1), - ) - ) - - @decorators.to_grid - def ndarray_2d_from(self, grid, *args, **kwargs): - """ - Mock function mimicking the behaviour of a class function which given an input grid, returns a 2D ndarray - of shape [total_masked_grid_pixels, 2]. - - Such functions are common in **PyAutoGalaxy** for light and mass profile objects. - """ - return self._cartesian_grid_via_radial_from( - grid=grid, radius=np.full(grid.shape[0], 2.0) - ) - - @decorators.to_vector_yx - def ndarray_yx_2d_from(self, grid, *args, **kwargs): - """ - Mock function mimicking the behaviour of a class function which given an input grid, returns a 2D ndarray - of shape [total_masked_grid_pixels] which represents a vector field. - - Such functions are common in **PyAutoGalaxy** for light and mass profile objects. - """ - return self._cartesian_grid_via_radial_from( - grid=grid, radius=np.full(grid.shape[0], 2.0) - ) - - @decorators.to_array - def ndarray_1d_list_from(self, grid, *args, **kwargs): - """ - Mock function mimicking the behaviour of a class function which given an input 1D grid, returns a list of 1D - ndarrays of shape [total_masked_grid_pixels]. - - Such functions are common in **PyAutoGalaxy** for light and mass profile objects. - """ - grid_radii = self.radial_grid_from(grid=grid) - return [ - np.exp( - np.multiply( - -self.sersic_constant, - np.add(np.power(np.divide(grid_radii, 0.2), 1.0 / 2.0), -1), - ) - ) - ] - - @decorators.to_grid - def ndarray_2d_list_from(self, grid, *args, **kwargs): - """ - Mock function mimicking the behaviour of a class function which given an input grid, returns a 2D list of - ndarrays of shape [total_masked_grid_pixels, 2]. - - Such functions are common in **PyAutoGalaxy** for light and mass profile objects. - """ - return [ - self._cartesian_grid_via_radial_from( - grid=grid, radius=np.full(grid.shape[0], 2.0) - ) - ] - - @decorators.to_vector_yx - def ndarray_yx_2d_list_from(self, grid, *args, **kwargs): - """ - Mock function mimicking the behaviour of a class function which given an input grid, returns a list of 2D - ndarrays of shape [total_masked_grid_pixels] which represents a vector field. - - Such functions are common in **PyAutoGalaxy** for light and mass profile objects. - """ - return [ - self._cartesian_grid_via_radial_from( - grid=grid, radius=np.full(grid.shape[0], 2.0) - ) - ] - - class MockGrid1DLikeObj: def __init__(self, centre=(0.0, 0.0), angle=0.0): self.centre = centre self.angle = angle - @over_sample @decorators.project_grid def ndarray_1d_from(self, grid, *args, **kwargs): return np.ones(shape=grid.shape[0]) diff --git a/test_autoarray/config/grids.yaml b/test_autoarray/config/grids.yaml index 33f7c08c..b3fe649c 100644 --- a/test_autoarray/config/grids.yaml +++ b/test_autoarray/config/grids.yaml @@ -1,8 +1,3 @@ -interpolate: - ndarray_1d_from_grid: - MockGridLikeIteratorObj: true - ndarray_2d_from_grid: - MockGridLikeIteratorObj: true # Certain light and mass profile calculations become ill defined at (0.0, 0.0) or close to this value. This can lead # to numerical issues in the calculation of the profile, for example a np.nan may arise, crashing the code. @@ -45,10 +40,8 @@ over_sampling: radial_factor_list: MockGrid1DLikeObj: [1.0] MockGrid2DLikeObj: [1.0] - MockGridLikeIteratorObj: [1.0] MockGridRadialMinimum: [1.0] sub_size_list: MockGrid1DLikeObj: [1, 1] MockGrid2DLikeObj: [1, 1] - MockGridLikeIteratorObj: [1, 1] MockGridRadialMinimum: [1, 1] \ No newline at end of file diff --git a/test_autoarray/dataset/abstract/test_dataset.py b/test_autoarray/dataset/abstract/test_dataset.py index 42cd37b2..81abd2fe 100644 --- a/test_autoarray/dataset/abstract/test_dataset.py +++ b/test_autoarray/dataset/abstract/test_dataset.py @@ -74,17 +74,6 @@ def test__grid__uses_mask_and_settings( assert (masked_imaging_7x7.grids.uniform == grid_2d_7x7).all() assert (masked_imaging_7x7.grids.uniform.slim == grid_2d_7x7).all() - masked_imaging_7x7 = ds.AbstractDataset( - data=masked_image_7x7, - noise_map=masked_noise_map_7x7, - over_sampling=aa.OverSamplingDataset(uniform=aa.OverSamplingIterate()), - ) - - assert isinstance( - masked_imaging_7x7.grids.uniform.over_sampling, aa.OverSamplingIterate - ) - assert (masked_imaging_7x7.grids.uniform == grid_2d_7x7).all() - def test__grids_pixelization__uses_mask_and_settings( image_7x7, @@ -99,12 +88,8 @@ def test__grids_pixelization__uses_mask_and_settings( masked_imaging_7x7 = ds.AbstractDataset( data=masked_image_7x7, noise_map=masked_noise_map_7x7, - over_sampling=aa.OverSamplingDataset( - pixelization=aa.OverSamplingIterate(sub_steps=[2, 4]) - ), ) - assert masked_imaging_7x7.grids.pixelization.over_sampling.sub_steps == [2, 4] assert (masked_imaging_7x7.grids.pixelization == grid_2d_7x7).all() assert (masked_imaging_7x7.grids.pixelization.slim == grid_2d_7x7).all() diff --git a/test_autoarray/operators/over_sample/test_decorator.py b/test_autoarray/operators/over_sample/test_decorator.py index 73fde84a..4052787d 100644 --- a/test_autoarray/operators/over_sample/test_decorator.py +++ b/test_autoarray/operators/over_sample/test_decorator.py @@ -24,7 +24,7 @@ def test__in_grid_2d__over_sample_uniform__out_ndarray_1d(): grid_2d = aa.Grid2D.from_mask(mask=mask, over_sampling=over_sampling) - obj = aa.m.MockGridLikeIteratorObj() + obj = aa.m.MockGrid2DLikeObj() ndarray_1d = obj.ndarray_1d_from(grid=grid_2d) @@ -48,76 +48,3 @@ def test__in_grid_2d__over_sample_uniform__out_ndarray_1d(): assert isinstance(ndarray_1d, aa.Array2D) assert (ndarray_1d == ndarray_1d_via_grid).all() - - -def test__in_grid_2d_over_sample_iterate__out_ndarray_1d__values_use_iteration(): - mask = aa.Mask2D( - mask=[ - [True, True, True, True, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - origin=(0.001, 0.001), - ) - - over_sampling = aa.OverSamplingIterate(fractional_accuracy=1.0, sub_steps=[2, 3]) - - grid_2d = aa.Grid2D.from_mask(mask=mask, over_sampling=over_sampling) - - obj = aa.m.MockGridLikeIteratorObj() - - ndarray_1d = obj.ndarray_1d_from(grid=grid_2d) - - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=3) - - values_sub_3 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - - assert ndarray_1d == pytest.approx(values_sub_3, 1.0e-4) - - grid_2d = aa.Grid2D.from_mask( - mask=mask, - over_sampling=aa.OverSamplingIterate( - fractional_accuracy=0.000001, sub_steps=[2, 4, 8, 16, 32] - ), - ) - - obj = aa.m.MockGridLikeIteratorObj() - - ndarray_1d = obj.ndarray_1d_from(grid=grid_2d) - - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=2) - - values_sub_2 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - - assert ndarray_1d == pytest.approx(values_sub_2, 1.0e-4) - - grid_2d = aa.Grid2D.from_mask( - mask=mask, - over_sampling=aa.OverSamplingIterate(fractional_accuracy=0.5, sub_steps=[2, 4]), - ) - - iterate_obj = aa.m.MockGridLikeIteratorObj() - - ndarray_1d = iterate_obj.ndarray_1d_from(grid=grid_2d) - - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=2) - values_sub_2 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=4) - values_sub_4 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - - assert ndarray_1d.native[1, 1] == values_sub_2.native[1, 1] - assert ndarray_1d.native[2, 2] != values_sub_2.native[2, 2] - - assert ndarray_1d.native[1, 1] != values_sub_4.native[1, 1] - assert ndarray_1d.native[2, 2] == values_sub_4.native[2, 2] diff --git a/test_autoarray/operators/over_sample/test_iterate.py b/test_autoarray/operators/over_sample/test_iterate.py deleted file mode 100644 index 7330eaec..00000000 --- a/test_autoarray/operators/over_sample/test_iterate.py +++ /dev/null @@ -1,230 +0,0 @@ -import numpy as np - -import autoarray as aa - -from autoarray.structures.mock.mock_decorators import ( - ndarray_1d_from, - ndarray_1d_zeros_from, - ndarray_2d_from, -) - - -def test__threshold_mask_from(): - mask = aa.Mask2D( - mask=[ - [True, True, True, True], - [True, False, False, True], - [True, False, False, True], - [True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - ) - - arr = aa.Array2D( - values=[ - [0.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 1.0, 0.0], - [0.0, 1.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - ], - mask=mask, - ) - - over_sampling = aa.OverSamplerIterate(mask=mask, fractional_accuracy=0.9999) - - threshold_mask = over_sampling.threshold_mask_from( - array_lower_sub_2d=arr.native, array_higher_sub_2d=arr.native - ) - - assert ( - threshold_mask - == np.array( - [ - [True, True, True, True], - [True, True, True, True], - [True, True, True, True], - [True, True, True, True], - ] - ) - ).all() - - mask_lower_sub = aa.Mask2D( - mask=[ - [True, True, True, True], - [True, False, False, True], - [True, False, False, True], - [True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - ) - - mask_higher_sub = aa.Mask2D( - mask=[ - [True, True, True, True], - [True, False, True, True], - [True, False, False, True], - [True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - ) - - over_sampling = aa.OverSamplerIterate(mask=mask, fractional_accuracy=0.5) - - array_lower_sub = aa.Array2D( - [ - [0.0, 0.0, 0.0, 0.0], - [0.0, 2.0, 2.0, 0.0], - [0.0, 2.0, 2.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - ], - mask=mask_lower_sub, - ) - - array_higher_sub = aa.Array2D( - [ - [0.0, 0.0, 0.0, 0.0], - [0.0, 5.0, 5.0, 0.0], - [0.0, 5.0, 5.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - ], - mask=mask_higher_sub, - ) - - threshold_mask = over_sampling.threshold_mask_from( - array_lower_sub_2d=array_lower_sub.native, - array_higher_sub_2d=array_higher_sub.native, - ) - - assert ( - threshold_mask - == np.array( - [ - [True, True, True, True], - [True, False, True, True], - [True, False, False, True], - [True, True, True, True], - ] - ) - ).all() - - -def test__array_via_func_from__extreme_fractional_accuracies_uses_last_or_first_sub(): - mask = aa.Mask2D( - mask=[ - [True, True, True, True, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - origin=(0.001, 0.001), - ) - - over_sampling = aa.OverSamplerIterate( - mask=mask, fractional_accuracy=1.0, sub_steps=[2, 3] - ) - - values = over_sampling.array_via_func_from( - func=ndarray_1d_from, - obj=None, - ) - - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=3) - - values_sub_3 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - - assert (values == values_sub_3).all() - - # This test ensures that if the fractional accuracy is met on the last sub_size jump (e.g. 2 doesnt meet it, - # but 3 does) that the sub_size of 3 is used. There was a bug where the mask was not updated correctly and the - # iterated array double counted the values. - - values = over_sampling.array_via_func_from( - func=ndarray_1d_from, - obj=None, - ) - - assert (values == values_sub_3).all() - - over_sampling = aa.OverSamplerIterate( - mask=mask, fractional_accuracy=0.000001, sub_steps=[2, 4, 8, 16, 32] - ) - - values = over_sampling.array_via_func_from( - func=ndarray_1d_from, - obj=None, - ) - - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=2) - - values_sub_2 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - - assert (values == values_sub_2).all() - - -def test__array_via_func_from__check_values_computed_to_fractional_accuracy(): - mask = aa.Mask2D( - mask=[ - [True, True, True, True, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - origin=(0.001, 0.001), - ) - - over_sampling = aa.OverSamplerIterate( - mask=mask, fractional_accuracy=0.5, sub_steps=[2, 4] - ) - - values = over_sampling.array_via_func_from( - func=ndarray_1d_from, - obj=None, - ) - - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=2) - values_sub_2 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=4) - values_sub_4 = over_sample_uniform.array_via_func_from( - func=ndarray_1d_from, obj=object - ) - - assert values.native[1, 1] == values_sub_2.native[1, 1] - assert values.native[2, 2] != values_sub_2.native[2, 2] - - assert values.native[1, 1] != values_sub_4.native[1, 1] - assert values.native[2, 2] == values_sub_4.native[2, 2] - - -def test__array_via_func_from__func_returns_all_zeros__iteration_terminated(): - mask = aa.Mask2D( - mask=[ - [True, True, True, True, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - origin=(0.001, 0.001), - ) - - over_sampling = aa.OverSamplerIterate( - mask=mask, fractional_accuracy=1.0, sub_steps=[2, 3] - ) - - values = over_sampling.array_via_func_from( - func=ndarray_1d_zeros_from, - obj=None, - ) - - assert (values == np.zeros((9,))).all() From 177bfdac0c8745f73435af3661549a75a2e6f57a Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 13:39:58 +0000 Subject: [PATCH 03/26] mid way through big refactor removing a lor of ocer sampoling nonsense, dedcorator unit test passes --- autoarray/__init__.py | 3 +- autoarray/dataset/grids.py | 3 +- autoarray/dataset/imaging/dataset.py | 2 +- autoarray/dataset/interferometer/dataset.py | 9 +- autoarray/dataset/over_sampling.py | 19 +- autoarray/fixtures.py | 22 +- .../pixelization/border_relocator.py | 8 +- .../pixelization/image_mesh/hilbert.py | 4 +- .../pixelization/mappers/abstract.py | 4 +- .../inversion/pixelization/mappers/factory.py | 4 +- autoarray/operators/over_sampling/abstract.py | 18 -- .../operators/over_sampling/decorator.py | 6 +- .../{uniform.py => over_sampler.py} | 242 ++---------------- ...er_sample_util.py => over_sampler_util.py} | 183 ++++++++++++- autoarray/structures/decorators/abstract.py | 10 +- .../structures/decorators/relocate_radial.py | 1 - autoarray/structures/grids/uniform_2d.py | 106 ++++---- autoarray/util/__init__.py | 2 +- .../dataset/abstract/test_dataset.py | 22 +- .../dataset/plot/test_imaging_plotters.py | 4 +- .../imaging/test_inversion_imaging_util.py | 6 +- .../inversion/inversion/test_abstract.py | 4 +- .../pixelization/mappers/test_abstract.py | 4 +- .../pixelization/mappers/test_delaunay.py | 2 +- .../pixelization/mappers/test_factory.py | 6 +- .../pixelization/mappers/test_rectangular.py | 4 +- .../pixelization/mappers/test_voronoi.py | 2 +- .../pixelization/test_border_relocator.py | 6 +- test_autoarray/inversion/test_linear_obj.py | 2 +- .../operators/over_sample/test_decorator.py | 8 +- .../operators/over_sample/test_uniform.py | 36 +-- 31 files changed, 333 insertions(+), 419 deletions(-) delete mode 100644 autoarray/operators/over_sampling/abstract.py rename autoarray/operators/over_sampling/{uniform.py => over_sampler.py} (55%) rename autoarray/operators/over_sampling/{over_sample_util.py => over_sampler_util.py} (68%) diff --git a/autoarray/__init__.py b/autoarray/__init__.py index ed3f1a46..4e3237c7 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -69,8 +69,7 @@ from .structures.arrays.irregular import ArrayIrregular from .structures.grids.uniform_1d import Grid1D from .structures.grids.uniform_2d import Grid2D -from .operators.over_sampling.uniform import OverSamplingUniform -from .operators.over_sampling.uniform import OverSamplerUniform +from .operators.over_sampling.over_sampler import OverSampler from .structures.grids.irregular_2d import Grid2DIrregular from .structures.grids.irregular_2d import Grid2DIrregularUniform from .structures.mesh.rectangular_2d import Mesh2DRectangular diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index 16aa90b0..1b86b44e 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -6,7 +6,6 @@ from autoarray.structures.grids.uniform_1d import Grid1D from autoarray.structures.grids.uniform_2d import Grid2D -from autoarray.operators.over_sampling.uniform import OverSamplingUniform from autoarray.inversion.pixelization.border_relocator import BorderRelocator from autoconf import cached_property @@ -72,7 +71,7 @@ def uniform(self) -> Union[Grid1D, Grid2D]: """ return Grid2D.from_mask( mask=self.mask, - over_sampling=self.over_sampling.uniform, + over_sampling=self.over_sampling.over_sampler, ) @cached_property diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index c3aa4faa..8ada9e4b 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -455,7 +455,7 @@ def apply_over_sampling( This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). """ - uniform = over_sampling.uniform or self.over_sampling.uniform + uniform = over_sampling.over_sampler or self.over_sampling.over_sampler non_uniform = over_sampling.non_uniform or self.over_sampling.non_uniform pixelization = over_sampling.pixelization or self.over_sampling.pixelization diff --git a/autoarray/dataset/interferometer/dataset.py b/autoarray/dataset/interferometer/dataset.py index caf78796..18d896bc 100644 --- a/autoarray/dataset/interferometer/dataset.py +++ b/autoarray/dataset/interferometer/dataset.py @@ -9,9 +9,6 @@ from autoarray.dataset.grids import GridsDataset from autoarray.dataset.over_sampling import OverSamplingDataset from autoarray.operators.transformer import TransformerNUFFT -from autoarray.operators.over_sampling.uniform import ( - OverSamplingUniform, -) from autoarray.structures.visibilities import Visibilities from autoarray.structures.visibilities import VisibilitiesNoiseMap @@ -78,9 +75,9 @@ def __init__( self.real_space_mask = real_space_mask over_sampling = OverSamplingDataset( - uniform=OverSamplingUniform(sub_size=1), - pixelization=OverSamplingUniform(sub_size=1), - non_uniform=OverSamplingUniform(sub_size=1), + uniform=1, + pixelization=1, + non_uniform=1, ) super().__init__( diff --git a/autoarray/dataset/over_sampling.py b/autoarray/dataset/over_sampling.py index 44d914c7..6bd3c5b9 100644 --- a/autoarray/dataset/over_sampling.py +++ b/autoarray/dataset/over_sampling.py @@ -1,10 +1,7 @@ import logging -from typing import Optional +from typing import Union -from autoarray.operators.over_sampling.abstract import AbstractOverSampling -from autoarray.operators.over_sampling.uniform import ( - OverSamplingUniform, -) +from autoarray.structures.arrays.uniform_2d import Array2D logger = logging.getLogger(__name__) @@ -12,9 +9,9 @@ class OverSamplingDataset: def __init__( self, - uniform: Optional[AbstractOverSampling] = None, - non_uniform: Optional[AbstractOverSampling] = None, - pixelization: Optional[AbstractOverSampling] = None, + uniform: Union[int, Array2D] = 4, + non_uniform: Union[int, Array2D] = 4, + pixelization: Union[int, Array2D] = 4, ): """ Customizes how over sampling calculations are performed using the grids of the data. @@ -60,6 +57,6 @@ def __init__( by **PyAutoLens** when the grid has been deflected and ray-traced and therefore some of the default over sampling schemes are not appropriate. """ - self.uniform = uniform or OverSamplingUniform(sub_size=4) - self.pixelization = pixelization or OverSamplingUniform(sub_size=4) - self.non_uniform = non_uniform or OverSamplingUniform(sub_size=4) + self.uniform = uniform + self.pixelization = pixelization + self.non_uniform = non_uniform diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 82bb9d9b..601333b0 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -152,9 +152,7 @@ def make_imaging_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=1) - ), + over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=1)), ) @@ -163,9 +161,7 @@ def make_imaging_7x7_sub_2(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=2) - ), + over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=2)), ) @@ -174,9 +170,7 @@ def make_imaging_covariance_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_covariance_matrix=make_noise_covariance_matrix_7x7(), - over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=1) - ), + over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=1)), ) @@ -185,9 +179,7 @@ def make_imaging_7x7_no_blur(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=1) - ), + over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=1)), ) @@ -196,9 +188,7 @@ def make_imaging_7x7_no_blur_sub_2(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=2) - ), + over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=2)), ) @@ -408,7 +398,7 @@ def make_voronoi_mesh_grid_9(): def make_over_sampler_2d_7x7(): - return aa.OverSamplerUniform(mask=make_mask_2d_7x7(), sub_size=2) + return aa.OverSampler(mask=make_mask_2d_7x7(), sub_size=2) def make_border_relocator_2d_7x7(): diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index da3cd86e..a0cb7425 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -11,7 +11,7 @@ from autoarray.mask import mask_2d_util -from autoarray.operators.over_sampling import over_sample_util +from autoarray.operators.over_sampling import over_sampler_util from autoarray.structures.grids import grid_2d_util @@ -53,7 +53,7 @@ def sub_slim_indexes_for_slim_index_via_mask_2d_from( sub_slim_indexes_for_slim_index = [[] for _ in range(total_pixels)] slim_index_for_sub_slim_indexes = ( - over_sample_util.slim_index_for_sub_slim_index_via_mask_2d_from( + over_sampler_util.slim_index_for_sub_slim_index_via_mask_2d_from( mask_2d=mask_2d, sub_size=np.array(sub_size) ).astype("int") ) @@ -104,7 +104,7 @@ def sub_border_pixel_slim_indexes_from( mask_2d=mask_2d, sub_size=sub_size ) - sub_grid_2d_slim = over_sample_util.grid_2d_slim_over_sampled_via_mask_from( + sub_grid_2d_slim = over_sampler_util.grid_2d_slim_over_sampled_via_mask_from( mask_2d=mask_2d, pixel_scales=(1.0, 1.0), sub_size=np.array(sub_size), @@ -181,7 +181,7 @@ def sub_border_slim(self) -> np.ndarray: @property def sub_grid(self): - return over_sample_util.grid_2d_slim_over_sampled_via_mask_from( + return over_sampler_util.grid_2d_slim_over_sampled_via_mask_from( mask_2d=np.array(self.mask), pixel_scales=self.mask.pixel_scales, sub_size=np.array(self.sub_size), diff --git a/autoarray/inversion/pixelization/image_mesh/hilbert.py b/autoarray/inversion/pixelization/image_mesh/hilbert.py index 0b7cc1bb..214f435a 100644 --- a/autoarray/inversion/pixelization/image_mesh/hilbert.py +++ b/autoarray/inversion/pixelization/image_mesh/hilbert.py @@ -9,7 +9,7 @@ from autoarray.inversion.pixelization.image_mesh.abstract_weighted import ( AbstractImageMeshWeighted, ) -from autoarray.operators.over_sampling.uniform import OverSamplerUniform +from autoarray.operators.over_sampling.over_sampler import OverSampler from autoarray.inversion.inversion.settings import SettingsInversion from autoarray.structures.grids.irregular_2d import Grid2DIrregular @@ -112,7 +112,7 @@ def super_resolution_grid_from(img_2d, mask, mask_radius, pixel_scales, sub_scal radius=mask_radius, ) - over_sampler = OverSamplerUniform(mask=new_mask, sub_size=sub_scale) + over_sampler = OverSampler(mask=new_mask, sub_size=sub_scale) new_grid = over_sampler.over_sampled_grid diff --git a/autoarray/inversion/pixelization/mappers/abstract.py b/autoarray/inversion/pixelization/mappers/abstract.py index dd45b899..b144c4a6 100644 --- a/autoarray/inversion/pixelization/mappers/abstract.py +++ b/autoarray/inversion/pixelization/mappers/abstract.py @@ -11,7 +11,7 @@ from autoarray.inversion.pixelization.border_relocator import BorderRelocator from autoarray.inversion.pixelization.mappers.mapper_grids import MapperGrids from autoarray.inversion.regularization.abstract import AbstractRegularization -from autoarray.operators.over_sampling.abstract import AbstractOverSampler +from autoarray.operators.over_sampling.over_sampler import OverSampler from autoarray.structures.arrays.uniform_2d import Array2D from autoarray.structures.grids.uniform_2d import Grid2D from autoarray.structures.mesh.abstract_2d import Abstract2DMesh @@ -26,7 +26,7 @@ def __init__( self, mapper_grids: MapperGrids, regularization: Optional[AbstractRegularization], - over_sampler: AbstractOverSampler, + over_sampler: OverSampler, border_relocator: BorderRelocator, run_time_dict: Optional[Dict] = None, ): diff --git a/autoarray/inversion/pixelization/mappers/factory.py b/autoarray/inversion/pixelization/mappers/factory.py index 8cdfa721..50f056c0 100644 --- a/autoarray/inversion/pixelization/mappers/factory.py +++ b/autoarray/inversion/pixelization/mappers/factory.py @@ -2,7 +2,7 @@ from autoarray.inversion.pixelization.mappers.mapper_grids import MapperGrids from autoarray.inversion.pixelization.border_relocator import BorderRelocator -from autoarray.operators.over_sampling.abstract import AbstractOverSampler +from autoarray.operators.over_sampling.over_sampler import OverSampler from autoarray.inversion.regularization.abstract import AbstractRegularization from autoarray.structures.mesh.rectangular_2d import Mesh2DRectangular from autoarray.structures.mesh.delaunay_2d import Mesh2DDelaunay @@ -12,7 +12,7 @@ def mapper_from( mapper_grids: MapperGrids, regularization: Optional[AbstractRegularization], - over_sampler: AbstractOverSampler, + over_sampler: OverSampler, border_relocator: Optional[BorderRelocator] = None, run_time_dict: Optional[Dict] = None, ): diff --git a/autoarray/operators/over_sampling/abstract.py b/autoarray/operators/over_sampling/abstract.py deleted file mode 100644 index a29dc117..00000000 --- a/autoarray/operators/over_sampling/abstract.py +++ /dev/null @@ -1,18 +0,0 @@ -from autoarray.numpy_wrapper import register_pytree_node_class - -from autoarray.mask.mask_2d import Mask2D - - -class AbstractOverSampling: - def over_sampler_from(self, mask: Mask2D) -> "AbstractOverSampler": - raise NotImplementedError() - - -@register_pytree_node_class -class AbstractOverSampler: - def tree_flatten(self): - return (self.mask,), () - - @classmethod - def tree_unflatten(cls, aux_data, children): - return cls(mask=children[0]) diff --git a/autoarray/operators/over_sampling/decorator.py b/autoarray/operators/over_sampling/decorator.py index 9c32cb29..1e1e5bd8 100644 --- a/autoarray/operators/over_sampling/decorator.py +++ b/autoarray/operators/over_sampling/decorator.py @@ -51,12 +51,10 @@ def wrapper( if isinstance(grid, Grid2DIrregular) or isinstance(grid, Grid1D): return func(obj=obj, grid=grid, *args, **kwargs) - over_sampled_grid = grid.over_sampler.over_sampled_grid - if obj is not None: - values = func(obj, over_sampled_grid, *args, **kwargs) + values = func(obj, grid.grid_over_sampled, *args, **kwargs) else: - values = func(over_sampled_grid, *args, **kwargs) + values = func(grid.grid_over_sampled, *args, **kwargs) return grid.over_sampler.binned_array_2d_from(array=values) diff --git a/autoarray/operators/over_sampling/uniform.py b/autoarray/operators/over_sampling/over_sampler.py similarity index 55% rename from autoarray/operators/over_sampling/uniform.py rename to autoarray/operators/over_sampling/over_sampler.py index 1bcd10cd..50838ac9 100644 --- a/autoarray/operators/over_sampling/uniform.py +++ b/autoarray/operators/over_sampling/over_sampler.py @@ -1,24 +1,21 @@ import numpy as np -from typing import List, Tuple, Union +from typing import Union from autoconf import conf from autoconf import cached_property +from autoarray.numpy_wrapper import register_pytree_node_class from autoarray.mask.mask_2d import Mask2D -from autoarray.operators.over_sampling.abstract import AbstractOverSampling -from autoarray.operators.over_sampling.abstract import AbstractOverSampler from autoarray.structures.arrays.uniform_2d import Array2D -from autoarray.structures.grids.irregular_2d import Grid2DIrregular -from autoarray.structures.grids.uniform_2d import Grid2D -from autoarray import exc -from autoarray.operators.over_sampling import over_sample_util +from autoarray.operators.over_sampling import over_sampler_util -class OverSamplingUniform(AbstractOverSampling): - def __init__(self, sub_size: Union[int, Array2D]): +@register_pytree_node_class +class OverSampler: + def __init__(self, mask: Mask2D, sub_size: Union[int, Array2D]): """ - Over samples grid calculations using a uniform sub-grid. + Over samples grid calculations using a uniform sub-grid. When a 2D grid of (y,x) coordinates is input into a function, the result is evaluated at every coordinate on the grid. When the grid is paired to a 2D image (e.g. an `Array2D`) the solution needs to approximate @@ -131,205 +128,8 @@ def __init__(self, sub_size: Union[int, Array2D]): 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`. - Parameters - ---------- - sub_size - The size (sub_size x sub_size) of each unmasked pixels sub-grid. - """ - - self.sub_size = sub_size - - @classmethod - def from_radial_bins( - cls, - grid: Grid2D, - sub_size_list: List[int], - radial_list: List[float], - centre_list: List[Tuple] = None, - ) -> "OverSamplingUniform": - """ - Returns an adaptive sub-grid size based on the radial distance of every pixel from the centre of the mask. - - The adaptive sub-grid size is computed as follows: - - 1) Compute the radial distance of every pixel in the mask from the centre of the mask (or input centres). - 2) For every pixel, determine the sub-grid size based on the radial distance of that pixel. For example, if - the first entry in `radial_list` is 0.5 and the first entry in `sub_size_list` 8, all pixels with a radial - distance less than 0.5 will have a sub-grid size of 8x8. - - This scheme can produce high sub-size values towards the centre of the mask, where the galaxy is brightest and - has the most rapidly changing light profile which requires a high sub-grid size to resolve accurately. - - If the data has multiple galaxies, the `centre_list` can be used to define the centre of each galaxy - and therefore increase the sub-grid size based on the light profile of each individual galaxy. - - Parameters - ---------- - mask - The mask defining the 2D region where the over-sampled grid is computed. - sub_size_list - The sub-grid size for every radial bin. - radial_list - The radial distance defining each bin, which are refeneced based on the previous entry. For example, if - the first entry is 0.5, the second 1.0 and the third 1.5, the adaptive sub-grid size will be between 0.5 - and 1.0 for the first sub-grid size, between 1.0 and 1.5 for the second sub-grid size, etc. - centre_list - A list of centres for each galaxy whose centres require higher sub-grid sizes. - - Returns - ------- - A uniform over-sampling object with an adaptive sub-grid size based on the radial distance of every pixel from - the centre of the mask. - """ - - if centre_list is None: - centre_list = [grid.mask.mask_centre] - - sub_size = np.zeros(grid.shape_slim) - - for centre in centre_list: - radial_grid = grid.distances_to_coordinate_from(coordinate=centre) - - sub_size_of_centre = over_sample_util.sub_size_radial_bins_from( - radial_grid=np.array(radial_grid), - sub_size_list=np.array(sub_size_list), - radial_list=np.array(radial_list), - ) - - sub_size = np.where( - sub_size_of_centre > sub_size, sub_size_of_centre, sub_size - ) - - sub_size = Array2D(values=sub_size, mask=grid.mask) - - return cls(sub_size=sub_size) - - @classmethod - def from_adaptive_scheme( - cls, grid: Grid2D, name: str, centre: Tuple[float, float] - ) -> "OverSamplingUniform": - """ - Returns a 2D grid whose over sampling is adaptive, placing a high number of sub-pixels in the regions of the - grid closest to the centre input (y,x) coordinates. - - This adaptive over sampling is primarily used in PyAutoGalaxy, to evaluate the image of a light profile - (e.g. a Sersic function) with high levels of sub gridding in its centre and lower levels of sub gridding - further away from the centre (saving computational time). - - The `autogalaxy_workspace` and `autolens_workspace` packages have guides called `over_sampling.ipynb` - which describe over sampling in more detail. - - The inputs `name` and `centre` typically correspond to the name of the light profile (e.g. `Sersic`) and - its `centre`, so that the adaptive over sampling parameters for that light profile are loaded from config - files and used to setup the grid. - - Parameters - ---------- - name - The name of the light profile, which is used to load the adaptive over sampling parameters from config files. - centre - The (y,x) centre of the adaptive over sampled grid, around which the highest sub-pixel resolution is placed. - - Returns - ------- - A new Grid2D with adaptive over sampling. - - """ - - if not grid.is_uniform: - raise exc.GridException( - "You cannot make an adaptive over-sampled grid from a non-uniform grid." - ) - - sub_size_list = conf.instance["grids"]["over_sampling"]["sub_size_list"][name] - radial_factor_list = conf.instance["grids"]["over_sampling"][ - "radial_factor_list" - ][name] - - centre = grid.geometry.scaled_coordinate_2d_to_scaled_at_pixel_centre_from( - scaled_coordinate_2d=centre - ) - - return OverSamplingUniform.from_radial_bins( - grid=grid, - sub_size_list=sub_size_list, - radial_list=[ - min(grid.pixel_scales) * radial_factor - for radial_factor in radial_factor_list - ], - centre_list=[centre], - ) - - @classmethod - def from_adapt( - cls, - data: Array2D, - noise_map: Array2D, - signal_to_noise_cut: float = 5.0, - sub_size_lower: int = 2, - sub_size_upper: int = 4, - ): - """ - Returns an adaptive sub-grid size based on the signal-to-noise of the data. - - The adaptive sub-grid size is computed as follows: - - 1) The signal-to-noise of every pixel is computed as the data divided by the noise-map. - 2) For all pixels with signal-to-noise above the signal-to-noise cut, the sub-grid size is set to the upper - value. For all other pixels, the sub-grid size is set to the lower value. - - This scheme can produce low sub-size values over entire datasets if the data has a low signal-to-noise. However, - just because the data has a low signal-to-noise does not mean that the sub-grid size should be low. - - To mitigate this, the signal-to-noise cut is set to the maximum signal-to-noise of the data divided by 2.0 if - it this value is below the signal-to-noise cut. - - Parameters - ---------- - data - The data which is to be fitted via a calculation using this over-sampling sub-grid. - noise_map - The noise-map of the data. - signal_to_noise_cut - The signal-to-noise cut which defines whether the sub-grid size is the upper or lower value. - sub_size_lower - The sub-grid size for pixels with signal-to-noise below the signal-to-noise cut. - sub_size_upper - The sub-grid size for pixels with signal-to-noise above the signal-to-noise cut. - - Returns - ------- - The adaptive sub-grid sizes. - """ - signal_to_noise = data / noise_map - - if np.max(signal_to_noise) < (2.0 * signal_to_noise_cut): - signal_to_noise_cut = np.max(signal_to_noise) / 2.0 - - sub_size = np.where( - signal_to_noise > signal_to_noise_cut, sub_size_upper, sub_size_lower - ) - - sub_size = Array2D(values=sub_size, mask=data.mask) - - return cls(sub_size=sub_size) - - def over_sampler_from(self, mask: Mask2D) -> "OverSamplerUniform": - return OverSamplerUniform( - mask=mask, - sub_size=self.sub_size, - ) - - -class OverSamplerUniform(AbstractOverSampler): - def __init__(self, mask: Mask2D, sub_size: Union[int, Array2D]): - """ - Over samples grid calculations using a uniform sub-grid. - - See the class `OverSamplingUniform` for a description of how the over-sampling works in full. - - The class `OverSamplingUniform` is used for the high level API, whereby this is where users input their - preferred over-sampling configuration. This class, `OverSamplerUniform`, contains the functionality + 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 which actually performs the over-sampling calculations, but is hidden from the user. Parameters @@ -348,6 +148,13 @@ def __init__(self, mask: Mask2D, sub_size: Union[int, Array2D]): self.sub_size = sub_size + def tree_flatten(self): + return (self.mask,), () + + @classmethod + def tree_unflatten(cls, aux_data, children): + return cls(mask=children[0]) + @property def sub_total(self): """ @@ -392,17 +199,6 @@ def sub_pixel_areas(self) -> np.ndarray: return sub_pixel_areas - @cached_property - def over_sampled_grid(self) -> Grid2DIrregular: - grid = 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=np.array(self.sub_size).astype("int"), - origin=self.mask.origin, - ) - - return Grid2DIrregular(values=grid) - def binned_array_2d_from(self, array: Array2D) -> "Array2D": """ Convenience method to access the binned-up array in its 1D representation, which is a Grid2D stored as an @@ -426,7 +222,7 @@ def binned_array_2d_from(self, array: Array2D) -> "Array2D": except AttributeError: pass - binned_array_2d = over_sample_util.binned_array_2d_from( + binned_array_2d = over_sampler_util.binned_array_2d_from( array_2d=np.array(array), mask_2d=np.array(self.mask), sub_size=np.array(self.sub_size).astype("int"), @@ -491,7 +287,7 @@ def sub_mask_native_for_sub_mask_slim(self) -> np.ndarray: print(derive_indexes_2d.sub_mask_native_for_sub_mask_slim) """ - return over_sample_util.native_sub_index_for_slim_sub_index_2d_from( + return over_sampler_util.native_sub_index_for_slim_sub_index_2d_from( mask_2d=self.mask.array, sub_size=np.array(self.sub_size) ).astype("int") @@ -544,6 +340,6 @@ def slim_for_sub_slim(self) -> np.ndarray: print(derive_indexes_2d.slim_for_sub_slim) """ - return over_sample_util.slim_index_for_sub_slim_index_via_mask_2d_from( + return over_sampler_util.slim_index_for_sub_slim_index_via_mask_2d_from( mask_2d=np.array(self.mask), sub_size=np.array(self.sub_size) ).astype("int") diff --git a/autoarray/operators/over_sampling/over_sample_util.py b/autoarray/operators/over_sampling/over_sampler_util.py similarity index 68% rename from autoarray/operators/over_sampling/over_sample_util.py rename to autoarray/operators/over_sampling/over_sampler_util.py index 3adc9207..0cf0fda7 100644 --- a/autoarray/operators/over_sampling/over_sample_util.py +++ b/autoarray/operators/over_sampling/over_sampler_util.py @@ -1,14 +1,18 @@ -from autoarray.numpy_wrapper import register_pytree_node_class - - +from __future__ import annotations import numpy as np -from typing import List, Tuple +from typing import TYPE_CHECKING, List, Tuple +from autoconf import conf + +from autoarray.structures.arrays.uniform_2d import Array2D -from autoarray.mask.mask_2d import Mask2D +if TYPE_CHECKING: + from autoarray.structures.grids.uniform_2d import Grid2D from autoarray.geometry import geometry_util from autoarray import numba_util from autoarray.mask import mask_2d_util + +from autoarray import exc from autoarray import type as ty @@ -340,7 +344,7 @@ def grid_2d_slim_over_sampled_via_mask_from( """ 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. + 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 @@ -487,3 +491,170 @@ def binned_array_2d_from( index += 1 return binned_array_2d_slim + + +def from_radial_bins( + grid: Grid2D, + sub_size_list: List[int], + radial_list: List[float], + centre_list: List[Tuple] = None, +) -> Array2D: + """ + Returns an adaptive sub-grid size based on the radial distance of every pixel from the centre of the mask. + + The adaptive sub-grid size is computed as follows: + + 1) Compute the radial distance of every pixel in the mask from the centre of the mask (or input centres). + 2) For every pixel, determine the sub-grid size based on the radial distance of that pixel. For example, if + the first entry in `radial_list` is 0.5 and the first entry in `sub_size_list` 8, all pixels with a radial + distance less than 0.5 will have a sub-grid size of 8x8. + + This scheme can produce high sub-size values towards the centre of the mask, where the galaxy is brightest and + has the most rapidly changing light profile which requires a high sub-grid size to resolve accurately. + + If the data has multiple galaxies, the `centre_list` can be used to define the centre of each galaxy + and therefore increase the sub-grid size based on the light profile of each individual galaxy. + + Parameters + ---------- + mask + The mask defining the 2D region where the over-sampled grid is computed. + sub_size_list + The sub-grid size for every radial bin. + radial_list + The radial distance defining each bin, which are refeneced based on the previous entry. For example, if + the first entry is 0.5, the second 1.0 and the third 1.5, the adaptive sub-grid size will be between 0.5 + and 1.0 for the first sub-grid size, between 1.0 and 1.5 for the second sub-grid size, etc. + centre_list + A list of centres for each galaxy whose centres require higher sub-grid sizes. + + Returns + ------- + A uniform over-sampling object with an adaptive sub-grid size based on the radial distance of every pixel from + the centre of the mask. + """ + + if centre_list is None: + centre_list = [grid.mask.mask_centre] + + sub_size = np.zeros(grid.shape_slim) + + for centre in centre_list: + radial_grid = grid.distances_to_coordinate_from(coordinate=centre) + + sub_size_of_centre = sub_size_radial_bins_from( + radial_grid=np.array(radial_grid), + sub_size_list=np.array(sub_size_list), + radial_list=np.array(radial_list), + ) + + sub_size = np.where(sub_size_of_centre > sub_size, sub_size_of_centre, sub_size) + + return Array2D(values=sub_size, mask=grid.mask) + + +def from_adaptive_scheme( + grid: Grid2D, name: str, centre: Tuple[float, float] +) -> Array2D: + """ + Returns a 2D grid whose over sampling is adaptive, placing a high number of sub-pixels in the regions of the + grid closest to the centre input (y,x) coordinates. + + This adaptive over sampling is primarily used in PyAutoGalaxy, to evaluate the image of a light profile + (e.g. a Sersic function) with high levels of sub gridding in its centre and lower levels of sub gridding + further away from the centre (saving computational time). + + The `autogalaxy_workspace` and `autolens_workspace` packages have guides called `over_sampling.ipynb` + which describe over sampling in more detail. + + The inputs `name` and `centre` typically correspond to the name of the light profile (e.g. `Sersic`) and + its `centre`, so that the adaptive over sampling parameters for that light profile are loaded from config + files and used to setup the grid. + + Parameters + ---------- + name + The name of the light profile, which is used to load the adaptive over sampling parameters from config files. + centre + The (y,x) centre of the adaptive over sampled grid, around which the highest sub-pixel resolution is placed. + + Returns + ------- + A new Grid2D with adaptive over sampling. + + """ + + if not grid.is_uniform: + raise exc.GridException( + "You cannot make an adaptive over-sampled grid from a non-uniform grid." + ) + + sub_size_list = conf.instance["grids"]["over_sampling"]["sub_size_list"][name] + radial_factor_list = conf.instance["grids"]["over_sampling"]["radial_factor_list"][ + name + ] + + centre = grid.geometry.scaled_coordinate_2d_to_scaled_at_pixel_centre_from( + scaled_coordinate_2d=centre + ) + + return from_radial_bins( + grid=grid, + sub_size_list=sub_size_list, + radial_list=[ + min(grid.pixel_scales) * radial_factor + for radial_factor in radial_factor_list + ], + centre_list=[centre], + ) + + +def from_adapt( + data: Array2D, + noise_map: Array2D, + signal_to_noise_cut: float = 5.0, + sub_size_lower: int = 2, + sub_size_upper: int = 4, +) -> Array2D: + """ + Returns an adaptive sub-grid size based on the signal-to-noise of the data. + + The adaptive sub-grid size is computed as follows: + + 1) The signal-to-noise of every pixel is computed as the data divided by the noise-map. + 2) For all pixels with signal-to-noise above the signal-to-noise cut, the sub-grid size is set to the upper + value. For all other pixels, the sub-grid size is set to the lower value. + + This scheme can produce low sub-size values over entire datasets if the data has a low signal-to-noise. However, + just because the data has a low signal-to-noise does not mean that the sub-grid size should be low. + + To mitigate this, the signal-to-noise cut is set to the maximum signal-to-noise of the data divided by 2.0 if + it this value is below the signal-to-noise cut. + + Parameters + ---------- + data + The data which is to be fitted via a calculation using this over-sampling sub-grid. + noise_map + The noise-map of the data. + signal_to_noise_cut + The signal-to-noise cut which defines whether the sub-grid size is the upper or lower value. + sub_size_lower + The sub-grid size for pixels with signal-to-noise below the signal-to-noise cut. + sub_size_upper + The sub-grid size for pixels with signal-to-noise above the signal-to-noise cut. + + Returns + ------- + The adaptive sub-grid sizes. + """ + signal_to_noise = data / noise_map + + if np.max(signal_to_noise) < (2.0 * signal_to_noise_cut): + signal_to_noise_cut = np.max(signal_to_noise) / 2.0 + + sub_size = np.where( + signal_to_noise > signal_to_noise_cut, sub_size_upper, sub_size_lower + ) + + return Array2D(values=sub_size, mask=data.mask) diff --git a/autoarray/structures/decorators/abstract.py b/autoarray/structures/decorators/abstract.py index 1401d1e7..b1abbca1 100644 --- a/autoarray/structures/decorators/abstract.py +++ b/autoarray/structures/decorators/abstract.py @@ -1,11 +1,9 @@ from typing import List, Union +import numpy as np + from autoarray.mask.mask_1d import Mask1D from autoarray.mask.mask_2d import Mask2D -from autoarray.operators.over_sampling.abstract import AbstractOverSampling -from autoarray.structures.arrays.irregular import ArrayIrregular -from autoarray.structures.arrays.uniform_1d import Array1D -from autoarray.structures.arrays.uniform_2d import Array2D from autoarray.structures.grids.uniform_1d import Grid1D from autoarray.structures.grids.irregular_2d import Grid2DIrregular from autoarray.structures.grids.uniform_2d import Grid2D @@ -62,8 +60,8 @@ def mask(self) -> Union[Mask1D, Mask2D]: return self.grid.mask @property - def over_sampling(self) -> AbstractOverSampling: - return self.grid.over_sampling + def over_sampling(self) -> np.ndarray: + return self.grid.over_sampling_size def via_grid_2d(self, result): raise NotImplementedError diff --git a/autoarray/structures/decorators/relocate_radial.py b/autoarray/structures/decorators/relocate_radial.py index ce9d2bf0..ca00bdd4 100644 --- a/autoarray/structures/decorators/relocate_radial.py +++ b/autoarray/structures/decorators/relocate_radial.py @@ -57,7 +57,6 @@ def wrapper( ------- The grid_like object whose coordinates are radially moved from (0.0, 0.0). """ - try: grid_radial_minimum = conf.instance["grids"]["radial_minimum"][ "radial_minimum" diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 61f2d6b2..85da447c 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -1,16 +1,12 @@ from __future__ import annotations import numpy as np from pathlib import Path -from typing import TYPE_CHECKING, List, Optional, Tuple, Union - -if TYPE_CHECKING: - from autoarray.operators.over_sampling.abstract import AbstractOverSampling +from typing import List, Optional, Tuple, Union from autoconf import conf from autoconf import cached_property from autoarray.mask.mask_2d import Mask2D -from autoarray.operators.over_sampling.abstract import AbstractOverSampler from autoarray.structures.abstract_structure import Structure from autoarray.structures.arrays.uniform_2d import Array2D from autoarray.structures.grids.irregular_2d import Grid2DIrregular @@ -18,6 +14,7 @@ from autoarray.structures.arrays import array_2d_util from autoarray.structures.grids import grid_2d_util from autoarray.geometry import geometry_util +from autoarray.operators.over_sampling import over_sampler_util from autoarray import type as ty @@ -28,8 +25,9 @@ def __init__( values: Union[np.ndarray, List], mask: Mask2D, store_native: bool = False, - over_sampling: Optional[AbstractOverSampling] = None, - over_sampling_non_uniform: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, + over_sampling_non_uniform: Union[int, Array2D] = 4, + grid_over_sampled: Optional[Grid2D] = None, *args, **kwargs, ): @@ -151,7 +149,7 @@ def __init__( store_native If True, the ndarray is stored in its native format [total_y_pixels, total_x_pixels, 2]. This avoids mapping large data arrays to and from the slim / native formats, which can be a computational bottleneck. - over_sampling + over_sampling_size The over sampling scheme, which divides the grid into a sub grid of smaller pixels when computing values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls into each pixel. @@ -168,17 +166,31 @@ def __init__( grid_2d_util.check_grid_2d(grid_2d=values) - # self.over_sampling = over_sampling - # self.over_sampling_non_uniform = over_sampling_non_uniform + # self.over_sampling_non_uniform = ( + # over_sampling_non_uniform or OverSampling(sub_size=4) + # ) - from autoarray.operators.over_sampling.uniform import ( - OverSamplingUniform, - ) + if isinstance(over_sampling_size, int): + sub_size = np.full( + fill_value=over_sampling_size, shape=mask.shape_slim + ).astype("int") - self.over_sampling = over_sampling or OverSamplingUniform(sub_size=4) - self.over_sampling_non_uniform = ( - over_sampling_non_uniform or OverSamplingUniform(sub_size=4) - ) + from autoarray.operators.over_sampling.over_sampler import OverSampler + + self.over_sampler = OverSampler(sub_size=sub_size, mask=mask) + + if grid_over_sampled is None: + self.grid_over_sampled = ( + over_sampler_util.grid_2d_slim_over_sampled_via_mask_from( + mask_2d=np.array(self.mask), + pixel_scales=self.mask.pixel_scales, + sub_size=np.array(self.over_sampler.sub_size).astype("int"), + origin=self.mask.origin, + ) + ) + + else: + self.grid_over_sampled = grid_over_sampled @classmethod def no_mask( @@ -187,7 +199,7 @@ def no_mask( pixel_scales: ty.PixelScales, shape_native: Tuple[int, int] = None, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the grid coordinates in 1D or 2D, automatically @@ -234,7 +246,7 @@ def no_mask( return Grid2D( values=values, mask=mask, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod @@ -245,7 +257,7 @@ def from_yx_1d( shape_native: Tuple[int, int], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the grid coordinates as 1D y and x values. @@ -309,7 +321,7 @@ def from_yx_1d( shape_native=shape_native, pixel_scales=pixel_scales, origin=origin, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod @@ -319,7 +331,7 @@ def from_yx_2d( x: Union[np.ndarray, List], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the grid coordinates as 2D y and x values. @@ -364,7 +376,7 @@ def from_yx_2d( values=np.stack((y, x), axis=-1), pixel_scales=pixel_scales, origin=origin, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod @@ -372,7 +384,7 @@ def from_extent( cls, extent: Tuple[float, float, float, float], shape_native: Tuple[int, int], - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the extent of the (y,x) grid coordinates as an input @@ -421,7 +433,7 @@ def from_extent( return Grid2D.no_mask( values=grid_2d, pixel_scales=pixel_scales, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod @@ -430,7 +442,7 @@ def uniform( shape_native: Tuple[int, int], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a `Grid2D` (see *Grid2D.__new__*) as a uniform grid of (y,x) values given an input `shape_native` and @@ -459,7 +471,7 @@ def uniform( shape_native=shape_native, pixel_scales=pixel_scales, origin=origin, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod @@ -468,7 +480,7 @@ def bounding_box( bounding_box: np.ndarray, shape_native: Tuple[int, int], buffer_around_corners: bool = False, - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) from an input bounding box with coordinates [y_min, y_max, x_min, x_max], @@ -511,14 +523,14 @@ def bounding_box( shape_native=shape_native, pixel_scales=pixel_scales, origin=origin, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod def from_mask( cls, mask: Mask2D, - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) from a mask, where only unmasked pixels are included in the grid (if the @@ -541,7 +553,7 @@ def from_mask( return Grid2D( values=grid_1d, mask=mask, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod @@ -550,7 +562,7 @@ def from_fits( file_path: Union[Path, str], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) from a mask, where only unmasked pixels are included in the grid (if the @@ -570,7 +582,7 @@ def from_fits( values=grid_2d, pixel_scales=pixel_scales, origin=origin, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) @classmethod @@ -578,7 +590,7 @@ def blurring_grid_from( cls, mask: Mask2D, kernel_shape_native: Tuple[int, int], - over_sampling: Optional[AbstractOverSampling] = None, + over_sampling_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Setup a blurring-grid from a mask, where a blurring grid consists of all pixels that are masked (and @@ -666,7 +678,7 @@ def blurring_grid_from( return cls.from_mask( mask=blurring_mask, - over_sampling=over_sampling, + over_sampling_size=over_sampling_size, ) def subtracted_from(self, offset: Tuple[(float, float), np.ndarray]) -> "Grid2D": @@ -682,9 +694,13 @@ def subtracted_from(self, offset: Tuple[(float, float), np.ndarray]) -> "Grid2D" return Grid2D( values=self - np.array(offset), mask=mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, ) + @property + def over_sampling_size(self): + return self.over_sampler.sub_size + @property def slim(self) -> "Grid2D": """ @@ -697,7 +713,7 @@ def slim(self) -> "Grid2D": return Grid2D( values=self, mask=self.mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, ) @property @@ -714,14 +730,10 @@ def native(self) -> "Grid2D": return Grid2D( values=self, mask=self.mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, store_native=True, ) - @cached_property - def over_sampler(self) -> AbstractOverSampler: - return self.over_sampling.over_sampler_from(mask=self.mask) - @property def flipped(self) -> "Grid2D": """ @@ -756,7 +768,7 @@ def grid_2d_via_deflection_grid_from(self, deflection_grid: "Grid2D") -> "Grid2D return Grid2D( values=self - deflection_grid, mask=self.mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, ) def blurring_grid_via_kernel_shape_from( @@ -773,12 +785,10 @@ def blurring_grid_via_kernel_shape_from( The 2D shape of the kernel which convolves signal from masked pixels to unmasked pixels. """ - from autoarray.operators.over_sampling.uniform import OverSamplingUniform - return Grid2D.blurring_grid_from( mask=self.mask, kernel_shape_native=kernel_shape_native, - over_sampling=OverSamplingUniform(sub_size=1), + over_sampling_size=1, ) def grid_with_coordinates_within_distance_removed_from( @@ -815,7 +825,7 @@ def grid_with_coordinates_within_distance_removed_from( return Grid2D.from_mask( mask=mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, ) def squared_distances_to_coordinate_from( @@ -1067,7 +1077,7 @@ def padded_grid_from(self, kernel_shape_native: Tuple[int, int]) -> "Grid2D": return Grid2D.from_mask( mask=padded_mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, ) @cached_property diff --git a/autoarray/util/__init__.py b/autoarray/util/__init__.py index 46b5afb5..7d8c90cf 100644 --- a/autoarray/util/__init__.py +++ b/autoarray/util/__init__.py @@ -2,7 +2,7 @@ from autoarray.geometry import geometry_util as geometry from autoarray.mask import mask_1d_util as mask_1d from autoarray.mask import mask_2d_util as mask_2d -from autoarray.operators.over_sampling import over_sample_util as over_sample +from autoarray.operators.over_sampling import over_sampler_util as over_sample from autoarray.structures.arrays import array_1d_util as array_1d from autoarray.structures.arrays import array_2d_util as array_2d from autoarray.structures.grids import grid_1d_util as grid_1d diff --git a/test_autoarray/dataset/abstract/test_dataset.py b/test_autoarray/dataset/abstract/test_dataset.py index 81abd2fe..8d7db370 100644 --- a/test_autoarray/dataset/abstract/test_dataset.py +++ b/test_autoarray/dataset/abstract/test_dataset.py @@ -65,9 +65,7 @@ def test__grid__uses_mask_and_settings( masked_imaging_7x7 = ds.AbstractDataset( data=masked_image_7x7, noise_map=masked_noise_map_7x7, - over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=2) - ), + over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=2)), ) assert isinstance(masked_imaging_7x7.grids.uniform, aa.Grid2D) @@ -97,8 +95,8 @@ def test__grids_pixelization__uses_mask_and_settings( data=masked_image_7x7, noise_map=masked_noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=2), - pixelization=aa.OverSamplingUniform(sub_size=4), + uniform=aa.OverSampling(sub_size=2), + pixelization=aa.OverSampling(sub_size=4), ), ) @@ -111,8 +109,8 @@ def test__grid_settings__sub_size(image_7x7, noise_map_7x7): data=image_7x7, noise_map=noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=2), - pixelization=aa.OverSamplingUniform(sub_size=4), + uniform=aa.OverSampling(sub_size=2), + pixelization=aa.OverSampling(sub_size=4), ), ) @@ -143,8 +141,8 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): data=image_7x7, noise_map=noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=2), - pixelization=aa.OverSamplingUniform(sub_size=2), + uniform=aa.OverSampling(sub_size=2), + pixelization=aa.OverSampling(sub_size=2), ), ) @@ -162,12 +160,12 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): dataset_7x7 = dataset_7x7.apply_over_sampling( over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSamplingUniform(sub_size=4), - pixelization=aa.OverSamplingUniform(sub_size=4), + uniform=aa.OverSampling(sub_size=4), + pixelization=aa.OverSampling(sub_size=4), ) ) - assert dataset_7x7.over_sampling.uniform.sub_size == 4 + assert dataset_7x7.over_sampling.over_sampler.sub_size == 4 assert dataset_7x7.over_sampling.pixelization.sub_size == 4 assert dataset_7x7.grids.uniform[0][0] == pytest.approx(3.0, 1.0e-4) diff --git a/test_autoarray/dataset/plot/test_imaging_plotters.py b/test_autoarray/dataset/plot/test_imaging_plotters.py index ebec9be4..84b9e420 100644 --- a/test_autoarray/dataset/plot/test_imaging_plotters.py +++ b/test_autoarray/dataset/plot/test_imaging_plotters.py @@ -20,9 +20,7 @@ def test__individual_attributes_are_output( visuals = aplt.Visuals2D(mask=mask_2d_7x7, positions=grid_2d_irregular_7x7_list) imaging_7x7 = imaging_7x7.apply_over_sampling( - over_sampling=aa.OverSamplingDataset( - non_uniform=aa.OverSamplingUniform(sub_size=1) - ) + over_sampling=aa.OverSamplingDataset(non_uniform=aa.OverSampling(sub_size=1)) ) dataset_plotter = aplt.ImagingPlotter( diff --git a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py index d2875b2b..c6ea9357 100644 --- a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py +++ b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py @@ -188,7 +188,7 @@ def test__data_vector_via_w_tilde_data_two_methods_agree(): # TODO : Use pytest.parameterize for sub_size in range(1, 3): - over_sampler = aa.OverSamplerUniform( + over_sampler = aa.OverSampler( mask=mask, sub_size=sub_size, ) @@ -272,7 +272,7 @@ def test__curvature_matrix_via_w_tilde_two_methods_agree(): source_plane_data_grid=mask.derive_grid.unmasked, ) - over_sampler = aa.OverSamplerUniform( + over_sampler = aa.OverSampler( mask=mask, sub_size=1, ) @@ -319,7 +319,7 @@ def test__curvature_matrix_via_w_tilde_preload_two_methods_agree(): pixelization = aa.mesh.Rectangular(shape=(20, 20)) for sub_size in range(1, 2, 3): - over_sampler = aa.OverSamplerUniform( + over_sampler = aa.OverSampler( mask=mask, sub_size=sub_size, ) diff --git a/test_autoarray/inversion/inversion/test_abstract.py b/test_autoarray/inversion/inversion/test_abstract.py index c2d8f967..48f89851 100644 --- a/test_autoarray/inversion/inversion/test_abstract.py +++ b/test_autoarray/inversion/inversion/test_abstract.py @@ -135,7 +135,7 @@ def test__curvature_matrix__via_w_tilde__identical_to_mapping(): reg = aa.reg.Constant(coefficient=1.0) - over_sampler = aa.OverSamplerUniform(mask=mask, sub_size=1) + over_sampler = aa.OverSampler(mask=mask, sub_size=1) mapper_0 = aa.Mapper( mapper_grids=mapper_grids_0, over_sampler=over_sampler, regularization=reg @@ -216,7 +216,7 @@ def test__curvature_matrix_via_w_tilde__includes_source_interpolation__identical reg = aa.reg.Constant(coefficient=1.0) - over_sampler = aa.OverSamplerUniform(mask=mask, sub_size=1) + over_sampler = aa.OverSampler(mask=mask, sub_size=1) mapper_0 = aa.Mapper( mapper_grids=mapper_grids_0, over_sampler=over_sampler, regularization=reg diff --git a/test_autoarray/inversion/pixelization/mappers/test_abstract.py b/test_autoarray/inversion/pixelization/mappers/test_abstract.py index 0adbe491..1baf2f5f 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_abstract.py +++ b/test_autoarray/inversion/pixelization/mappers/test_abstract.py @@ -140,7 +140,7 @@ def test__adaptive_pixel_signals_from___matches_util(grid_2d_7x7, image_7x7): ) pix_weights_for_sub_slim_index = np.ones((9, 1), dtype="int") - over_sampler = aa.OverSamplerUniform(mask=grid_2d_7x7.mask, sub_size=1) + over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) mapper = aa.m.MockMapper( source_plane_data_grid=grid_2d_7x7, @@ -214,7 +214,7 @@ def test__mapped_to_source_from(grid_2d_7x7): source_plane_mesh_grid=mesh_grid, ) - over_sampler = aa.OverSamplerUniform(mask=grid_2d_7x7.mask, sub_size=1) + over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) mapper = aa.Mapper( mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None diff --git a/test_autoarray/inversion/pixelization/mappers/test_delaunay.py b/test_autoarray/inversion/pixelization/mappers/test_delaunay.py index 970e8555..3aaaefb6 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_delaunay.py +++ b/test_autoarray/inversion/pixelization/mappers/test_delaunay.py @@ -17,7 +17,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_7x7): source_plane_mesh_grid=mesh_grid, ) - over_sampler = aa.OverSamplerUniform(mask=grid_2d_7x7.mask, sub_size=1) + over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) mapper = aa.Mapper( mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None diff --git a/test_autoarray/inversion/pixelization/mappers/test_factory.py b/test_autoarray/inversion/pixelization/mappers/test_factory.py index 2fb22771..d5c49661 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_factory.py +++ b/test_autoarray/inversion/pixelization/mappers/test_factory.py @@ -20,7 +20,7 @@ def test__rectangular_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - over_sampler = aa.OverSamplerUniform(mask=mask, sub_size=2) + over_sampler = aa.OverSampler(mask=mask, sub_size=2) over_sampled_grid = over_sampler.over_sampled_grid over_sampled_grid[0, 0] = -2.0 over_sampled_grid[0, 1] = 2.0 @@ -73,7 +73,7 @@ def test__delaunay_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - over_sampler = aa.OverSamplerUniform(mask=mask, sub_size=2) + over_sampler = aa.OverSampler(mask=mask, sub_size=2) over_sampled_grid = over_sampler.over_sampled_grid over_sampled_grid[0, 0] = -2.0 over_sampled_grid[0, 1] = 2.0 @@ -131,7 +131,7 @@ def test__voronoi_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - over_sampler = aa.OverSamplerUniform(mask=mask, sub_size=2) + over_sampler = aa.OverSampler(mask=mask, sub_size=2) over_sampled_grid = over_sampler.over_sampled_grid over_sampled_grid[0, 0] = -2.0 over_sampled_grid[0, 1] = 2.0 diff --git a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py index 9e948a10..2f945024 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py +++ b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py @@ -26,7 +26,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): mask=grid.mask, source_plane_data_grid=grid, source_plane_mesh_grid=mesh_grid ) - over_sampler = aa.OverSamplerUniform(mask=grid.mask, sub_size=1) + over_sampler = aa.OverSampler(mask=grid.mask, sub_size=1) mapper = aa.Mapper( mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None @@ -51,7 +51,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): def test__pixel_signals_from__matches_util(grid_2d_7x7, image_7x7): mesh_grid = aa.Mesh2DRectangular.overlay_grid(shape_native=(3, 3), grid=grid_2d_7x7) - over_sampler = aa.OverSamplerUniform(mask=grid_2d_7x7.mask, sub_size=1) + over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) mapper_grids = aa.MapperGrids( mask=grid_2d_7x7.mask, diff --git a/test_autoarray/inversion/pixelization/mappers/test_voronoi.py b/test_autoarray/inversion/pixelization/mappers/test_voronoi.py index f6a73523..9a84e7ef 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_voronoi.py +++ b/test_autoarray/inversion/pixelization/mappers/test_voronoi.py @@ -14,7 +14,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_7x7): values=source_plane_mesh_grid, ) - over_sampler = aa.OverSamplerUniform(mask=grid_2d_7x7.mask, sub_size=1) + over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) source_plane_mesh_grid = aa.Mesh2DVoronoi( values=source_plane_mesh_grid, diff --git a/test_autoarray/inversion/pixelization/test_border_relocator.py b/test_autoarray/inversion/pixelization/test_border_relocator.py index eb6cd330..aec9df59 100644 --- a/test_autoarray/inversion/pixelization/test_border_relocator.py +++ b/test_autoarray/inversion/pixelization/test_border_relocator.py @@ -325,7 +325,7 @@ def test__relocated_grid_from__inside_border_no_relocations(): shape_native=(30, 30), radius=1.0, pixel_scales=(0.1, 0.1) ) - over_sampling = aa.OverSamplerUniform( + over_sampling = aa.OverSampler( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) grid = over_sampling.over_sampled_grid @@ -345,7 +345,7 @@ def test__relocated_grid_from__outside_border_includes_relocations(): shape_native=(30, 30), radius=1.0, pixel_scales=(0.1, 0.1) ) - over_sampling = aa.OverSamplerUniform( + over_sampling = aa.OverSampler( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) grid = over_sampling.over_sampled_grid @@ -368,7 +368,7 @@ def test__relocated_grid_from__positive_origin_included_in_relocate(): centre=(1.0, 1.0), ) - over_sampling = aa.OverSamplerUniform( + over_sampling = aa.OverSampler( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) grid = over_sampling.over_sampled_grid diff --git a/test_autoarray/inversion/test_linear_obj.py b/test_autoarray/inversion/test_linear_obj.py index ab5b447c..d9361e30 100644 --- a/test_autoarray/inversion/test_linear_obj.py +++ b/test_autoarray/inversion/test_linear_obj.py @@ -44,7 +44,7 @@ def test__data_to_pix_unique_from(): mask = aa.Mask2D.all_false(shape_native=(1, 2), pixel_scales=0.1) - over_sampling = aa.OverSamplerUniform(mask=mask, sub_size=2) + over_sampling = aa.OverSampler(mask=mask, sub_size=2) grid = aa.Grid2D.uniform( shape_native=(1, 2), pixel_scales=0.1, over_sampling=over_sampling diff --git a/test_autoarray/operators/over_sample/test_decorator.py b/test_autoarray/operators/over_sample/test_decorator.py index 4052787d..7091ba66 100644 --- a/test_autoarray/operators/over_sample/test_decorator.py +++ b/test_autoarray/operators/over_sample/test_decorator.py @@ -20,15 +20,13 @@ def test__in_grid_2d__over_sample_uniform__out_ndarray_1d(): pixel_scales=(1.0, 1.0), ) - over_sampling = aa.OverSamplingUniform(sub_size=2) - - grid_2d = aa.Grid2D.from_mask(mask=mask, over_sampling=over_sampling) + grid_2d = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) obj = aa.m.MockGrid2DLikeObj() ndarray_1d = obj.ndarray_1d_from(grid=grid_2d) - over_sample_uniform = aa.OverSamplerUniform(mask=mask, sub_size=2) + 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 @@ -36,7 +34,7 @@ def test__in_grid_2d__over_sample_uniform__out_ndarray_1d(): mask_sub_2 = aa.Mask2D(mask=mask_sub_2, pixel_scales=(0.5, 0.5)) - grid = aa.Grid2D(values=over_sample_uniform.over_sampled_grid, mask=mask_sub_2) + grid = aa.Grid2D(values=grid_2d.grid_over_sampled, mask=mask_sub_2) ndarray_1d_via_grid = obj.ndarray_1d_from(grid=grid) diff --git a/test_autoarray/operators/over_sample/test_uniform.py b/test_autoarray/operators/over_sample/test_uniform.py index 06d7cc67..36123a5a 100644 --- a/test_autoarray/operators/over_sample/test_uniform.py +++ b/test_autoarray/operators/over_sample/test_uniform.py @@ -30,7 +30,7 @@ def test__from_sub_size_int(): pixel_scales=1.0, ) - over_sampling = aa.OverSamplerUniform(mask=mask, sub_size=2) + over_sampling = aa.OverSampler(mask=mask, sub_size=2) assert over_sampling.sub_size.slim == pytest.approx([2, 2, 2], 1.0e-4) assert over_sampling.sub_size.native == pytest.approx( @@ -44,7 +44,7 @@ def test__sub_pixel_areas(): pixel_scales=1.0, ) - over_sampling = aa.OverSamplerUniform(mask=mask, sub_size=np.array([1, 2])) + over_sampling = aa.OverSampler(mask=mask, sub_size=np.array([1, 2])) areas = over_sampling.sub_pixel_areas @@ -56,7 +56,7 @@ def test__from_manual_adapt_radial_bin(): grid = aa.Grid2D.from_mask(mask=mask) - over_sampling = aa.OverSamplingUniform.from_radial_bins( + over_sampling = aa.OverSampling.from_radial_bins( grid=grid, sub_size_list=[8, 4, 2], radial_list=[1.5, 2.5] ) assert over_sampling.sub_size.native == pytest.approx( @@ -78,7 +78,7 @@ def test__from_manual_adapt_radial_bin__centre_list_input(): grid = aa.Grid2D.from_mask(mask=mask) - over_sampling = aa.OverSamplingUniform.from_radial_bins( + over_sampling = aa.OverSampling.from_radial_bins( grid=grid, sub_size_list=[8, 4, 2], radial_list=[1.5, 2.5], @@ -108,7 +108,7 @@ def test__from_adapt(): data = aa.Array2D(values=[1.0, 2.0, 3.0], mask=mask) noise_map = aa.Array2D(values=[1.0, 2.0, 1.0], mask=mask) - over_sampling = aa.OverSamplingUniform.from_adapt( + over_sampling = aa.OverSampling.from_adapt( data=data, noise_map=noise_map, signal_to_noise_cut=1.5, @@ -118,7 +118,7 @@ def test__from_adapt(): assert over_sampling.sub_size == pytest.approx([2, 2, 4], 1.0e-4) - over_sampling = aa.OverSamplingUniform.from_adapt( + over_sampling = aa.OverSampling.from_adapt( data=data, noise_map=noise_map, signal_to_noise_cut=0.5, @@ -135,36 +135,20 @@ def test__sub_fraction(): pixel_scales=1.0, ) - over_sampling = aa.OverSamplerUniform( + over_sampling = aa.OverSampler( mask=mask, sub_size=aa.Array2D(values=[1, 2], mask=mask) ) assert over_sampling.sub_fraction.slim == pytest.approx([1.0, 0.25], 1.0e-4) -def test__over_sampled_grid(): - mask = aa.Mask2D( - mask=[[False, False], [True, True]], - pixel_scales=1.0, - ) - - over_sampling = aa.OverSamplerUniform( - mask=mask, sub_size=aa.Array2D(values=[1, 2], mask=mask) - ) - - assert over_sampling.over_sampled_grid.native == pytest.approx( - np.array([[0.5, -0.5], [0.75, 0.25], [0.75, 0.75], [0.25, 0.25], [0.25, 0.75]]), - 1.0e-4, - ) - - def test__binned_array_2d_from(): mask = aa.Mask2D( mask=[[False, False], [True, True]], pixel_scales=1.0, ) - over_sampling = aa.OverSamplerUniform( + over_sampling = aa.OverSampler( mask=mask, sub_size=aa.Array2D(values=[1, 2], mask=mask) ) @@ -182,7 +166,7 @@ def test__sub_mask_index_for_sub_mask_1d_index(): sub_size=2, ) - over_sampling = aa.OverSamplerUniform(mask=mask, 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( @@ -202,7 +186,7 @@ def test__slim_index_for_sub_slim_index(): sub_size=2, ) - over_sampling = aa.OverSamplerUniform(mask=mask, sub_size=2) + over_sampling = aa.OverSampler(mask=mask, sub_size=2) slim_index_for_sub_slim_index_util = ( aa.util.over_sample.slim_index_for_sub_slim_index_via_mask_2d_from( From b209f3e4017d8af94347bb8e8a899aaf4f1e3dcc Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 13:47:43 +0000 Subject: [PATCH 04/26] fix over sample util tests and rename --- .../pixelization/border_relocator.py | 8 +- ...er_sampler_util.py => over_sample_util.py} | 8 +- .../operators/over_sampling/over_sampler.py | 8 +- autoarray/structures/grids/uniform_2d.py | 4 +- autoarray/util/__init__.py | 2 +- .../over_sample/test_over_sample_util.py | 78 +++++++++++++++++++ .../{test_uniform.py => test_over_sampler.py} | 78 ------------------- 7 files changed, 93 insertions(+), 93 deletions(-) rename autoarray/operators/over_sampling/{over_sampler_util.py => over_sample_util.py} (96%) rename test_autoarray/operators/over_sample/{test_uniform.py => test_over_sampler.py} (61%) diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index a0cb7425..da3cd86e 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -11,7 +11,7 @@ from autoarray.mask import mask_2d_util -from autoarray.operators.over_sampling import over_sampler_util +from autoarray.operators.over_sampling import over_sample_util from autoarray.structures.grids import grid_2d_util @@ -53,7 +53,7 @@ def sub_slim_indexes_for_slim_index_via_mask_2d_from( sub_slim_indexes_for_slim_index = [[] for _ in range(total_pixels)] slim_index_for_sub_slim_indexes = ( - over_sampler_util.slim_index_for_sub_slim_index_via_mask_2d_from( + over_sample_util.slim_index_for_sub_slim_index_via_mask_2d_from( mask_2d=mask_2d, sub_size=np.array(sub_size) ).astype("int") ) @@ -104,7 +104,7 @@ def sub_border_pixel_slim_indexes_from( mask_2d=mask_2d, sub_size=sub_size ) - sub_grid_2d_slim = over_sampler_util.grid_2d_slim_over_sampled_via_mask_from( + sub_grid_2d_slim = over_sample_util.grid_2d_slim_over_sampled_via_mask_from( mask_2d=mask_2d, pixel_scales=(1.0, 1.0), sub_size=np.array(sub_size), @@ -181,7 +181,7 @@ def sub_border_slim(self) -> np.ndarray: @property def sub_grid(self): - return over_sampler_util.grid_2d_slim_over_sampled_via_mask_from( + return 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=np.array(self.sub_size), diff --git a/autoarray/operators/over_sampling/over_sampler_util.py b/autoarray/operators/over_sampling/over_sample_util.py similarity index 96% rename from autoarray/operators/over_sampling/over_sampler_util.py rename to autoarray/operators/over_sampling/over_sample_util.py index 0cf0fda7..e0e59566 100644 --- a/autoarray/operators/over_sampling/over_sampler_util.py +++ b/autoarray/operators/over_sampling/over_sample_util.py @@ -493,7 +493,7 @@ def binned_array_2d_from( return binned_array_2d_slim -def from_radial_bins( +def over_sample_size_via_radial_bins_from( grid: Grid2D, sub_size_list: List[int], radial_list: List[float], @@ -553,7 +553,7 @@ def from_radial_bins( return Array2D(values=sub_size, mask=grid.mask) -def from_adaptive_scheme( +def over_sample_size_via_adaptive_scheme_from( grid: Grid2D, name: str, centre: Tuple[float, float] ) -> Array2D: """ @@ -598,7 +598,7 @@ def from_adaptive_scheme( scaled_coordinate_2d=centre ) - return from_radial_bins( + return over_sample_size_via_radial_bins_from( grid=grid, sub_size_list=sub_size_list, radial_list=[ @@ -609,7 +609,7 @@ def from_adaptive_scheme( ) -def from_adapt( +def over_sample_size_via_adapt_from( data: Array2D, noise_map: Array2D, signal_to_noise_cut: float = 5.0, diff --git a/autoarray/operators/over_sampling/over_sampler.py b/autoarray/operators/over_sampling/over_sampler.py index 50838ac9..b826a5d7 100644 --- a/autoarray/operators/over_sampling/over_sampler.py +++ b/autoarray/operators/over_sampling/over_sampler.py @@ -8,7 +8,7 @@ from autoarray.mask.mask_2d import Mask2D from autoarray.structures.arrays.uniform_2d import Array2D -from autoarray.operators.over_sampling import over_sampler_util +from autoarray.operators.over_sampling import over_sample_util @register_pytree_node_class @@ -222,7 +222,7 @@ def binned_array_2d_from(self, array: Array2D) -> "Array2D": except AttributeError: pass - binned_array_2d = over_sampler_util.binned_array_2d_from( + binned_array_2d = over_sample_util.binned_array_2d_from( array_2d=np.array(array), mask_2d=np.array(self.mask), sub_size=np.array(self.sub_size).astype("int"), @@ -287,7 +287,7 @@ def sub_mask_native_for_sub_mask_slim(self) -> np.ndarray: print(derive_indexes_2d.sub_mask_native_for_sub_mask_slim) """ - return over_sampler_util.native_sub_index_for_slim_sub_index_2d_from( + return over_sample_util.native_sub_index_for_slim_sub_index_2d_from( mask_2d=self.mask.array, sub_size=np.array(self.sub_size) ).astype("int") @@ -340,6 +340,6 @@ def slim_for_sub_slim(self) -> np.ndarray: print(derive_indexes_2d.slim_for_sub_slim) """ - return over_sampler_util.slim_index_for_sub_slim_index_via_mask_2d_from( + return over_sample_util.slim_index_for_sub_slim_index_via_mask_2d_from( mask_2d=np.array(self.mask), sub_size=np.array(self.sub_size) ).astype("int") diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 85da447c..abd635be 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -14,7 +14,7 @@ from autoarray.structures.arrays import array_2d_util from autoarray.structures.grids import grid_2d_util from autoarray.geometry import geometry_util -from autoarray.operators.over_sampling import over_sampler_util +from autoarray.operators.over_sampling import over_sample_util from autoarray import type as ty @@ -181,7 +181,7 @@ def __init__( if grid_over_sampled is None: self.grid_over_sampled = ( - over_sampler_util.grid_2d_slim_over_sampled_via_mask_from( + 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=np.array(self.over_sampler.sub_size).astype("int"), diff --git a/autoarray/util/__init__.py b/autoarray/util/__init__.py index 7d8c90cf..46b5afb5 100644 --- a/autoarray/util/__init__.py +++ b/autoarray/util/__init__.py @@ -2,7 +2,7 @@ from autoarray.geometry import geometry_util as geometry from autoarray.mask import mask_1d_util as mask_1d from autoarray.mask import mask_2d_util as mask_2d -from autoarray.operators.over_sampling import over_sampler_util as over_sample +from autoarray.operators.over_sampling import over_sample_util as over_sample from autoarray.structures.arrays import array_1d_util as array_1d from autoarray.structures.arrays import array_2d_util as array_2d from autoarray.structures.grids import grid_1d_util as grid_1d 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 017900c5..c0552d8a 100644 --- a/test_autoarray/operators/over_sample/test_over_sample_util.py +++ b/test_autoarray/operators/over_sample/test_over_sample_util.py @@ -367,3 +367,81 @@ def test__grid_2d_slim_over_sampled_via_mask_from(): assert grid[0:4] == pytest.approx( np.array([[1.75, -0.5], [1.75, 2.5], [0.25, -0.5], [0.25, 2.5]]), 1e-4 ) + + +def test__from_manual_adapt_radial_bin(): + mask = aa.Mask2D.circular(shape_native=(5, 5), pixel_scales=2.0, radius=3.0) + + grid = aa.Grid2D.from_mask(mask=mask) + + sub_size = aa.util.over_sample.over_sample_size_via_radial_bins_from( + grid=grid, sub_size_list=[8, 4, 2], radial_list=[1.5, 2.5] + ) + assert sub_size.native == pytest.approx( + np.array( + [ + [0, 0, 0, 0, 0], + [0, 2, 4, 2, 0], + [0, 4, 8, 4, 0], + [0, 2, 4, 2, 0], + [0, 0, 0, 0, 0], + ] + ), + 1.0e-4, + ) + + +def test__from_manual_adapt_radial_bin__centre_list_input(): + mask = aa.Mask2D.circular(shape_native=(5, 5), pixel_scales=2.0, radius=3.0) + + grid = aa.Grid2D.from_mask(mask=mask) + + sub_size = aa.util.over_sample.over_sample_size_via_radial_bins_from( + grid=grid, + sub_size_list=[8, 4, 2], + radial_list=[1.5, 2.5], + centre_list=[(0.0, -2.0), (0.0, 2.0)], + ) + + assert sub_size.native == pytest.approx( + np.array( + [ + [0, 0, 0, 0, 0], + [0, 4, 2, 4, 0], + [0, 8, 4, 8, 0], + [0, 4, 2, 4, 0], + [0, 0, 0, 0, 0], + ] + ), + 1.0e-4, + ) + + +def test__from_adapt(): + mask = aa.Mask2D( + mask=[[True, True, True], [True, False, False], [True, True, False]], + pixel_scales=1.0, + ) + + data = aa.Array2D(values=[1.0, 2.0, 3.0], mask=mask) + noise_map = aa.Array2D(values=[1.0, 2.0, 1.0], mask=mask) + + sub_size = aa.util.over_sample.over_sample_size_via_adapt_from( + data=data, + noise_map=noise_map, + signal_to_noise_cut=1.5, + sub_size_lower=2, + sub_size_upper=4, + ) + + assert sub_size == pytest.approx([2, 2, 4], 1.0e-4) + + sub_size = aa.util.over_sample.over_sample_size_via_adapt_from( + data=data, + noise_map=noise_map, + signal_to_noise_cut=0.5, + sub_size_lower=2, + sub_size_upper=4, + ) + + assert sub_size == pytest.approx([4, 4, 4], 1.0e-4) diff --git a/test_autoarray/operators/over_sample/test_uniform.py b/test_autoarray/operators/over_sample/test_over_sampler.py similarity index 61% rename from test_autoarray/operators/over_sample/test_uniform.py rename to test_autoarray/operators/over_sample/test_over_sampler.py index 36123a5a..7da24d79 100644 --- a/test_autoarray/operators/over_sample/test_uniform.py +++ b/test_autoarray/operators/over_sample/test_over_sampler.py @@ -51,84 +51,6 @@ def test__sub_pixel_areas(): assert areas == pytest.approx([1.0, 0.25, 0.25, 0.25, 0.25], 1.0e-4) -def test__from_manual_adapt_radial_bin(): - mask = aa.Mask2D.circular(shape_native=(5, 5), pixel_scales=2.0, radius=3.0) - - grid = aa.Grid2D.from_mask(mask=mask) - - over_sampling = aa.OverSampling.from_radial_bins( - grid=grid, sub_size_list=[8, 4, 2], radial_list=[1.5, 2.5] - ) - assert over_sampling.sub_size.native == pytest.approx( - np.array( - [ - [0, 0, 0, 0, 0], - [0, 2, 4, 2, 0], - [0, 4, 8, 4, 0], - [0, 2, 4, 2, 0], - [0, 0, 0, 0, 0], - ] - ), - 1.0e-4, - ) - - -def test__from_manual_adapt_radial_bin__centre_list_input(): - mask = aa.Mask2D.circular(shape_native=(5, 5), pixel_scales=2.0, radius=3.0) - - grid = aa.Grid2D.from_mask(mask=mask) - - over_sampling = aa.OverSampling.from_radial_bins( - grid=grid, - sub_size_list=[8, 4, 2], - radial_list=[1.5, 2.5], - centre_list=[(0.0, -2.0), (0.0, 2.0)], - ) - - assert over_sampling.sub_size.native == pytest.approx( - np.array( - [ - [0, 0, 0, 0, 0], - [0, 4, 2, 4, 0], - [0, 8, 4, 8, 0], - [0, 4, 2, 4, 0], - [0, 0, 0, 0, 0], - ] - ), - 1.0e-4, - ) - - -def test__from_adapt(): - mask = aa.Mask2D( - mask=[[True, True, True], [True, False, False], [True, True, False]], - pixel_scales=1.0, - ) - - data = aa.Array2D(values=[1.0, 2.0, 3.0], mask=mask) - noise_map = aa.Array2D(values=[1.0, 2.0, 1.0], mask=mask) - - over_sampling = aa.OverSampling.from_adapt( - data=data, - noise_map=noise_map, - signal_to_noise_cut=1.5, - sub_size_lower=2, - sub_size_upper=4, - ) - - assert over_sampling.sub_size == pytest.approx([2, 2, 4], 1.0e-4) - - over_sampling = aa.OverSampling.from_adapt( - data=data, - noise_map=noise_map, - signal_to_noise_cut=0.5, - sub_size_lower=2, - sub_size_upper=4, - ) - - assert over_sampling.sub_size == pytest.approx([4, 4, 4], 1.0e-4) - - def test__sub_fraction(): mask = aa.Mask2D( mask=[[False, False], [True, True]], From cdda058ac607d140afe6935ad038a9b0a6effbc1 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 14:29:56 +0000 Subject: [PATCH 05/26] structure tests pass --- autoarray/fit/mock/mock_fit_imaging.py | 4 ++-- autoarray/fit/mock/mock_fit_interferometer.py | 8 ++++++-- autoarray/operators/over_sampling/over_sample_util.py | 2 +- autoarray/structures/grids/uniform_2d.py | 10 ++++++---- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/autoarray/fit/mock/mock_fit_imaging.py b/autoarray/fit/mock/mock_fit_imaging.py index 81df04a0..181d3d56 100644 --- a/autoarray/fit/mock/mock_fit_imaging.py +++ b/autoarray/fit/mock/mock_fit_imaging.py @@ -8,7 +8,7 @@ class MockFitImaging(FitImaging): def __init__( self, - dataset=MockDataset(), + dataset: Optional[MockDataset] = None, dataset_model: Optional[DatasetModel] = None, use_mask_in_fit: bool = False, noise_map=None, @@ -18,7 +18,7 @@ def __init__( run_time_dict: Optional[Dict] = None, ): super().__init__( - dataset=dataset, + dataset=dataset or MockDataset(), dataset_model=dataset_model, use_mask_in_fit=use_mask_in_fit, run_time_dict=run_time_dict, diff --git a/autoarray/fit/mock/mock_fit_interferometer.py b/autoarray/fit/mock/mock_fit_interferometer.py index 40378b53..8d00dad9 100644 --- a/autoarray/fit/mock/mock_fit_interferometer.py +++ b/autoarray/fit/mock/mock_fit_interferometer.py @@ -1,3 +1,5 @@ +from typing import Optional + from autoarray.dataset.mock.mock_dataset import MockDataset from autoarray.fit.fit_interferometer import FitInterferometer @@ -5,13 +7,15 @@ class MockFitInterferometer(FitInterferometer): def __init__( self, - dataset=MockDataset(), + dataset: Optional[MockDataset] = None, use_mask_in_fit: bool = False, model_data=None, inversion=None, noise_map=None, ): - super().__init__(dataset=dataset, use_mask_in_fit=use_mask_in_fit) + super().__init__( + dataset=dataset or MockDataset(), use_mask_in_fit=use_mask_in_fit + ) self._model_data = model_data self._inversion = inversion diff --git a/autoarray/operators/over_sampling/over_sample_util.py b/autoarray/operators/over_sampling/over_sample_util.py index e0e59566..8f31bc65 100644 --- a/autoarray/operators/over_sampling/over_sample_util.py +++ b/autoarray/operators/over_sampling/over_sample_util.py @@ -334,7 +334,7 @@ def sub_size_radial_bins_from( return sub_size -@numba_util.jit() +# @numba_util.jit() def grid_2d_slim_over_sampled_via_mask_from( mask_2d: np.ndarray, pixel_scales: ty.PixelScales, diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index abd635be..1475b703 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -171,13 +171,15 @@ def __init__( # ) if isinstance(over_sampling_size, int): - sub_size = np.full( + over_sampling_size = np.full( fill_value=over_sampling_size, shape=mask.shape_slim ).astype("int") + over_sampling_size = Array2D(values=over_sampling_size, mask=mask) + from autoarray.operators.over_sampling.over_sampler import OverSampler - self.over_sampler = OverSampler(sub_size=sub_size, mask=mask) + self.over_sampler = OverSampler(sub_size=over_sampling_size, mask=mask) if grid_over_sampled is None: self.grid_over_sampled = ( @@ -825,7 +827,7 @@ def grid_with_coordinates_within_distance_removed_from( return Grid2D.from_mask( mask=mask, - over_sampling_size=self.over_sampling_size, + over_sampling_size=self.over_sampling_size.apply_mask(mask=mask), ) def squared_distances_to_coordinate_from( @@ -1077,7 +1079,7 @@ def padded_grid_from(self, kernel_shape_native: Tuple[int, int]) -> "Grid2D": return Grid2D.from_mask( mask=padded_mask, - over_sampling_size=self.over_sampling_size, + over_sampling_size=4, ) @cached_property From a5ddad2925a70c50ddc52eab932848627b884106 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 14:42:40 +0000 Subject: [PATCH 06/26] fix dataset tests --- autoarray/dataset/grids.py | 18 ++---------- autoarray/dataset/imaging/dataset.py | 2 +- autoarray/dataset/plot/imaging_plotters.py | 6 ++-- autoarray/fixtures.py | 10 +++---- autoarray/structures/decorators/to_grid.py | 4 +-- .../dataset/abstract/test_dataset.py | 28 +++++++++---------- .../dataset/plot/test_imaging_plotters.py | 2 +- 7 files changed, 29 insertions(+), 41 deletions(-) diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index 1b86b44e..8e30c2d1 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -71,7 +71,7 @@ def uniform(self) -> Union[Grid1D, Grid2D]: """ return Grid2D.from_mask( mask=self.mask, - over_sampling=self.over_sampling.over_sampler, + over_sampling_size=self.over_sampling.uniform, ) @cached_property @@ -95,7 +95,7 @@ def non_uniform(self) -> Optional[Union[Grid1D, Grid2D]]: """ return Grid2D.from_mask( mask=self.mask, - over_sampling=self.over_sampling.non_uniform, + over_sampling_size=self.over_sampling.non_uniform, ) @cached_property @@ -116,7 +116,7 @@ def pixelization(self) -> Grid2D: """ return Grid2D.from_mask( mask=self.mask, - over_sampling=self.over_sampling.pixelization, + over_sampling_size=self.over_sampling.pixelization, ) @cached_property @@ -143,18 +143,6 @@ def blurring(self) -> Optional[Grid2D]: kernel_shape_native=self.psf.shape_native, ) - @cached_property - def over_sampler_uniform(self): - return self.uniform.over_sampling.over_sampler_from(mask=self.mask) - - @cached_property - def over_sampler_non_uniform(self): - return self.non_uniform.over_sampling.over_sampler_from(mask=self.mask) - - @cached_property - def over_sampler_pixelization(self): - return self.pixelization.over_sampling.over_sampler_from(mask=self.mask) - @cached_property def border_relocator(self) -> BorderRelocator: return BorderRelocator( diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 8ada9e4b..c3aa4faa 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -455,7 +455,7 @@ def apply_over_sampling( This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). """ - uniform = over_sampling.over_sampler or self.over_sampling.over_sampler + uniform = over_sampling.uniform or self.over_sampling.uniform non_uniform = over_sampling.non_uniform or self.over_sampling.non_uniform pixelization = over_sampling.pixelization or self.over_sampling.pixelization diff --git a/autoarray/dataset/plot/imaging_plotters.py b/autoarray/dataset/plot/imaging_plotters.py index 57fb92d8..f758cf59 100644 --- a/autoarray/dataset/plot/imaging_plotters.py +++ b/autoarray/dataset/plot/imaging_plotters.py @@ -127,7 +127,7 @@ def figures_2d( if over_sampling: self.mat_plot_2d.plot_array( - array=self.dataset.grids.over_sampler_uniform.sub_size, + array=self.dataset.grids.uniform.over_sampling_size, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( title=title_str or f"Over Sampling (Uniform)", @@ -138,7 +138,7 @@ def figures_2d( if over_sampling_non_uniform: self.mat_plot_2d.plot_array( - array=self.dataset.grids.over_sampler_non_uniform.sub_size, + array=self.dataset.grids.non_uniform.over_sampling_size, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( title=title_str or f"Over Sampling (Non Uniform)", @@ -149,7 +149,7 @@ def figures_2d( if over_sampling_pixelization: self.mat_plot_2d.plot_array( - array=self.dataset.grids.over_sampler_pixelization.sub_size, + array=self.dataset.grids.pixelization.over_sampling_size, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( title=title_str or f"Over Sampling (Pixelization)", diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 601333b0..9cceef5b 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -152,7 +152,7 @@ def make_imaging_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=1)), + over_sampling=aa.OverSamplingDataset(uniform=1), ) @@ -161,7 +161,7 @@ def make_imaging_7x7_sub_2(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=2)), + over_sampling=aa.OverSamplingDataset(uniform=2), ) @@ -170,7 +170,7 @@ def make_imaging_covariance_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_covariance_matrix=make_noise_covariance_matrix_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=1)), + over_sampling=aa.OverSamplingDataset(uniform=1), ) @@ -179,7 +179,7 @@ def make_imaging_7x7_no_blur(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=1)), + over_sampling=aa.OverSamplingDataset(uniform=1), ) @@ -188,7 +188,7 @@ def make_imaging_7x7_no_blur_sub_2(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=2)), + over_sampling=aa.OverSamplingDataset(uniform=2), ) diff --git a/autoarray/structures/decorators/to_grid.py b/autoarray/structures/decorators/to_grid.py index 3b70b1b2..706397fc 100644 --- a/autoarray/structures/decorators/to_grid.py +++ b/autoarray/structures/decorators/to_grid.py @@ -25,13 +25,13 @@ def via_grid_2d(self, result) -> Union[Grid2D, List[Grid2D]]: return Grid2D( values=result, mask=self.mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, ) return [ Grid2D( values=res, mask=self.mask, - over_sampling=self.over_sampling, + over_sampling_size=self.over_sampling_size, ) for res in result ] diff --git a/test_autoarray/dataset/abstract/test_dataset.py b/test_autoarray/dataset/abstract/test_dataset.py index 8d7db370..05350fee 100644 --- a/test_autoarray/dataset/abstract/test_dataset.py +++ b/test_autoarray/dataset/abstract/test_dataset.py @@ -65,7 +65,7 @@ def test__grid__uses_mask_and_settings( masked_imaging_7x7 = ds.AbstractDataset( data=masked_image_7x7, noise_map=masked_noise_map_7x7, - over_sampling=aa.OverSamplingDataset(uniform=aa.OverSampling(sub_size=2)), + over_sampling=aa.OverSamplingDataset(uniform=2), ) assert isinstance(masked_imaging_7x7.grids.uniform, aa.Grid2D) @@ -95,13 +95,13 @@ def test__grids_pixelization__uses_mask_and_settings( data=masked_image_7x7, noise_map=masked_noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSampling(sub_size=2), - pixelization=aa.OverSampling(sub_size=4), + uniform=2, + pixelization=4, ), ) assert isinstance(masked_imaging_7x7.grids.pixelization, aa.Grid2D) - assert masked_imaging_7x7.grids.pixelization.over_sampling.sub_size == 4 + assert masked_imaging_7x7.grids.pixelization.over_sampling_size[0] == 4 def test__grid_settings__sub_size(image_7x7, noise_map_7x7): @@ -109,13 +109,13 @@ def test__grid_settings__sub_size(image_7x7, noise_map_7x7): data=image_7x7, noise_map=noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSampling(sub_size=2), - pixelization=aa.OverSampling(sub_size=4), + uniform=2, + pixelization=4, ), ) - assert dataset_7x7.grids.uniform.over_sampling.sub_size == 2 - assert dataset_7x7.grids.pixelization.over_sampling.sub_size == 4 + assert dataset_7x7.grids.uniform.over_sampling_size[0] == 2 + assert dataset_7x7.grids.pixelization.over_sampling_size[0] == 4 def test__new_imaging_with_arrays_trimmed_via_kernel_shape(): @@ -141,8 +141,8 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): data=image_7x7, noise_map=noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSampling(sub_size=2), - pixelization=aa.OverSampling(sub_size=2), + uniform=2, + pixelization=2, ), ) @@ -160,13 +160,13 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): dataset_7x7 = dataset_7x7.apply_over_sampling( over_sampling=aa.OverSamplingDataset( - uniform=aa.OverSampling(sub_size=4), - pixelization=aa.OverSampling(sub_size=4), + uniform=4, + pixelization=4, ) ) - assert dataset_7x7.over_sampling.over_sampler.sub_size == 4 - assert dataset_7x7.over_sampling.pixelization.sub_size == 4 + assert dataset_7x7.over_sampling.uniform == 4 + assert dataset_7x7.over_sampling.pixelization == 4 assert dataset_7x7.grids.uniform[0][0] == pytest.approx(3.0, 1.0e-4) assert dataset_7x7.grids.pixelization[0][0] == pytest.approx(3.0, 1.0e-4) diff --git a/test_autoarray/dataset/plot/test_imaging_plotters.py b/test_autoarray/dataset/plot/test_imaging_plotters.py index 84b9e420..65d1f9a0 100644 --- a/test_autoarray/dataset/plot/test_imaging_plotters.py +++ b/test_autoarray/dataset/plot/test_imaging_plotters.py @@ -20,7 +20,7 @@ def test__individual_attributes_are_output( visuals = aplt.Visuals2D(mask=mask_2d_7x7, positions=grid_2d_irregular_7x7_list) imaging_7x7 = imaging_7x7.apply_over_sampling( - over_sampling=aa.OverSamplingDataset(non_uniform=aa.OverSampling(sub_size=1)) + over_sampling=aa.OverSamplingDataset(non_uniform=1) ) dataset_plotter = aplt.ImagingPlotter( From eabb866d786cc918632cd1391e56902cec078098 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 15:33:45 +0000 Subject: [PATCH 07/26] most tests fixed --- autoarray/dataset/grids.py | 2 +- autoarray/fixtures.py | 18 ++++---- autoarray/inversion/linear_obj/func_list.py | 2 +- .../pixelization/image_mesh/hilbert.py | 2 +- .../inversion/plot/inversion_plotters.py | 4 +- autoarray/inversion/plot/mapper_plotters.py | 2 +- .../over_sampling/over_sample_util.py | 2 +- .../operators/over_sampling/over_sampler.py | 13 ++++++ autoarray/plot/mat_plot/two_d.py | 2 +- autoarray/structures/decorators/abstract.py | 2 +- .../imaging/test_inversion_imaging_util.py | 30 ++++++------- .../pixelization/mappers/test_factory.py | 24 +++++------ .../pixelization/test_border_relocator.py | 42 +++++++++++++------ test_autoarray/inversion/test_linear_obj.py | 4 +- 14 files changed, 88 insertions(+), 61 deletions(-) diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index 8e30c2d1..16e3c4d4 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -146,7 +146,7 @@ def blurring(self) -> Optional[Grid2D]: @cached_property def border_relocator(self) -> BorderRelocator: return BorderRelocator( - mask=self.mask, sub_size=self.pixelization.over_sampling.sub_size + mask=self.mask, sub_size=self.pixelization.over_sampling_size ) diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 9cceef5b..2ae6c3bc 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -89,6 +89,10 @@ def make_grid_2d_7x7(): return aa.Grid2D.from_mask(mask=make_mask_2d_7x7()) +def make_grid_2d_sub_2_7x7(): + return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sampling_size=2) + + def make_grid_2d_7x7_simple(): grid_2d_7x7 = make_grid_2d_7x7() grid_2d_7x7[0] = np.array([1.0, 1.0]) @@ -351,7 +355,7 @@ def make_regularization_matern_kernel(): def make_rectangular_mesh_grid_3x3(): return aa.Mesh2DRectangular.overlay_grid( - grid=make_over_sampler_2d_7x7().over_sampled_grid, shape_native=(3, 3) + grid=make_grid_2d_sub_2_7x7().grid_over_sampled, shape_native=(3, 3) ) @@ -410,7 +414,7 @@ def make_border_relocator_2d_7x7(): def make_rectangular_mapper_7x7_3x3(): mapper_grids = aa.MapperGrids( mask=make_mask_2d_7x7(), - source_plane_data_grid=make_over_sampler_2d_7x7().over_sampled_grid, + source_plane_data_grid=make_grid_2d_sub_2_7x7().grid_over_sampled, source_plane_mesh_grid=make_rectangular_mesh_grid_3x3(), image_plane_mesh_grid=None, adapt_data=aa.Array2D.ones(shape_native=(3, 3), pixel_scales=0.1), @@ -418,7 +422,7 @@ def make_rectangular_mapper_7x7_3x3(): return aa.MapperRectangular( mapper_grids=mapper_grids, - over_sampler=make_over_sampler_2d_7x7(), + over_sampler=make_grid_2d_sub_2_7x7().over_sampler, border_relocator=make_border_relocator_2d_7x7(), regularization=make_regularization_constant(), ) @@ -427,7 +431,7 @@ def make_rectangular_mapper_7x7_3x3(): def make_delaunay_mapper_9_3x3(): mapper_grids = aa.MapperGrids( mask=make_mask_2d_7x7(), - source_plane_data_grid=make_over_sampler_2d_7x7().over_sampled_grid, + source_plane_data_grid=make_grid_2d_sub_2_7x7().grid_over_sampled, source_plane_mesh_grid=make_delaunay_mesh_grid_9(), image_plane_mesh_grid=aa.Grid2D.uniform(shape_native=(3, 3), pixel_scales=0.1), adapt_data=aa.Array2D.ones(shape_native=(3, 3), pixel_scales=0.1), @@ -435,7 +439,7 @@ def make_delaunay_mapper_9_3x3(): return aa.MapperDelaunay( mapper_grids=mapper_grids, - over_sampler=make_over_sampler_2d_7x7(), + over_sampler=make_grid_2d_sub_2_7x7().over_sampler, border_relocator=make_border_relocator_2d_7x7(), regularization=make_regularization_constant(), ) @@ -444,7 +448,7 @@ def make_delaunay_mapper_9_3x3(): def make_voronoi_mapper_9_3x3(): mapper_grids = aa.MapperGrids( mask=make_mask_2d_7x7(), - source_plane_data_grid=make_over_sampler_2d_7x7().over_sampled_grid, + source_plane_data_grid=make_grid_2d_sub_2_7x7().grid_over_sampled, source_plane_mesh_grid=make_voronoi_mesh_grid_9(), image_plane_mesh_grid=aa.Grid2D.uniform(shape_native=(3, 3), pixel_scales=0.1), adapt_data=aa.Array2D.ones(shape_native=(3, 3), pixel_scales=0.1), @@ -452,7 +456,7 @@ def make_voronoi_mapper_9_3x3(): return aa.MapperVoronoi( mapper_grids=mapper_grids, - over_sampler=make_over_sampler_2d_7x7(), + over_sampler=make_grid_2d_sub_2_7x7().over_sampler, border_relocator=make_border_relocator_2d_7x7(), regularization=make_regularization_constant(), ) diff --git a/autoarray/inversion/linear_obj/func_list.py b/autoarray/inversion/linear_obj/func_list.py index 547a9427..9ec30e9b 100644 --- a/autoarray/inversion/linear_obj/func_list.py +++ b/autoarray/inversion/linear_obj/func_list.py @@ -96,7 +96,7 @@ def unique_mappings(self) -> UniqueMappings: For a `LinearObjFuncList` every data pixel's group of sub-pixels maps directly to the linear function. """ - sub_size = np.max(self.grid.over_sampling.sub_size) + sub_size = np.max(self.grid.over_sampling_size) # TODO : This shape slim is prob unreliable and needs to be divided by sub_size**2 diff --git a/autoarray/inversion/pixelization/image_mesh/hilbert.py b/autoarray/inversion/pixelization/image_mesh/hilbert.py index 214f435a..3a4185c8 100644 --- a/autoarray/inversion/pixelization/image_mesh/hilbert.py +++ b/autoarray/inversion/pixelization/image_mesh/hilbert.py @@ -114,7 +114,7 @@ def super_resolution_grid_from(img_2d, mask, mask_radius, pixel_scales, sub_scal over_sampler = OverSampler(mask=new_mask, sub_size=sub_scale) - new_grid = over_sampler.over_sampled_grid + new_grid = over_sampler.uniform_over_sampled new_img = griddata( points=grid, values=img_2d.ravel(), xi=new_grid, fill_value=0.0, method="linear" diff --git a/autoarray/inversion/plot/inversion_plotters.py b/autoarray/inversion/plot/inversion_plotters.py index 66dd9b7f..098769e8 100644 --- a/autoarray/inversion/plot/inversion_plotters.py +++ b/autoarray/inversion/plot/inversion_plotters.py @@ -184,7 +184,7 @@ def figures_2d_of_pixelization( self.mat_plot_2d.plot_array( array=array, visuals_2d=self.get_visuals_2d_for_data(), - grid_indexes=mapper_plotter.mapper.over_sampler.over_sampled_grid, + grid_indexes=mapper_plotter.mapper.over_sampler.uniform_over_sampled, auto_labels=AutoLabels( title="Data Subtracted", filename="data_subtracted" ), @@ -200,7 +200,7 @@ def figures_2d_of_pixelization( self.mat_plot_2d.plot_array( array=array, visuals_2d=self.get_visuals_2d_for_data(), - grid_indexes=mapper_plotter.mapper.over_sampler.over_sampled_grid, + grid_indexes=mapper_plotter.mapper.over_sampler.uniform_over_sampled, auto_labels=AutoLabels( title="Reconstructed Image", filename="reconstructed_image" ), diff --git a/autoarray/inversion/plot/mapper_plotters.py b/autoarray/inversion/plot/mapper_plotters.py index d6fa791d..7234e8da 100644 --- a/autoarray/inversion/plot/mapper_plotters.py +++ b/autoarray/inversion/plot/mapper_plotters.py @@ -117,7 +117,7 @@ def subplot_image_and_mapper( ) self.mat_plot_2d.index_scatter.scatter_grid_indexes( - grid=self.mapper.over_sampler.over_sampled_grid, + grid=self.mapper.over_sampler.uniform_over_sampled, indexes=indexes, ) diff --git a/autoarray/operators/over_sampling/over_sample_util.py b/autoarray/operators/over_sampling/over_sample_util.py index 8f31bc65..e0e59566 100644 --- a/autoarray/operators/over_sampling/over_sample_util.py +++ b/autoarray/operators/over_sampling/over_sample_util.py @@ -334,7 +334,7 @@ def sub_size_radial_bins_from( return sub_size -# @numba_util.jit() +@numba_util.jit() def grid_2d_slim_over_sampled_via_mask_from( mask_2d: np.ndarray, pixel_scales: ty.PixelScales, diff --git a/autoarray/operators/over_sampling/over_sampler.py b/autoarray/operators/over_sampling/over_sampler.py index b826a5d7..19730fe3 100644 --- a/autoarray/operators/over_sampling/over_sampler.py +++ b/autoarray/operators/over_sampling/over_sampler.py @@ -343,3 +343,16 @@ def slim_for_sub_slim(self) -> np.ndarray: return over_sample_util.slim_index_for_sub_slim_index_via_mask_2d_from( mask_2d=np.array(self.mask), sub_size=np.array(self.sub_size) ).astype("int") + + @property + def uniform_over_sampled(self): + from autoarray.structures.grids.irregular_2d import Grid2DIrregular + + grid = 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=np.array(self.sub_size).astype("int"), + origin=self.mask.origin, + ) + + return Grid2DIrregular(values=grid) diff --git a/autoarray/plot/mat_plot/two_d.py b/autoarray/plot/mat_plot/two_d.py index 965c5c76..19566050 100644 --- a/autoarray/plot/mat_plot/two_d.py +++ b/autoarray/plot/mat_plot/two_d.py @@ -421,7 +421,7 @@ def plot_grid( ax = self.setup_subplot() if plot_over_sampled_grid: - grid_plot = grid.over_sampler.over_sampled_grid + grid_plot = grid.over_sampler.uniform_over_sampled else: grid_plot = grid diff --git a/autoarray/structures/decorators/abstract.py b/autoarray/structures/decorators/abstract.py index b1abbca1..2d6e7f23 100644 --- a/autoarray/structures/decorators/abstract.py +++ b/autoarray/structures/decorators/abstract.py @@ -60,7 +60,7 @@ def mask(self) -> Union[Mask1D, Mask2D]: return self.grid.mask @property - def over_sampling(self) -> np.ndarray: + def over_sampling_size(self) -> np.ndarray: return self.grid.over_sampling_size def via_grid_2d(self, result): diff --git a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py index c6ea9357..495b6886 100644 --- a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py +++ b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py @@ -188,20 +188,17 @@ def test__data_vector_via_w_tilde_data_two_methods_agree(): # TODO : Use pytest.parameterize for sub_size in range(1, 3): - over_sampler = aa.OverSampler( - mask=mask, - sub_size=sub_size, - ) - - grid = over_sampler.over_sampled_grid + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=sub_size) mapper_grids = pixelization.mapper_grids_from( - mask=mask, border_relocator=None, source_plane_data_grid=grid + mask=mask, + border_relocator=None, + source_plane_data_grid=grid.grid_over_sampled, ) mapper = aa.Mapper( mapper_grids=mapper_grids, - over_sampler=over_sampler, + over_sampler=grid.over_sampler, regularization=None, ) @@ -236,7 +233,7 @@ def test__data_vector_via_w_tilde_data_two_methods_agree(): pix_sizes_for_sub_slim_index=mapper.pix_sizes_for_sub_slim_index, pix_weights_for_sub_slim_index=mapper.pix_weights_for_sub_slim_index, pix_pixels=mapper.params, - sub_size=np.array(over_sampler.sub_size), + sub_size=np.array(grid.over_sampling_size), ) data_vector_via_w_tilde = ( @@ -319,20 +316,17 @@ def test__curvature_matrix_via_w_tilde_preload_two_methods_agree(): pixelization = aa.mesh.Rectangular(shape=(20, 20)) for sub_size in range(1, 2, 3): - over_sampler = aa.OverSampler( - mask=mask, - sub_size=sub_size, - ) - - grid = over_sampler.over_sampled_grid + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=sub_size) mapper_grids = pixelization.mapper_grids_from( - mask=mask, border_relocator=None, source_plane_data_grid=grid + mask=mask, + border_relocator=None, + source_plane_data_grid=grid.grid_over_sampled, ) mapper = aa.Mapper( mapper_grids=mapper_grids, - over_sampler=over_sampler, + over_sampler=grid.over_sampler, regularization=None, ) @@ -358,7 +352,7 @@ def test__curvature_matrix_via_w_tilde_preload_two_methods_agree(): pix_sizes_for_sub_slim_index=mapper.pix_sizes_for_sub_slim_index, pix_weights_for_sub_slim_index=mapper.pix_weights_for_sub_slim_index, pix_pixels=mapper.params, - sub_size=np.array(over_sampler.sub_size), + sub_size=np.array(grid.over_sampling_size), ) curvature_matrix_via_w_tilde = aa.util.inversion_imaging.curvature_matrix_via_w_tilde_curvature_preload_imaging_from( diff --git a/test_autoarray/inversion/pixelization/mappers/test_factory.py b/test_autoarray/inversion/pixelization/mappers/test_factory.py index d5c49661..7bbeacb9 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_factory.py +++ b/test_autoarray/inversion/pixelization/mappers/test_factory.py @@ -21,16 +21,16 @@ def test__rectangular_mapper(): # Slightly manipulate input grid so sub gridding is evidence in first source pixel. over_sampler = aa.OverSampler(mask=mask, sub_size=2) - over_sampled_grid = over_sampler.over_sampled_grid - over_sampled_grid[0, 0] = -2.0 - over_sampled_grid[0, 1] = 2.0 + grid_over_sampled = over_sampler.uniform_over_sampled + grid_over_sampled[0, 0] = -2.0 + grid_over_sampled[0, 1] = 2.0 mesh = aa.mesh.Rectangular(shape=(3, 3)) mapper_grids = mesh.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=over_sampled_grid, + source_plane_data_grid=grid_over_sampled, source_plane_mesh_grid=None, ) @@ -74,9 +74,9 @@ def test__delaunay_mapper(): # Slightly manipulate input grid so sub gridding is evidence in first source pixel. over_sampler = aa.OverSampler(mask=mask, sub_size=2) - over_sampled_grid = over_sampler.over_sampled_grid - over_sampled_grid[0, 0] = -2.0 - over_sampled_grid[0, 1] = 2.0 + grid_over_sampled = over_sampler.uniform_over_sampled + grid_over_sampled[0, 0] = -2.0 + grid_over_sampled[0, 1] = 2.0 mesh = aa.mesh.Delaunay() image_mesh = aa.image_mesh.Overlay(shape=(3, 3)) @@ -87,7 +87,7 @@ def test__delaunay_mapper(): mapper_grids = mesh.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=over_sampled_grid, + source_plane_data_grid=grid_over_sampled, source_plane_mesh_grid=image_plane_mesh_grid, ) @@ -132,9 +132,9 @@ def test__voronoi_mapper(): # Slightly manipulate input grid so sub gridding is evidence in first source pixel. over_sampler = aa.OverSampler(mask=mask, sub_size=2) - over_sampled_grid = over_sampler.over_sampled_grid - over_sampled_grid[0, 0] = -2.0 - over_sampled_grid[0, 1] = 2.0 + grid_over_sampled = over_sampler.uniform_over_sampled + grid_over_sampled[0, 0] = -2.0 + grid_over_sampled[0, 1] = 2.0 mesh = aa.mesh.Voronoi() image_mesh = aa.image_mesh.Overlay(shape=(3, 3)) @@ -145,7 +145,7 @@ def test__voronoi_mapper(): mapper_grids = mesh.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=over_sampled_grid, + source_plane_data_grid=grid_over_sampled, source_plane_mesh_grid=image_plane_mesh_grid, ) diff --git a/test_autoarray/inversion/pixelization/test_border_relocator.py b/test_autoarray/inversion/pixelization/test_border_relocator.py index aec9df59..882e0423 100644 --- a/test_autoarray/inversion/pixelization/test_border_relocator.py +++ b/test_autoarray/inversion/pixelization/test_border_relocator.py @@ -325,17 +325,16 @@ def test__relocated_grid_from__inside_border_no_relocations(): shape_native=(30, 30), radius=1.0, pixel_scales=(0.1, 0.1) ) - over_sampling = aa.OverSampler( - mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) + grid = aa.Grid2D.from_mask( + mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) ) - grid = over_sampling.over_sampled_grid - grid[1, :] = [0.1, 0.1] + grid.grid_over_sampled[1, :] = [0.1, 0.1] border_relocator = aa.BorderRelocator( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) - relocated_grid = border_relocator.relocated_grid_from(grid=grid) + relocated_grid = border_relocator.relocated_grid_from(grid=grid.grid_over_sampled) assert (relocated_grid[1] == np.array([0.1, 0.1])).all() @@ -345,22 +344,21 @@ def test__relocated_grid_from__outside_border_includes_relocations(): shape_native=(30, 30), radius=1.0, pixel_scales=(0.1, 0.1) ) - over_sampling = aa.OverSampler( - mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) + grid = aa.Grid2D.from_mask( + mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) ) - grid = over_sampling.over_sampled_grid - grid[1, :] = [10.1, 0.1] + grid.grid_over_sampled[1, :] = [10.1, 0.1] border_relocator = aa.BorderRelocator( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) - relocated_grid = border_relocator.relocated_grid_from(grid=grid) + relocated_grid = border_relocator.relocated_grid_from(grid=grid.grid_over_sampled) assert relocated_grid[1] == pytest.approx([0.97783243, 0.00968151], 1e-4) -def test__relocated_grid_from__positive_origin_included_in_relocate(): +def test__relocated_grid_from__positive_origin_includeddfdsd_in_relocate(): mask = aa.Mask2D.circular( shape_native=(60, 60), radius=1.0, @@ -371,7 +369,7 @@ def test__relocated_grid_from__positive_origin_included_in_relocate(): over_sampling = aa.OverSampler( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) - grid = over_sampling.over_sampled_grid + grid = over_sampling.grid_over_sampled grid[1, :] = [11.1, 1.0] border_relocator = aa.BorderRelocator( @@ -381,3 +379,23 @@ def test__relocated_grid_from__positive_origin_included_in_relocate(): relocated_grid = border_relocator.relocated_grid_from(grid=grid) assert relocated_grid[1] == pytest.approx([1.97783243, 1.0], 1e-4) + + +def test__relocated_grid_from__positive_origin_included_in_relocate(): + mask = aa.Mask2D.circular( + shape_native=(60, 60), + radius=1.0, + pixel_scales=(0.1, 0.1), + centre=(1.0, 1.0), + ) + + grid = aa.Grid2D.from_mask( + mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) + ) + grid.grid_over_sampled[1, :] = [11.1, 0.1] + + border_relocator = aa.BorderRelocator(mask=mask, sub_size=grid.over_sampling_size) + + relocated_grid = border_relocator.relocated_grid_from(grid=grid.grid_over_sampled) + + assert relocated_grid[1] == pytest.approx([1.97783243, 1.0], 1e-4) diff --git a/test_autoarray/inversion/test_linear_obj.py b/test_autoarray/inversion/test_linear_obj.py index d9361e30..1319742a 100644 --- a/test_autoarray/inversion/test_linear_obj.py +++ b/test_autoarray/inversion/test_linear_obj.py @@ -44,10 +44,8 @@ def test__data_to_pix_unique_from(): mask = aa.Mask2D.all_false(shape_native=(1, 2), pixel_scales=0.1) - over_sampling = aa.OverSampler(mask=mask, sub_size=2) - grid = aa.Grid2D.uniform( - shape_native=(1, 2), pixel_scales=0.1, over_sampling=over_sampling + shape_native=(1, 2), pixel_scales=0.1, over_sampling_size=2 ) linear_obj = aa.AbstractLinearObjFuncList(grid=grid, regularization=None) From 9ebe6f8a331f2e0e44d5ef730ecfb0b937c9fd79 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 19:17:56 +0000 Subject: [PATCH 08/26] all tests pass --- autoarray/config/visualize/plots.yaml | 1 - autoarray/dataset/over_sampling.py | 4 --- autoarray/dataset/plot/imaging_plotters.py | 13 --------- autoarray/structures/decorators/to_grid.py | 15 +++++++++- autoarray/structures/grids/uniform_2d.py | 25 +++++++++++----- .../dataset/plot/test_imaging_plotters.py | 2 -- .../pixelization/test_border_relocator.py | 29 ++----------------- .../structures/grids/test_uniform_2d.py | 11 ------- 8 files changed, 33 insertions(+), 67 deletions(-) diff --git a/autoarray/config/visualize/plots.yaml b/autoarray/config/visualize/plots.yaml index e4512bfd..1f2185b2 100644 --- a/autoarray/config/visualize/plots.yaml +++ b/autoarray/config/visualize/plots.yaml @@ -9,7 +9,6 @@ dataset: # Settings for plots of all datasets noise_map: false # Plot the individual noise-map of every dataset? signal_to_noise_map: false # Plot the individual signal-to-noise-map of every dataset? over_sampling: false # Plot the over-sampling sub-size, used to evaluate light profiles, of every dataset? - over_sampling_non_uniform: false # Plot the over-sampling sub-size, used to evaluate non uniform grids, of every dataset? over_sampling_pixelization: false # Plot the over-sampling sub-size, used to evaluate pixelizations, of every dataset? imaging: # Settings for plots of imaging datasets (e.g. ImagingPlotter) psf: false diff --git a/autoarray/dataset/over_sampling.py b/autoarray/dataset/over_sampling.py index 6bd3c5b9..b4661783 100644 --- a/autoarray/dataset/over_sampling.py +++ b/autoarray/dataset/over_sampling.py @@ -36,10 +36,6 @@ def __init__( the image data. This grid is used specifically for pixelizations computed via the `inversion` module, which can benefit from using different oversampling schemes than the normal grid. - - `grid_non_uniform`: A grid of (y,x) coordinates which are mapped from the image pixel centres but have had - their values deflected to become non-uniform. This is used to compute over sampled light profiles of lensed - sources in PyAutoLens. - Different calculations typically benefit from different over sampling, which this class enables the customization of. diff --git a/autoarray/dataset/plot/imaging_plotters.py b/autoarray/dataset/plot/imaging_plotters.py index f758cf59..59f9ac10 100644 --- a/autoarray/dataset/plot/imaging_plotters.py +++ b/autoarray/dataset/plot/imaging_plotters.py @@ -62,7 +62,6 @@ def figures_2d( psf: bool = False, signal_to_noise_map: bool = False, over_sampling: bool = False, - over_sampling_non_uniform: bool = False, over_sampling_pixelization: bool = False, title_str: Optional[str] = None, ): @@ -136,17 +135,6 @@ def figures_2d( ), ) - if over_sampling_non_uniform: - self.mat_plot_2d.plot_array( - array=self.dataset.grids.non_uniform.over_sampling_size, - visuals_2d=self.get_visuals_2d(), - auto_labels=AutoLabels( - title=title_str or f"Over Sampling (Non Uniform)", - filename="over_sampling_non_uniform", - cb_unit="", - ), - ) - if over_sampling_pixelization: self.mat_plot_2d.plot_array( array=self.dataset.grids.pixelization.over_sampling_size, @@ -231,7 +219,6 @@ def subplot_dataset(self): self.figures_2d(signal_to_noise_map=True) self.figures_2d(over_sampling=True) - self.figures_2d(over_sampling_non_uniform=True) self.figures_2d(over_sampling_pixelization=True) self.mat_plot_2d.output.subplot_to_figure(auto_filename="subplot_dataset") diff --git a/autoarray/structures/decorators/to_grid.py b/autoarray/structures/decorators/to_grid.py index 706397fc..0ea63190 100644 --- a/autoarray/structures/decorators/to_grid.py +++ b/autoarray/structures/decorators/to_grid.py @@ -22,18 +22,31 @@ def via_grid_2d(self, result) -> Union[Grid2D, List[Grid2D]]: The input result (e.g. of a decorated function) that is converted to a Grid2D or list of Grid2D objects. """ if not isinstance(result, list): + try: + grid_over_sampled = result.grid_over_sampled + except AttributeError: + grid_over_sampled = None + return Grid2D( values=result, mask=self.mask, over_sampling_size=self.over_sampling_size, + grid_over_sampled=grid_over_sampled, ) + + try: + grid_over_sampled_list = [res.grid_over_sampled for res in result] + except AttributeError: + grid_over_sampled_list = [None] * len(result) + return [ Grid2D( values=res, mask=self.mask, over_sampling_size=self.over_sampling_size, + grid_over_sampled=grid_over_sampled, ) - for res in result + for res, grid_over_sampled in zip(result, grid_over_sampled_list) ] def via_grid_2d_irr(self, result) -> Union[Grid2DIrregular, List[Grid2DIrregular]]: diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 1475b703..36fba67b 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -16,6 +16,7 @@ from autoarray.geometry import geometry_util from autoarray.operators.over_sampling import over_sample_util +from autoarray import exc from autoarray import type as ty @@ -26,7 +27,6 @@ def __init__( mask: Mask2D, store_native: bool = False, over_sampling_size: Union[int, Array2D] = 4, - over_sampling_non_uniform: Union[int, Array2D] = 4, grid_over_sampled: Optional[Grid2D] = None, *args, **kwargs, @@ -166,10 +166,6 @@ def __init__( grid_2d_util.check_grid_2d(grid_2d=values) - # self.over_sampling_non_uniform = ( - # over_sampling_non_uniform or OverSampling(sub_size=4) - # ) - if isinstance(over_sampling_size, int): over_sampling_size = np.full( fill_value=over_sampling_size, shape=mask.shape_slim @@ -1064,6 +1060,8 @@ def padded_grid_from(self, kernel_shape_native: Tuple[int, int]) -> "Grid2D": kernel_shape_native The 2D shape of the kernel which convolves signal from masked pixels to unmasked pixels. """ + if kernel_shape_native[0] % 2 == 0 or kernel_shape_native[1] % 2 == 0: + raise exc.KernelException("Kernel2D Kernel2D must be odd") shape = self.mask.shape @@ -1077,11 +1075,22 @@ def padded_grid_from(self, kernel_shape_native: Tuple[int, int]) -> "Grid2D": pixel_scales=self.mask.pixel_scales, ) - return Grid2D.from_mask( - mask=padded_mask, - over_sampling_size=4, + pad_width = ( + (padded_shape[0] - shape[0]) // 2, + (padded_shape[1] - shape[1]) // 2, + ) + + over_sampling_size = np.pad( + self.over_sampling_size.native, + pad_width, + mode="constant", + constant_values=1, ) + over_sampling_size[over_sampling_size == 0] = 1 + + return Grid2D.from_mask(mask=padded_mask, over_sampling_size=over_sampling_size) + @cached_property def is_uniform(self) -> bool: """ diff --git a/test_autoarray/dataset/plot/test_imaging_plotters.py b/test_autoarray/dataset/plot/test_imaging_plotters.py index 65d1f9a0..e7102c5f 100644 --- a/test_autoarray/dataset/plot/test_imaging_plotters.py +++ b/test_autoarray/dataset/plot/test_imaging_plotters.py @@ -35,7 +35,6 @@ def test__individual_attributes_are_output( psf=True, signal_to_noise_map=True, over_sampling=True, - over_sampling_non_uniform=True, over_sampling_pixelization=True, ) @@ -44,7 +43,6 @@ def test__individual_attributes_are_output( assert path.join(plot_path, "psf.png") in plot_patch.paths assert path.join(plot_path, "signal_to_noise_map.png") in plot_patch.paths assert path.join(plot_path, "over_sampling.png") in plot_patch.paths - assert path.join(plot_path, "over_sampling_non_uniform.png") in plot_patch.paths assert path.join(plot_path, "over_sampling_pixelization.png") in plot_patch.paths plot_patch.paths = [] diff --git a/test_autoarray/inversion/pixelization/test_border_relocator.py b/test_autoarray/inversion/pixelization/test_border_relocator.py index 882e0423..fb536691 100644 --- a/test_autoarray/inversion/pixelization/test_border_relocator.py +++ b/test_autoarray/inversion/pixelization/test_border_relocator.py @@ -358,29 +358,6 @@ def test__relocated_grid_from__outside_border_includes_relocations(): assert relocated_grid[1] == pytest.approx([0.97783243, 0.00968151], 1e-4) -def test__relocated_grid_from__positive_origin_includeddfdsd_in_relocate(): - mask = aa.Mask2D.circular( - shape_native=(60, 60), - radius=1.0, - pixel_scales=(0.1, 0.1), - centre=(1.0, 1.0), - ) - - over_sampling = aa.OverSampler( - mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) - ) - grid = over_sampling.grid_over_sampled - grid[1, :] = [11.1, 1.0] - - border_relocator = aa.BorderRelocator( - mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) - ) - - relocated_grid = border_relocator.relocated_grid_from(grid=grid) - - assert relocated_grid[1] == pytest.approx([1.97783243, 1.0], 1e-4) - - def test__relocated_grid_from__positive_origin_included_in_relocate(): mask = aa.Mask2D.circular( shape_native=(60, 60), @@ -389,10 +366,8 @@ def test__relocated_grid_from__positive_origin_included_in_relocate(): centre=(1.0, 1.0), ) - grid = aa.Grid2D.from_mask( - mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) - ) - grid.grid_over_sampled[1, :] = [11.1, 0.1] + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + grid.grid_over_sampled[1, :] = [11.1, 1.0] border_relocator = aa.BorderRelocator(mask=mask, sub_size=grid.over_sampling_size) diff --git a/test_autoarray/structures/grids/test_uniform_2d.py b/test_autoarray/structures/grids/test_uniform_2d.py index c81f701e..a7e8e904 100644 --- a/test_autoarray/structures/grids/test_uniform_2d.py +++ b/test_autoarray/structures/grids/test_uniform_2d.py @@ -665,17 +665,6 @@ def test__padded_grid_from(): assert padded_grid.shape == (42, 2) assert (padded_grid == padded_grid_util).all() - grid_2d = aa.Grid2D.uniform(shape_native=(5, 5), pixel_scales=8.0) - - padded_grid = grid_2d.padded_grid_from(kernel_shape_native=(2, 5)) - - padded_grid_util = aa.util.grid_2d.grid_2d_slim_via_mask_from( - mask_2d=np.full((6, 9), False), pixel_scales=(8.0, 8.0) - ) - - assert padded_grid.shape == (54, 2) - assert (padded_grid == padded_grid_util).all() - def test__squared_distances_to_coordinate_from(): mask = aa.Mask2D( From d4a8ef371f7fb42126e69a8dd0df8b3170f63065 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 20:40:36 +0000 Subject: [PATCH 09/26] black --- autoarray/fixtures.py | 13 +++--- autoarray/inversion/mock/mock_mapper.py | 8 +++- .../pixelization/mappers/abstract.py | 10 ++--- .../pixelization/mappers/delaunay.py | 10 +++-- .../inversion/pixelization/mappers/factory.py | 5 --- .../pixelization/mappers/rectangular.py | 8 ++-- .../inversion/pixelization/mappers/voronoi.py | 3 +- .../pixelization/mesh/rectangular.py | 14 +++++-- .../pixelization/mesh/triangulation.py | 17 +++++--- autoarray/plot/get_visuals/two_d.py | 6 ++- autoarray/plot/mat_plot/two_d.py | 6 +-- autoarray/plot/visuals/two_d.py | 2 +- test_autoarray/conftest.py | 5 +++ .../imaging/test_inversion_imaging_util.py | 15 ++----- .../inversion/inversion/test_abstract.py | 24 +++-------- .../pixelization/mappers/test_abstract.py | 11 ++--- .../pixelization/mappers/test_delaunay.py | 13 +++--- .../pixelization/mappers/test_factory.py | 41 ++++++++----------- .../pixelization/mappers/test_rectangular.py | 31 +++++++------- .../pixelization/mappers/test_voronoi.py | 15 +++---- .../pixelization/mesh/test_abstract.py | 12 ++---- .../inversion/plot/test_mapper_plotters.py | 7 +++- test_autoarray/plot/get_visuals/test_two_d.py | 5 ++- 23 files changed, 132 insertions(+), 149 deletions(-) diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 2ae6c3bc..8b5155f6 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -89,6 +89,10 @@ def make_grid_2d_7x7(): return aa.Grid2D.from_mask(mask=make_mask_2d_7x7()) +def make_grid_2d_sub_1_7x7(): + return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sampling_size=1) + + def make_grid_2d_sub_2_7x7(): return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sampling_size=2) @@ -414,7 +418,7 @@ def make_border_relocator_2d_7x7(): def make_rectangular_mapper_7x7_3x3(): mapper_grids = aa.MapperGrids( mask=make_mask_2d_7x7(), - source_plane_data_grid=make_grid_2d_sub_2_7x7().grid_over_sampled, + source_plane_data_grid=make_grid_2d_sub_2_7x7(), source_plane_mesh_grid=make_rectangular_mesh_grid_3x3(), image_plane_mesh_grid=None, adapt_data=aa.Array2D.ones(shape_native=(3, 3), pixel_scales=0.1), @@ -422,7 +426,6 @@ def make_rectangular_mapper_7x7_3x3(): return aa.MapperRectangular( mapper_grids=mapper_grids, - over_sampler=make_grid_2d_sub_2_7x7().over_sampler, border_relocator=make_border_relocator_2d_7x7(), regularization=make_regularization_constant(), ) @@ -431,7 +434,7 @@ def make_rectangular_mapper_7x7_3x3(): def make_delaunay_mapper_9_3x3(): mapper_grids = aa.MapperGrids( mask=make_mask_2d_7x7(), - source_plane_data_grid=make_grid_2d_sub_2_7x7().grid_over_sampled, + source_plane_data_grid=make_grid_2d_sub_2_7x7(), source_plane_mesh_grid=make_delaunay_mesh_grid_9(), image_plane_mesh_grid=aa.Grid2D.uniform(shape_native=(3, 3), pixel_scales=0.1), adapt_data=aa.Array2D.ones(shape_native=(3, 3), pixel_scales=0.1), @@ -439,7 +442,6 @@ def make_delaunay_mapper_9_3x3(): return aa.MapperDelaunay( mapper_grids=mapper_grids, - over_sampler=make_grid_2d_sub_2_7x7().over_sampler, border_relocator=make_border_relocator_2d_7x7(), regularization=make_regularization_constant(), ) @@ -448,7 +450,7 @@ def make_delaunay_mapper_9_3x3(): def make_voronoi_mapper_9_3x3(): mapper_grids = aa.MapperGrids( mask=make_mask_2d_7x7(), - source_plane_data_grid=make_grid_2d_sub_2_7x7().grid_over_sampled, + source_plane_data_grid=make_grid_2d_sub_2_7x7(), source_plane_mesh_grid=make_voronoi_mesh_grid_9(), image_plane_mesh_grid=aa.Grid2D.uniform(shape_native=(3, 3), pixel_scales=0.1), adapt_data=aa.Array2D.ones(shape_native=(3, 3), pixel_scales=0.1), @@ -456,7 +458,6 @@ def make_voronoi_mapper_9_3x3(): return aa.MapperVoronoi( mapper_grids=mapper_grids, - over_sampler=make_grid_2d_sub_2_7x7().over_sampler, border_relocator=make_border_relocator_2d_7x7(), regularization=make_regularization_constant(), ) diff --git a/autoarray/inversion/mock/mock_mapper.py b/autoarray/inversion/mock/mock_mapper.py index 6d98aa17..84f90425 100644 --- a/autoarray/inversion/mock/mock_mapper.py +++ b/autoarray/inversion/mock/mock_mapper.py @@ -32,11 +32,11 @@ def __init__( super().__init__( mapper_grids=mapper_grids, - over_sampler=over_sampler, border_relocator=border_relocator, regularization=regularization, ) + self._over_sampler = over_sampler self._edge_pixel_list = edge_pixel_list self._pix_sub_weights = pix_sub_weights self._pix_sub_weights_split_cross = pix_sub_weights_split_cross @@ -56,6 +56,12 @@ def params(self): return super().params return self._parameters + @property + def over_sampler(self): + if self._over_sampler is None: + return super().over_sampler + return self._over_sampler + @property def edge_pixel_list(self): return self._edge_pixel_list diff --git a/autoarray/inversion/pixelization/mappers/abstract.py b/autoarray/inversion/pixelization/mappers/abstract.py index b144c4a6..192fa4f5 100644 --- a/autoarray/inversion/pixelization/mappers/abstract.py +++ b/autoarray/inversion/pixelization/mappers/abstract.py @@ -11,7 +11,6 @@ from autoarray.inversion.pixelization.border_relocator import BorderRelocator from autoarray.inversion.pixelization.mappers.mapper_grids import MapperGrids from autoarray.inversion.regularization.abstract import AbstractRegularization -from autoarray.operators.over_sampling.over_sampler import OverSampler from autoarray.structures.arrays.uniform_2d import Array2D from autoarray.structures.grids.uniform_2d import Grid2D from autoarray.structures.mesh.abstract_2d import Abstract2DMesh @@ -26,7 +25,6 @@ def __init__( self, mapper_grids: MapperGrids, regularization: Optional[AbstractRegularization], - over_sampler: OverSampler, border_relocator: BorderRelocator, run_time_dict: Optional[Dict] = None, ): @@ -82,9 +80,6 @@ def __init__( 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. - over_sampler - Performs over-sampling whereby the masked image pixels are split into sub-pixels, which are all - mapped via the mapper with sub-fractional values of flux. border_relocator The border relocator, which relocates coordinates outside the border of the source-plane data grid to its edge. @@ -94,7 +89,6 @@ def __init__( super().__init__(regularization=regularization, run_time_dict=run_time_dict) - self.over_sampler = over_sampler self.border_relocator = border_relocator self.mapper_grids = mapper_grids @@ -118,6 +112,10 @@ def source_plane_mesh_grid(self) -> Abstract2DMesh: def image_plane_mesh_grid(self) -> Grid2D: return self.mapper_grids.image_plane_mesh_grid + @property + def over_sampler(self): + return self.mapper_grids.source_plane_data_grid.over_sampler + @property def edge_pixel_list(self) -> List[int]: return self.source_plane_mesh_grid.edge_pixel_list diff --git a/autoarray/inversion/pixelization/mappers/delaunay.py b/autoarray/inversion/pixelization/mappers/delaunay.py index c35c4d9b..b579a1eb 100644 --- a/autoarray/inversion/pixelization/mappers/delaunay.py +++ b/autoarray/inversion/pixelization/mappers/delaunay.py @@ -112,12 +112,14 @@ def pix_sub_weights(self) -> PixSubWeights: delaunay = self.delaunay simplex_index_for_sub_slim_index = delaunay.find_simplex( - self.source_plane_data_grid + self.source_plane_data_grid.grid_over_sampled ) pix_indexes_for_simplex_index = delaunay.simplices mappings, sizes = mapper_util.pix_indexes_for_sub_slim_index_delaunay_from( - source_plane_data_grid=np.array(self.source_plane_data_grid), + source_plane_data_grid=np.array( + self.source_plane_data_grid.grid_over_sampled + ), simplex_index_for_sub_slim_index=simplex_index_for_sub_slim_index, pix_indexes_for_simplex_index=pix_indexes_for_simplex_index, delaunay_points=delaunay.points, @@ -127,7 +129,9 @@ def pix_sub_weights(self) -> PixSubWeights: sizes = sizes.astype("int") weights = mapper_util.pixel_weights_delaunay_from( - source_plane_data_grid=np.array(self.source_plane_data_grid), + source_plane_data_grid=np.array( + self.source_plane_data_grid.grid_over_sampled + ), source_plane_mesh_grid=np.array(self.source_plane_mesh_grid), slim_index_for_sub_slim_index=self.slim_index_for_sub_slim_index, pix_indexes_for_sub_slim_index=mappings, diff --git a/autoarray/inversion/pixelization/mappers/factory.py b/autoarray/inversion/pixelization/mappers/factory.py index 50f056c0..31013320 100644 --- a/autoarray/inversion/pixelization/mappers/factory.py +++ b/autoarray/inversion/pixelization/mappers/factory.py @@ -2,7 +2,6 @@ from autoarray.inversion.pixelization.mappers.mapper_grids import MapperGrids from autoarray.inversion.pixelization.border_relocator import BorderRelocator -from autoarray.operators.over_sampling.over_sampler import OverSampler from autoarray.inversion.regularization.abstract import AbstractRegularization from autoarray.structures.mesh.rectangular_2d import Mesh2DRectangular from autoarray.structures.mesh.delaunay_2d import Mesh2DDelaunay @@ -12,7 +11,6 @@ def mapper_from( mapper_grids: MapperGrids, regularization: Optional[AbstractRegularization], - over_sampler: OverSampler, border_relocator: Optional[BorderRelocator] = None, run_time_dict: Optional[Dict] = None, ): @@ -50,7 +48,6 @@ def mapper_from( if isinstance(mapper_grids.source_plane_mesh_grid, Mesh2DRectangular): return MapperRectangular( mapper_grids=mapper_grids, - over_sampler=over_sampler, border_relocator=border_relocator, regularization=regularization, run_time_dict=run_time_dict, @@ -58,7 +55,6 @@ def mapper_from( elif isinstance(mapper_grids.source_plane_mesh_grid, Mesh2DDelaunay): return MapperDelaunay( mapper_grids=mapper_grids, - over_sampler=over_sampler, border_relocator=border_relocator, regularization=regularization, run_time_dict=run_time_dict, @@ -66,7 +62,6 @@ def mapper_from( elif isinstance(mapper_grids.source_plane_mesh_grid, Mesh2DVoronoi): return MapperVoronoi( mapper_grids=mapper_grids, - over_sampler=over_sampler, border_relocator=border_relocator, regularization=regularization, run_time_dict=run_time_dict, diff --git a/autoarray/inversion/pixelization/mappers/rectangular.py b/autoarray/inversion/pixelization/mappers/rectangular.py index 815e1922..6ae84064 100644 --- a/autoarray/inversion/pixelization/mappers/rectangular.py +++ b/autoarray/inversion/pixelization/mappers/rectangular.py @@ -100,16 +100,16 @@ def pix_sub_weights(self) -> PixSubWeights: are equal to 1.0. """ mappings = geometry_util.grid_pixel_indexes_2d_slim_from( - grid_scaled_2d_slim=np.array(self.source_plane_data_grid), + grid_scaled_2d_slim=np.array(self.source_plane_data_grid.grid_over_sampled), shape_native=self.source_plane_mesh_grid.shape_native, pixel_scales=self.source_plane_mesh_grid.pixel_scales, origin=self.source_plane_mesh_grid.origin, ).astype("int") - mappings = mappings.reshape((len(mappings), 1)) - return PixSubWeights( mappings=mappings.reshape((len(mappings), 1)), sizes=np.ones(len(mappings), dtype="int"), - weights=np.ones((len(self.source_plane_data_grid), 1), dtype="int"), + weights=np.ones( + (len(self.source_plane_data_grid.grid_over_sampled), 1), dtype="int" + ), ) diff --git a/autoarray/inversion/pixelization/mappers/voronoi.py b/autoarray/inversion/pixelization/mappers/voronoi.py index 766a9bcb..b286f5bb 100644 --- a/autoarray/inversion/pixelization/mappers/voronoi.py +++ b/autoarray/inversion/pixelization/mappers/voronoi.py @@ -130,7 +130,8 @@ def pix_sub_weights(self) -> PixSubWeights: """ mappings, sizes, weights = mapper_util.pix_size_weights_voronoi_nn_from( - grid=self.source_plane_data_grid, mesh_grid=self.source_plane_mesh_grid + grid=self.source_plane_data_grid.grid_over_sampled, + mesh_grid=self.source_plane_mesh_grid, ) mappings = mappings.astype("int") diff --git a/autoarray/inversion/pixelization/mesh/rectangular.py b/autoarray/inversion/pixelization/mesh/rectangular.py index 0f4cc204..e5fe9d50 100644 --- a/autoarray/inversion/pixelization/mesh/rectangular.py +++ b/autoarray/inversion/pixelization/mesh/rectangular.py @@ -104,11 +104,19 @@ def mapper_grids_from( self.run_time_dict = run_time_dict - relocated_grid = self.relocated_grid_from( + relocated_grid_over_sampled = self.relocated_grid_from( border_relocator=border_relocator, - source_plane_data_grid=source_plane_data_grid, + source_plane_data_grid=source_plane_data_grid.grid_over_sampled, preloads=preloads, ) + + relocated_grid = Grid2D( + values=source_plane_data_grid, + mask=source_plane_data_grid.mask, + over_sampling_size=source_plane_data_grid.over_sampling_size, + grid_over_sampled=relocated_grid_over_sampled, + ) + mesh_grid = self.mesh_grid_from(source_plane_data_grid=relocated_grid) return MapperGrids( @@ -141,7 +149,7 @@ def mesh_grid_from( by overlaying the `source_plane_data_grid` with the rectangular pixelization. """ return Mesh2DRectangular.overlay_grid( - shape_native=self.shape, grid=source_plane_data_grid + shape_native=self.shape, grid=source_plane_data_grid.grid_over_sampled ) @property diff --git a/autoarray/inversion/pixelization/mesh/triangulation.py b/autoarray/inversion/pixelization/mesh/triangulation.py index 91ee0363..6c499f9f 100644 --- a/autoarray/inversion/pixelization/mesh/triangulation.py +++ b/autoarray/inversion/pixelization/mesh/triangulation.py @@ -70,21 +70,28 @@ def mapper_grids_from( self.run_time_dict = run_time_dict - source_plane_data_grid = self.relocated_grid_from( + relocated_grid_over_sampled = self.relocated_grid_from( border_relocator=border_relocator, - source_plane_data_grid=source_plane_data_grid, + source_plane_data_grid=source_plane_data_grid.grid_over_sampled, preloads=preloads, ) + relocated_grid = Grid2D( + values=source_plane_data_grid, + mask=source_plane_data_grid.mask, + over_sampling_size=source_plane_data_grid.over_sampling_size, + grid_over_sampled=relocated_grid_over_sampled, + ) + relocated_source_plane_mesh_grid = self.relocated_mesh_grid_from( border_relocator=border_relocator, - source_plane_data_grid=source_plane_data_grid, + source_plane_data_grid=relocated_grid_over_sampled, source_plane_mesh_grid=source_plane_mesh_grid, ) try: source_plane_mesh_grid = self.mesh_grid_from( - source_plane_data_grid=source_plane_data_grid, + source_plane_data_grid=relocated_grid_over_sampled, source_plane_mesh_grid=relocated_source_plane_mesh_grid, ) except ValueError as e: @@ -92,7 +99,7 @@ def mapper_grids_from( return MapperGrids( mask=mask, - source_plane_data_grid=source_plane_data_grid, + source_plane_data_grid=relocated_grid, source_plane_mesh_grid=source_plane_mesh_grid, image_plane_mesh_grid=image_plane_mesh_grid, adapt_data=adapt_data, diff --git a/autoarray/plot/get_visuals/two_d.py b/autoarray/plot/get_visuals/two_d.py index 816a9ed8..02f8ee6b 100644 --- a/autoarray/plot/get_visuals/two_d.py +++ b/autoarray/plot/get_visuals/two_d.py @@ -183,11 +183,13 @@ def via_mapper_for_source_from(self, mapper: MapperRectangular) -> Visuals2D: ) grid = self.get( - "grid", mapper.source_plane_data_grid, "mapper_source_plane_data_grid" + "grid", + mapper.source_plane_data_grid.grid_over_sampled, + "mapper_source_plane_data_grid", ) try: - border_grid = mapper.mapper_grids.source_plane_data_grid[ + border_grid = mapper.mapper_grids.source_plane_data_grid.grid_over_sampled[ mapper.border_relocator.sub_border_slim ] border = self.get("border", border_grid) diff --git a/autoarray/plot/mat_plot/two_d.py b/autoarray/plot/mat_plot/two_d.py index 19566050..95f6eb67 100644 --- a/autoarray/plot/mat_plot/two_d.py +++ b/autoarray/plot/mat_plot/two_d.py @@ -610,7 +610,7 @@ def _plot_rectangular_mapper( visuals_2d.plot_via_plotter( plotter=self, - grid_indexes=mapper.source_plane_data_grid, + grid_indexes=mapper.source_plane_data_grid.grid_over_sampled, mapper=mapper, geometry=mapper.mapper_grids.mask.geometry, ) @@ -694,7 +694,7 @@ def _plot_delaunay_mapper( visuals_2d.plot_via_plotter( plotter=self, - grid_indexes=mapper.source_plane_data_grid, + grid_indexes=mapper.source_plane_data_grid.grid_over_sampled, mapper=mapper, geometry=mapper.mapper_grids.mask.geometry, ) @@ -777,7 +777,7 @@ def _plot_voronoi_mapper( visuals_2d.plot_via_plotter( plotter=self, - grid_indexes=mapper.source_plane_data_grid, + grid_indexes=mapper.source_plane_data_grid.grid_over_sampled, mapper=mapper, geometry=mapper.mapper_grids.mask.geometry, ) diff --git a/autoarray/plot/visuals/two_d.py b/autoarray/plot/visuals/two_d.py index 9a5629c9..e92b4100 100644 --- a/autoarray/plot/visuals/two_d.py +++ b/autoarray/plot/visuals/two_d.py @@ -100,6 +100,6 @@ def plot_via_plotter(self, plotter, grid_indexes=None, mapper=None, geometry=Non else: plotter.index_scatter.scatter_grid_indexes( - grid=mapper.source_plane_data_grid, + grid=mapper.source_plane_data_grid.grid_over_sampled, indexes=indexes, ) diff --git a/test_autoarray/conftest.py b/test_autoarray/conftest.py index 0d5437d7..1dbe19e1 100644 --- a/test_autoarray/conftest.py +++ b/test_autoarray/conftest.py @@ -84,6 +84,11 @@ def make_grid_2d_7x7(): return fixtures.make_grid_2d_7x7() +@pytest.fixture(name="grid_2d_sub_1_7x7") +def make_grid_2d_sub_1_7x7(): + return fixtures.make_grid_2d_sub_1_7x7() + + @pytest.fixture(name="grid_2d_irregular_7x7") def make_grid_2d_irregular_7x7(): return fixtures.make_grid_2d_irregular_7x7() diff --git a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py index 495b6886..957c68c3 100644 --- a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py +++ b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py @@ -193,12 +193,11 @@ def test__data_vector_via_w_tilde_data_two_methods_agree(): mapper_grids = pixelization.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=grid.grid_over_sampled, + source_plane_data_grid=grid, ) mapper = aa.Mapper( mapper_grids=mapper_grids, - over_sampler=grid.over_sampler, regularization=None, ) @@ -269,14 +268,7 @@ def test__curvature_matrix_via_w_tilde_two_methods_agree(): source_plane_data_grid=mask.derive_grid.unmasked, ) - over_sampler = aa.OverSampler( - mask=mask, - sub_size=1, - ) - - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) mapping_matrix = mapper.mapping_matrix @@ -321,12 +313,11 @@ def test__curvature_matrix_via_w_tilde_preload_two_methods_agree(): mapper_grids = pixelization.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=grid.grid_over_sampled, + source_plane_data_grid=grid, ) mapper = aa.Mapper( mapper_grids=mapper_grids, - over_sampler=grid.over_sampler, regularization=None, ) diff --git a/test_autoarray/inversion/inversion/test_abstract.py b/test_autoarray/inversion/inversion/test_abstract.py index 48f89851..4bb153b7 100644 --- a/test_autoarray/inversion/inversion/test_abstract.py +++ b/test_autoarray/inversion/inversion/test_abstract.py @@ -114,7 +114,7 @@ def test__curvature_matrix__via_w_tilde__identical_to_mapping(): pixel_scales=2.0, ) - grid = aa.Grid2D.from_mask(mask=mask) + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=1) mesh_0 = aa.mesh.Rectangular(shape=(3, 3)) mesh_1 = aa.mesh.Rectangular(shape=(4, 4)) @@ -135,14 +135,8 @@ def test__curvature_matrix__via_w_tilde__identical_to_mapping(): reg = aa.reg.Constant(coefficient=1.0) - over_sampler = aa.OverSampler(mask=mask, sub_size=1) - - mapper_0 = aa.Mapper( - mapper_grids=mapper_grids_0, over_sampler=over_sampler, regularization=reg - ) - mapper_1 = aa.Mapper( - mapper_grids=mapper_grids_1, over_sampler=over_sampler, regularization=reg - ) + mapper_0 = aa.Mapper(mapper_grids=mapper_grids_0, regularization=reg) + mapper_1 = aa.Mapper(mapper_grids=mapper_grids_1, regularization=reg) image = aa.Array2D.no_mask(values=np.random.random((7, 7)), pixel_scales=1.0) noise_map = aa.Array2D.no_mask(values=np.random.random((7, 7)), pixel_scales=1.0) @@ -184,7 +178,7 @@ def test__curvature_matrix_via_w_tilde__includes_source_interpolation__identical pixel_scales=2.0, ) - grid = aa.Grid2D.from_mask(mask=mask) + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=1) mesh_0 = aa.mesh.Delaunay() mesh_1 = aa.mesh.Delaunay() @@ -216,14 +210,8 @@ def test__curvature_matrix_via_w_tilde__includes_source_interpolation__identical reg = aa.reg.Constant(coefficient=1.0) - over_sampler = aa.OverSampler(mask=mask, sub_size=1) - - mapper_0 = aa.Mapper( - mapper_grids=mapper_grids_0, over_sampler=over_sampler, regularization=reg - ) - mapper_1 = aa.Mapper( - mapper_grids=mapper_grids_1, over_sampler=over_sampler, regularization=reg - ) + mapper_0 = aa.Mapper(mapper_grids=mapper_grids_0, regularization=reg) + mapper_1 = aa.Mapper(mapper_grids=mapper_grids_1, regularization=reg) image = aa.Array2D.no_mask(values=np.random.random((7, 7)), pixel_scales=1.0) noise_map = aa.Array2D.no_mask(values=np.random.random((7, 7)), pixel_scales=1.0) diff --git a/test_autoarray/inversion/pixelization/mappers/test_abstract.py b/test_autoarray/inversion/pixelization/mappers/test_abstract.py index 1baf2f5f..8e154b21 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_abstract.py +++ b/test_autoarray/inversion/pixelization/mappers/test_abstract.py @@ -180,9 +180,7 @@ def test__interpolated_array_from(grid_2d_7x7): source_plane_mesh_grid=mesh_grid, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=None, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) interpolated_array_via_mapper = mapper.interpolated_array_from( values=np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]), @@ -204,6 +202,7 @@ def test__mapped_to_source_from(grid_2d_7x7): values=[[0.1, 0.1], [1.1, 0.6], [2.1, 0.1], [0.4, 1.1], [1.1, 7.1], [2.1, 1.1]], shape_native=(3, 2), pixel_scales=1.0, + over_sampling_size=1, ) mesh_grid = aa.Mesh2DDelaunay(values=mesh_grid) @@ -214,11 +213,7 @@ def test__mapped_to_source_from(grid_2d_7x7): source_plane_mesh_grid=mesh_grid, ) - over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) - - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) array_slim = aa.Array2D.no_mask( [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], diff --git a/test_autoarray/inversion/pixelization/mappers/test_delaunay.py b/test_autoarray/inversion/pixelization/mappers/test_delaunay.py index 3aaaefb6..dd8a654f 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_delaunay.py +++ b/test_autoarray/inversion/pixelization/mappers/test_delaunay.py @@ -2,26 +2,23 @@ import autoarray as aa -def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_7x7): +def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_sub_1_7x7): mesh_grid = aa.Grid2D.no_mask( values=[[0.1, 0.1], [1.1, 0.6], [2.1, 0.1], [0.4, 1.1], [1.1, 7.1], [2.1, 1.1]], shape_native=(3, 2), pixel_scales=1.0, + over_sampling_size=1, ) mesh_grid = aa.Mesh2DDelaunay(values=mesh_grid) mapper_grids = aa.MapperGrids( - mask=grid_2d_7x7.mask, - source_plane_data_grid=grid_2d_7x7, + mask=grid_2d_sub_1_7x7.mask, + source_plane_data_grid=grid_2d_sub_1_7x7, source_plane_mesh_grid=mesh_grid, ) - over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) - - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) simplex_index_for_sub_slim_index = mapper.delaunay.find_simplex( mapper.source_plane_data_grid diff --git a/test_autoarray/inversion/pixelization/mappers/test_factory.py b/test_autoarray/inversion/pixelization/mappers/test_factory.py index 7bbeacb9..e5018c54 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_factory.py +++ b/test_autoarray/inversion/pixelization/mappers/test_factory.py @@ -20,23 +20,20 @@ def test__rectangular_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - over_sampler = aa.OverSampler(mask=mask, sub_size=2) - grid_over_sampled = over_sampler.uniform_over_sampled - grid_over_sampled[0, 0] = -2.0 - grid_over_sampled[0, 1] = 2.0 + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + grid.grid_over_sampled[0, 0] = -2.0 + grid.grid_over_sampled[0, 1] = 2.0 mesh = aa.mesh.Rectangular(shape=(3, 3)) mapper_grids = mesh.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=grid_over_sampled, + source_plane_data_grid=grid, source_plane_mesh_grid=None, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) assert isinstance(mapper, aa.MapperRectangular) assert mapper.image_plane_mesh_grid == None @@ -73,10 +70,10 @@ def test__delaunay_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - over_sampler = aa.OverSampler(mask=mask, sub_size=2) - grid_over_sampled = over_sampler.uniform_over_sampled - grid_over_sampled[0, 0] = -2.0 - grid_over_sampled[0, 1] = 2.0 + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + + grid.grid_over_sampled[0, 0] = -2.0 + grid.grid_over_sampled[0, 1] = 2.0 mesh = aa.mesh.Delaunay() image_mesh = aa.image_mesh.Overlay(shape=(3, 3)) @@ -87,13 +84,11 @@ def test__delaunay_mapper(): mapper_grids = mesh.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=grid_over_sampled, + source_plane_data_grid=grid, source_plane_mesh_grid=image_plane_mesh_grid, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) assert isinstance(mapper, aa.MapperDelaunay) assert (mapper.source_plane_mesh_grid == image_plane_mesh_grid).all() @@ -131,10 +126,10 @@ def test__voronoi_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - over_sampler = aa.OverSampler(mask=mask, sub_size=2) - grid_over_sampled = over_sampler.uniform_over_sampled - grid_over_sampled[0, 0] = -2.0 - grid_over_sampled[0, 1] = 2.0 + grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + + grid.grid_over_sampled[0, 0] = -2.0 + grid.grid_over_sampled[0, 1] = 2.0 mesh = aa.mesh.Voronoi() image_mesh = aa.image_mesh.Overlay(shape=(3, 3)) @@ -145,13 +140,11 @@ def test__voronoi_mapper(): mapper_grids = mesh.mapper_grids_from( mask=mask, border_relocator=None, - source_plane_data_grid=grid_over_sampled, + source_plane_data_grid=grid, source_plane_mesh_grid=image_plane_mesh_grid, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) assert (mapper.source_plane_mesh_grid == image_plane_mesh_grid).all() assert mapper.source_plane_mesh_grid.origin == pytest.approx((0.0, 0.0), 1.0e-4) diff --git a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py index 2f945024..4901477d 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py +++ b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py @@ -18,24 +18,23 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): ], pixel_scales=1.0, shape_native=(3, 3), + over_sampling_size=1, ) - mesh_grid = aa.Mesh2DRectangular.overlay_grid(shape_native=(3, 3), grid=grid) + mesh_grid = aa.Mesh2DRectangular.overlay_grid( + shape_native=(3, 3), grid=grid.grid_over_sampled + ) mapper_grids = aa.MapperGrids( mask=grid.mask, source_plane_data_grid=grid, source_plane_mesh_grid=mesh_grid ) - over_sampler = aa.OverSampler(mask=grid.mask, sub_size=1) - - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) pix_indexes_for_sub_slim_index_util = np.array( [ aa.util.geometry.grid_pixel_indexes_2d_slim_from( - grid_scaled_2d_slim=np.array(grid), + grid_scaled_2d_slim=np.array(grid.grid_over_sampled), shape_native=mesh_grid.shape_native, pixel_scales=mesh_grid.pixel_scales, origin=mesh_grid.origin, @@ -48,21 +47,19 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): ).all() -def test__pixel_signals_from__matches_util(grid_2d_7x7, image_7x7): - mesh_grid = aa.Mesh2DRectangular.overlay_grid(shape_native=(3, 3), grid=grid_2d_7x7) - - over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) +def test__pixel_signals_from__matches_util(grid_2d_sub_1_7x7, image_7x7): + mesh_grid = aa.Mesh2DRectangular.overlay_grid( + shape_native=(3, 3), grid=grid_2d_sub_1_7x7.grid_over_sampled + ) mapper_grids = aa.MapperGrids( - mask=grid_2d_7x7.mask, - source_plane_data_grid=grid_2d_7x7, + mask=grid_2d_sub_1_7x7.mask, + source_plane_data_grid=grid_2d_sub_1_7x7, source_plane_mesh_grid=mesh_grid, adapt_data=image_7x7, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) pixel_signals = mapper.pixel_signals_from(signal_scale=2.0) @@ -72,7 +69,7 @@ def test__pixel_signals_from__matches_util(grid_2d_7x7, image_7x7): pix_indexes_for_sub_slim_index=mapper.pix_indexes_for_sub_slim_index, pix_size_for_sub_slim_index=mapper.pix_sizes_for_sub_slim_index, pixel_weights=mapper.pix_weights_for_sub_slim_index, - slim_index_for_sub_slim_index=over_sampler.slim_for_sub_slim, + slim_index_for_sub_slim_index=grid_2d_sub_1_7x7.over_sampler.slim_for_sub_slim, adapt_data=np.array(image_7x7), ) diff --git a/test_autoarray/inversion/pixelization/mappers/test_voronoi.py b/test_autoarray/inversion/pixelization/mappers/test_voronoi.py index 9a84e7ef..8b54842e 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_voronoi.py +++ b/test_autoarray/inversion/pixelization/mappers/test_voronoi.py @@ -3,26 +3,25 @@ import autoarray as aa -def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_7x7): +def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_sub_1_7x7): source_plane_mesh_grid = aa.Grid2D.no_mask( values=[[0.1, 0.1], [1.1, 0.6], [2.1, 0.1], [0.4, 1.1], [1.1, 7.1], [2.1, 1.1]], shape_native=(3, 2), pixel_scales=1.0, + over_sampling_size=1, ) source_plane_mesh_grid = aa.Mesh2DVoronoi( values=source_plane_mesh_grid, ) - over_sampler = aa.OverSampler(mask=grid_2d_7x7.mask, sub_size=1) - source_plane_mesh_grid = aa.Mesh2DVoronoi( values=source_plane_mesh_grid, ) mapper_grids = aa.MapperGrids( - mask=grid_2d_7x7.mask, - source_plane_data_grid=grid_2d_7x7, + mask=grid_2d_sub_1_7x7.mask, + source_plane_data_grid=grid_2d_sub_1_7x7, source_plane_mesh_grid=source_plane_mesh_grid, ) pytest.importorskip( @@ -30,16 +29,14 @@ def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_7x7): reason="Voronoi C library not installed, see util.nn README.md", ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=over_sampler, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) ( pix_indexes_for_sub_slim_index_util, sizes, weights, ) = aa.util.mapper.pix_size_weights_voronoi_nn_from( - grid=grid_2d_7x7, mesh_grid=source_plane_mesh_grid + grid=grid_2d_sub_1_7x7, mesh_grid=source_plane_mesh_grid ) assert ( diff --git a/test_autoarray/inversion/pixelization/mesh/test_abstract.py b/test_autoarray/inversion/pixelization/mesh/test_abstract.py index ca431fe8..c00b6817 100644 --- a/test_autoarray/inversion/pixelization/mesh/test_abstract.py +++ b/test_autoarray/inversion/pixelization/mesh/test_abstract.py @@ -29,9 +29,7 @@ def test__grid_is_relocated_via_border(grid_2d_7x7): source_plane_mesh_grid=image_mesh, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=None, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) assert grid[8, 0] != mapper.source_plane_data_grid[8, 0] assert mapper.source_plane_data_grid[8, 0] < 5.0 @@ -48,9 +46,7 @@ def test__grid_is_relocated_via_border(grid_2d_7x7): source_plane_mesh_grid=image_mesh, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=None, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) assert isinstance(mapper, aa.MapperDelaunay) assert image_mesh[0, 0] != mapper.source_plane_mesh_grid[0, 0] @@ -67,9 +63,7 @@ def test__grid_is_relocated_via_border(grid_2d_7x7): source_plane_mesh_grid=image_mesh, ) - mapper = aa.Mapper( - mapper_grids=mapper_grids, over_sampler=None, regularization=None - ) + mapper = aa.Mapper(mapper_grids=mapper_grids, regularization=None) assert isinstance(mapper, aa.MapperDelaunay) assert image_mesh[0, 0] != mapper.source_plane_mesh_grid[0, 0] diff --git a/test_autoarray/inversion/plot/test_mapper_plotters.py b/test_autoarray/inversion/plot/test_mapper_plotters.py index 85ac07f8..7990a900 100644 --- a/test_autoarray/inversion/plot/test_mapper_plotters.py +++ b/test_autoarray/inversion/plot/test_mapper_plotters.py @@ -67,9 +67,12 @@ def test__get_2d__via_mapper_for_source_from(rectangular_mapper_7x7_3x3): assert mapper_plotter.visuals_2d.origin == None assert get_2d.origin.in_list == [(0.0, 0.0)] - assert (get_2d.grid == rectangular_mapper_7x7_3x3.source_plane_data_grid).all() + assert ( + get_2d.grid + == rectangular_mapper_7x7_3x3.source_plane_data_grid.grid_over_sampled + ).all() assert (get_2d.mesh_grid == rectangular_mapper_7x7_3x3.source_plane_mesh_grid).all() - border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid[ + border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.grid_over_sampled[ rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim ] assert (get_2d.border == border_grid).all() diff --git a/test_autoarray/plot/get_visuals/test_two_d.py b/test_autoarray/plot/get_visuals/test_two_d.py index c74a2ff0..51e81838 100644 --- a/test_autoarray/plot/get_visuals/test_two_d.py +++ b/test_autoarray/plot/get_visuals/test_two_d.py @@ -107,9 +107,10 @@ def test__via_mapper_for_source_from(rectangular_mapper_7x7_3x3): assert visuals_2d.origin == (1.0, 1.0) assert ( - visuals_2d_via.grid == rectangular_mapper_7x7_3x3.source_plane_data_grid + visuals_2d_via.grid + == rectangular_mapper_7x7_3x3.source_plane_data_grid.grid_over_sampled ).all() - border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid[ + border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.grid_over_sampled[ rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim ] assert (visuals_2d_via.border == border_grid).all() From 3e0f4420cb03ecd1c79da0e86261773f277ce7d5 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 21:14:41 +0000 Subject: [PATCH 10/26] horrible refactor nearly done --- autoarray/inversion/pixelization/mappers/rectangular.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoarray/inversion/pixelization/mappers/rectangular.py b/autoarray/inversion/pixelization/mappers/rectangular.py index 6ae84064..6001792f 100644 --- a/autoarray/inversion/pixelization/mappers/rectangular.py +++ b/autoarray/inversion/pixelization/mappers/rectangular.py @@ -106,8 +106,10 @@ def pix_sub_weights(self) -> PixSubWeights: origin=self.source_plane_mesh_grid.origin, ).astype("int") + mappings = mappings.reshape((len(mappings), 1)) + return PixSubWeights( - mappings=mappings.reshape((len(mappings), 1)), + mappings=mappings, sizes=np.ones(len(mappings), dtype="int"), weights=np.ones( (len(self.source_plane_data_grid.grid_over_sampled), 1), dtype="int" From 215baab85124e85181d0680520dabd05428ebef3 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 21:18:52 +0000 Subject: [PATCH 11/26] remove config nonsense --- .../over_sampling/over_sample_util.py | 56 ------------------- test_autoarray/config/grids.yaml | 37 +----------- 2 files changed, 1 insertion(+), 92 deletions(-) diff --git a/autoarray/operators/over_sampling/over_sample_util.py b/autoarray/operators/over_sampling/over_sample_util.py index e0e59566..c6584c92 100644 --- a/autoarray/operators/over_sampling/over_sample_util.py +++ b/autoarray/operators/over_sampling/over_sample_util.py @@ -553,62 +553,6 @@ def over_sample_size_via_radial_bins_from( return Array2D(values=sub_size, mask=grid.mask) -def over_sample_size_via_adaptive_scheme_from( - grid: Grid2D, name: str, centre: Tuple[float, float] -) -> Array2D: - """ - Returns a 2D grid whose over sampling is adaptive, placing a high number of sub-pixels in the regions of the - grid closest to the centre input (y,x) coordinates. - - This adaptive over sampling is primarily used in PyAutoGalaxy, to evaluate the image of a light profile - (e.g. a Sersic function) with high levels of sub gridding in its centre and lower levels of sub gridding - further away from the centre (saving computational time). - - The `autogalaxy_workspace` and `autolens_workspace` packages have guides called `over_sampling.ipynb` - which describe over sampling in more detail. - - The inputs `name` and `centre` typically correspond to the name of the light profile (e.g. `Sersic`) and - its `centre`, so that the adaptive over sampling parameters for that light profile are loaded from config - files and used to setup the grid. - - Parameters - ---------- - name - The name of the light profile, which is used to load the adaptive over sampling parameters from config files. - centre - The (y,x) centre of the adaptive over sampled grid, around which the highest sub-pixel resolution is placed. - - Returns - ------- - A new Grid2D with adaptive over sampling. - - """ - - if not grid.is_uniform: - raise exc.GridException( - "You cannot make an adaptive over-sampled grid from a non-uniform grid." - ) - - sub_size_list = conf.instance["grids"]["over_sampling"]["sub_size_list"][name] - radial_factor_list = conf.instance["grids"]["over_sampling"]["radial_factor_list"][ - name - ] - - centre = grid.geometry.scaled_coordinate_2d_to_scaled_at_pixel_centre_from( - scaled_coordinate_2d=centre - ) - - return over_sample_size_via_radial_bins_from( - grid=grid, - sub_size_list=sub_size_list, - radial_list=[ - min(grid.pixel_scales) * radial_factor - for radial_factor in radial_factor_list - ], - centre_list=[centre], - ) - - def over_sample_size_via_adapt_from( data: Array2D, noise_map: Array2D, diff --git a/test_autoarray/config/grids.yaml b/test_autoarray/config/grids.yaml index b3fe649c..61c268a2 100644 --- a/test_autoarray/config/grids.yaml +++ b/test_autoarray/config/grids.yaml @@ -9,39 +9,4 @@ radial_minimum: radial_minimum: - MockGridRadialMinimum: 2.5 - - -# Over sampling is an important numerical technique, whereby light profiles images are evaluated on a higher resolution -# grid than the image data to ensure the calculation is accurate. - -# By default, a user does not specify the over sampling factor, and a default over sampling scheme is used for each -# profile. This scheme first goes to the centre of the profile, and computes circles with certain radial values -# (e.g. radii). It then assigns an over sampling `sub_size` to each circle, where the central circles have the highest -# over sampling factor and the outer circles have the lowest. - -# The size of the circles that are appropriate for determining the over sampling factor are dependent on the resolution -# of the grid. For a high resolution grid (e.g. low pixel scale), a smaller circle central circle is necessary to -# over sample the profile accurately. The config file below therefore specifies the "radial factors" used for -# automatically determining the over sampling factors for each profile, which is the factor the pixel scale is multiplied -# by to determine the circle size. - -# The config entry below defines the default over sampling factor for each profile, where: - -# radial_factor_list: The factors that are multiplied by the pixel scale to determine the circle size that is used. -# sub_size_list: The over sampling factor that is used for each circle size. - -# For the default entries below, oversampling of degree 32 x 32 is used within a circle of radius 3.01 x pixel scale, -# 4 x 4 within a circle of radius 10.01 x pixel scale and 2 x 2 for all pixels outside of this radius. - -# For unit tests, we disable this feature by setting the over sampling factors to 1.0 and the sub sizes to 1. - -over_sampling: - radial_factor_list: - MockGrid1DLikeObj: [1.0] - MockGrid2DLikeObj: [1.0] - MockGridRadialMinimum: [1.0] - sub_size_list: - MockGrid1DLikeObj: [1, 1] - MockGrid2DLikeObj: [1, 1] - MockGridRadialMinimum: [1, 1] \ No newline at end of file + MockGridRadialMinimum: 2.5 \ No newline at end of file From 73d67002b840ec8e1b9f7c53500a13951d161e93 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 15 Dec 2024 21:30:50 +0000 Subject: [PATCH 12/26] uniform -> lp --- autoarray/dataset/abstract/dataset.py | 2 +- autoarray/dataset/grids.py | 41 +++---------------- autoarray/dataset/imaging/dataset.py | 6 +-- autoarray/dataset/interferometer/dataset.py | 3 +- autoarray/dataset/mock/mock_dataset.py | 3 +- autoarray/dataset/over_sampling.py | 12 ++---- autoarray/dataset/plot/imaging_plotters.py | 2 +- autoarray/fit/fit_dataset.py | 13 ++---- autoarray/fixtures.py | 10 ++--- .../inversion/inversion/dataset_interface.py | 2 +- .../dataset/abstract/test_dataset.py | 28 ++++++------- .../dataset/plot/test_imaging_plotters.py | 4 -- test_autoarray/fit/test_fit_dataset.py | 2 +- 13 files changed, 39 insertions(+), 89 deletions(-) diff --git a/autoarray/dataset/abstract/dataset.py b/autoarray/dataset/abstract/dataset.py index 62bf602f..ea7c1671 100644 --- a/autoarray/dataset/abstract/dataset.py +++ b/autoarray/dataset/abstract/dataset.py @@ -97,7 +97,7 @@ def __init__( @property def grid(self): - return self.grids.uniform + return self.grids.lp @cached_property def grids(self): diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index 16e3c4d4..9b66d302 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -27,11 +27,6 @@ def __init__( which is used for most normal calculations (e.g. evaluating the amount of light that falls in an pixel from a light profile). - - `non_uniform`: A grid of (y,x) coordinates which aligns with the centre of every image pixel of the image - data, but where their values are going to be deflected to become non-uniform such that the adaptive over - sampling scheme used for the main grid does not apply. This is used to compute over sampled light profiles of - lensed sources in PyAutoLens. - - `pixelization`: A grid of (y,x) coordinates which again align with the centre of every image pixel of the image data. This grid is used specifically for pixelizations computed via the `inversion` module, which can benefit from using different oversampling schemes than the normal grid. @@ -57,7 +52,7 @@ def __init__( self.psf = psf @cached_property - def uniform(self) -> Union[Grid1D, Grid2D]: + 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 @@ -71,31 +66,7 @@ def uniform(self) -> Union[Grid1D, Grid2D]: """ return Grid2D.from_mask( mask=self.mask, - over_sampling_size=self.over_sampling.uniform, - ) - - @cached_property - def non_uniform(self) -> Optional[Union[Grid1D, Grid2D]]: - """ - Returns the grid of (y,x) Cartesian coordinates at the centre of every pixel in the masked data, but - with a different over sampling scheme designed for - - where - their values are going to be deflected to become non-uniform such that the adaptive over sampling scheme used - for the main grid does not apply. - - This is used to compute over sampled light profiles of lensed sources in PyAutoLens. - - - 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( - mask=self.mask, - over_sampling_size=self.over_sampling.non_uniform, + over_sampling_size=self.over_sampling.lp, ) @cached_property @@ -139,7 +110,7 @@ def blurring(self) -> Optional[Grid2D]: if self.psf is None: return None - return self.uniform.blurring_grid_via_kernel_shape_from( + return self.lp.blurring_grid_via_kernel_shape_from( kernel_shape_native=self.psf.shape_native, ) @@ -153,14 +124,12 @@ def border_relocator(self) -> BorderRelocator: class GridsInterface: def __init__( self, - uniform=None, - non_uniform=None, + lp=None, pixelization=None, blurring=None, border_relocator=None, ): - self.uniform = uniform - self.non_uniform = non_uniform + self.lp = lp self.pixelization = pixelization self.blurring = blurring self.border_relocator = border_relocator diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index c3aa4faa..2c3e50b8 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -455,13 +455,11 @@ def apply_over_sampling( This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). """ - uniform = over_sampling.uniform or self.over_sampling.uniform - non_uniform = over_sampling.non_uniform or self.over_sampling.non_uniform + uniform = over_sampling.lp or self.over_sampling.lp pixelization = over_sampling.pixelization or self.over_sampling.pixelization over_sampling = OverSamplingDataset( - uniform=uniform, - non_uniform=non_uniform, + lp=uniform, pixelization=pixelization, ) diff --git a/autoarray/dataset/interferometer/dataset.py b/autoarray/dataset/interferometer/dataset.py index 18d896bc..abc155fd 100644 --- a/autoarray/dataset/interferometer/dataset.py +++ b/autoarray/dataset/interferometer/dataset.py @@ -75,9 +75,8 @@ def __init__( self.real_space_mask = real_space_mask over_sampling = OverSamplingDataset( - uniform=1, + lp=1, pixelization=1, - non_uniform=1, ) super().__init__( diff --git a/autoarray/dataset/mock/mock_dataset.py b/autoarray/dataset/mock/mock_dataset.py index 29ece502..c425c6a2 100644 --- a/autoarray/dataset/mock/mock_dataset.py +++ b/autoarray/dataset/mock/mock_dataset.py @@ -6,8 +6,7 @@ class MockDataset: def __init__(self, grids=None, psf=None, mask=None): if grids is None: self.grids = GridsInterface( - uniform=None, - non_uniform=None, + lp=None, pixelization=Grid2D.no_mask(values=[[[1.0, 1.0]]], pixel_scales=1.0), blurring=None, border_relocator=None, diff --git a/autoarray/dataset/over_sampling.py b/autoarray/dataset/over_sampling.py index b4661783..10c3187a 100644 --- a/autoarray/dataset/over_sampling.py +++ b/autoarray/dataset/over_sampling.py @@ -9,8 +9,7 @@ class OverSamplingDataset: def __init__( self, - uniform: Union[int, Array2D] = 4, - non_uniform: Union[int, Array2D] = 4, + lp: Union[int, Array2D] = 4, pixelization: Union[int, Array2D] = 4, ): """ @@ -41,18 +40,13 @@ def __init__( Parameters ---------- - uniform + lp The over sampling scheme, which divides the grid into a sub grid of smaller pixels when computing values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls into each pixel. pixelization How over sampling is performed for the grid which is associated with a pixelization, which is therefore passed into the calculations performed in the `inversion` module. - non_uniform - The over sampling scheme when the grid input into a function is not a uniform grid. This is used - by **PyAutoLens** when the grid has been deflected and ray-traced and therefore some of the default - over sampling schemes are not appropriate. """ - self.uniform = uniform + self.lp = lp self.pixelization = pixelization - self.non_uniform = non_uniform diff --git a/autoarray/dataset/plot/imaging_plotters.py b/autoarray/dataset/plot/imaging_plotters.py index 59f9ac10..a10a2cfc 100644 --- a/autoarray/dataset/plot/imaging_plotters.py +++ b/autoarray/dataset/plot/imaging_plotters.py @@ -126,7 +126,7 @@ def figures_2d( if over_sampling: self.mat_plot_2d.plot_array( - array=self.dataset.grids.uniform.over_sampling_size, + array=self.dataset.grids.lp.over_sampling_size, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( title=title_str or f"Over Sampling (Uniform)", diff --git a/autoarray/fit/fit_dataset.py b/autoarray/fit/fit_dataset.py index 1ace1795..c2f49894 100644 --- a/autoarray/fit/fit_dataset.py +++ b/autoarray/fit/fit_dataset.py @@ -161,8 +161,7 @@ def grids(self) -> GridsInterface: if offset[0] == 0.0 and offset[1] == 0.0: return GridsInterface( - uniform=self.dataset.grids.uniform, - non_uniform=self.dataset.grids.non_uniform, + lp=self.dataset.grids.lp, pixelization=self.dataset.grids.pixelization, blurring=self.dataset.grids.blurring, border_relocator=self.dataset.grids.border_relocator, @@ -174,11 +173,8 @@ def subtracted_from(grid, offset): return grid.subtracted_from(offset=offset) - uniform = subtracted_from( - grid=self.dataset.grids.uniform, offset=self.dataset_model.grid_offset - ) - non_uniform = subtracted_from( - grid=self.dataset.grids.non_uniform, offset=self.dataset_model.grid_offset + lp = subtracted_from( + grid=self.dataset.grids.lp, offset=self.dataset_model.grid_offset ) pixelization = subtracted_from( grid=self.dataset.grids.pixelization, offset=self.dataset_model.grid_offset @@ -188,8 +184,7 @@ def subtracted_from(grid, offset): ) return GridsInterface( - uniform=uniform, - non_uniform=non_uniform, + lp=lp, pixelization=pixelization, blurring=blurring, border_relocator=self.dataset.grids.border_relocator, diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 8b5155f6..7e76c9b6 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -160,7 +160,7 @@ def make_imaging_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=1), + over_sampling=aa.OverSamplingDataset(lp=1), ) @@ -169,7 +169,7 @@ def make_imaging_7x7_sub_2(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=2), + over_sampling=aa.OverSamplingDataset(lp=2), ) @@ -178,7 +178,7 @@ def make_imaging_covariance_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_covariance_matrix=make_noise_covariance_matrix_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=1), + over_sampling=aa.OverSamplingDataset(lp=1), ) @@ -187,7 +187,7 @@ def make_imaging_7x7_no_blur(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=1), + over_sampling=aa.OverSamplingDataset(lp=1), ) @@ -196,7 +196,7 @@ def make_imaging_7x7_no_blur_sub_2(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(uniform=2), + over_sampling=aa.OverSamplingDataset(lp=2), ) diff --git a/autoarray/inversion/inversion/dataset_interface.py b/autoarray/inversion/inversion/dataset_interface.py index 66025355..abc78041 100644 --- a/autoarray/inversion/inversion/dataset_interface.py +++ b/autoarray/inversion/inversion/dataset_interface.py @@ -66,4 +66,4 @@ def __init__( @property def mask(self): - return self.grids.uniform.mask + return self.grids.lp.mask diff --git a/test_autoarray/dataset/abstract/test_dataset.py b/test_autoarray/dataset/abstract/test_dataset.py index 05350fee..b72af8ab 100644 --- a/test_autoarray/dataset/abstract/test_dataset.py +++ b/test_autoarray/dataset/abstract/test_dataset.py @@ -65,12 +65,12 @@ def test__grid__uses_mask_and_settings( masked_imaging_7x7 = ds.AbstractDataset( data=masked_image_7x7, noise_map=masked_noise_map_7x7, - over_sampling=aa.OverSamplingDataset(uniform=2), + over_sampling=aa.OverSamplingDataset(lp=2), ) - assert isinstance(masked_imaging_7x7.grids.uniform, aa.Grid2D) - assert (masked_imaging_7x7.grids.uniform == grid_2d_7x7).all() - assert (masked_imaging_7x7.grids.uniform.slim == grid_2d_7x7).all() + assert isinstance(masked_imaging_7x7.grids.lp, aa.Grid2D) + assert (masked_imaging_7x7.grids.lp == grid_2d_7x7).all() + assert (masked_imaging_7x7.grids.lp.slim == grid_2d_7x7).all() def test__grids_pixelization__uses_mask_and_settings( @@ -95,7 +95,7 @@ def test__grids_pixelization__uses_mask_and_settings( data=masked_image_7x7, noise_map=masked_noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=2, + lp=2, pixelization=4, ), ) @@ -109,12 +109,12 @@ def test__grid_settings__sub_size(image_7x7, noise_map_7x7): data=image_7x7, noise_map=noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=2, + lp=2, pixelization=4, ), ) - assert dataset_7x7.grids.uniform.over_sampling_size[0] == 2 + assert dataset_7x7.grids.lp.over_sampling_size[0] == 2 assert dataset_7x7.grids.pixelization.over_sampling_size[0] == 4 @@ -141,7 +141,7 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): data=image_7x7, noise_map=noise_map_7x7, over_sampling=aa.OverSamplingDataset( - uniform=2, + lp=2, pixelization=2, ), ) @@ -149,24 +149,24 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): # The grid and grid_pixelizaiton are a cached_property which needs to be reset, # Which the code below tests. - grid_sub_2 = dataset_7x7.grids.uniform + grid_sub_2 = dataset_7x7.grids.lp grids_pixelization_sub_2 = dataset_7x7.grids.pixelization - dataset_7x7.grids.__dict__["uniform"][0][0] = 100.0 + dataset_7x7.grids.__dict__["lp"][0][0] = 100.0 dataset_7x7.grids.__dict__["pixelization"][0][0] = 100.0 - assert dataset_7x7.grids.uniform[0][0] == pytest.approx(100.0, 1.0e-4) + assert dataset_7x7.grids.lp[0][0] == pytest.approx(100.0, 1.0e-4) assert dataset_7x7.grids.pixelization[0][0] == pytest.approx(100.0, 1.0e-4) dataset_7x7 = dataset_7x7.apply_over_sampling( over_sampling=aa.OverSamplingDataset( - uniform=4, + lp=4, pixelization=4, ) ) - assert dataset_7x7.over_sampling.uniform == 4 + assert dataset_7x7.over_sampling.lp == 4 assert dataset_7x7.over_sampling.pixelization == 4 - assert dataset_7x7.grids.uniform[0][0] == pytest.approx(3.0, 1.0e-4) + assert dataset_7x7.grids.lp[0][0] == pytest.approx(3.0, 1.0e-4) assert dataset_7x7.grids.pixelization[0][0] == pytest.approx(3.0, 1.0e-4) diff --git a/test_autoarray/dataset/plot/test_imaging_plotters.py b/test_autoarray/dataset/plot/test_imaging_plotters.py index e7102c5f..6f71291f 100644 --- a/test_autoarray/dataset/plot/test_imaging_plotters.py +++ b/test_autoarray/dataset/plot/test_imaging_plotters.py @@ -19,10 +19,6 @@ def test__individual_attributes_are_output( ): visuals = aplt.Visuals2D(mask=mask_2d_7x7, positions=grid_2d_irregular_7x7_list) - imaging_7x7 = imaging_7x7.apply_over_sampling( - over_sampling=aa.OverSamplingDataset(non_uniform=1) - ) - dataset_plotter = aplt.ImagingPlotter( dataset=imaging_7x7, visuals_2d=visuals, diff --git a/test_autoarray/fit/test_fit_dataset.py b/test_autoarray/fit/test_fit_dataset.py index 2ddb13ad..1f8ec638 100644 --- a/test_autoarray/fit/test_fit_dataset.py +++ b/test_autoarray/fit/test_fit_dataset.py @@ -74,5 +74,5 @@ def test__grid_offset_via_data_model(masked_imaging_7x7, model_image_7x7): assert fit.dataset_model.grid_offset == (1.0, 2.0) - assert fit.grids.uniform[0] == pytest.approx((0.0, -3.0), 1.0e-4) + assert fit.grids.lp[0] == pytest.approx((0.0, -3.0), 1.0e-4) assert fit.grids.pixelization[0] == pytest.approx((0.0, -3.0), 1.0e-4) From 1fab29edc6e91a5474a0c344de7ce947a4214362 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 16 Dec 2024 15:32:15 +0000 Subject: [PATCH 13/26] border relocator refactor --- .../pixelization/border_relocator.py | 78 +++++++++++++++---- .../pixelization/mesh/rectangular.py | 11 +-- .../pixelization/mesh/triangulation.py | 19 ++--- .../inversion/pixelization/mesh/voronoi.py | 1 - .../pixelization/test_border_relocator.py | 14 ++-- 5 files changed, 78 insertions(+), 45 deletions(-) diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index da3cd86e..dfdf5610 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -139,6 +139,44 @@ def __init__(self, mask: Mask2D, sub_size: Union[int, Array2D]): self.sub_size = sub_size + @cached_property + def border_slim(self): + """ + Returns the 1D ``slim`` indexes of border pixels in the ``Mask2D``, representing all unmasked + sub-pixels (given by ``False``) which neighbor any masked value (give by ``True``) and which are on the + extreme exterior of the mask. + + The indexes are the extended below to form the ``sub_border_slim`` which is illustrated above. + + This quantity is too complicated to write-out in a docstring, and it is recommended you print it in + Python code to understand it if anything is unclear. + + Examples + -------- + + .. code-block:: python + + import autoarray as aa + + mask_2d = aa.Mask2D( + mask=[[True, True, True, True, True, True, True, True, True], + [True, False, False, False, False, False, False, False, True], + [True, False, True, True, True, True, True, False, True], + [True, False, True, False, False, False, True, False, True], + [True, False, True, False, True, False, True, False, True], + [True, False, True, False, False, False, True, False, True], + [True, False, True, True, True, True, True, False, True], + [True, False, False, False, False, False, False, False, True], + [True, True, True, True, True, True, True, True, True]] + pixel_scales=1.0, + ) + + derive_indexes_2d = aa.DeriveIndexes2D(mask=mask_2d) + + print(derive_indexes_2d.border_slim) + """ + return self.mask.derive_indexes.border_slim + @cached_property def sub_border_slim(self) -> np.ndarray: """ @@ -179,15 +217,6 @@ def sub_border_slim(self) -> np.ndarray: mask_2d=np.array(self.mask), sub_size=self.sub_size ).astype("int") - @property - def sub_grid(self): - return 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=np.array(self.sub_size), - origin=self.mask.origin, - ) - @cached_property def border_grid(self) -> np.ndarray: """ @@ -206,9 +235,16 @@ def sub_border_grid(self) -> np.ndarray: This is NOT all sub-pixels which are in mask pixels at the mask's border, but specifically the sub-pixels within these border pixels which are at the extreme edge of the border. """ - return self.sub_grid[self.sub_border_slim] + sub_grid = 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=np.array(self.sub_size), + origin=self.mask.origin, + ) - def relocated_grid_from(self, grid: Grid2DIrregular) -> Grid2DIrregular: + return sub_grid[self.sub_border_slim] + + def relocated_grid_from(self, grid: Grid2D) -> Grid2D: """ Relocate the coordinates of a grid to the border of this grid if they are outside the border, where the border is defined as all pixels at the edge of the grid's mask (see *mask._border_1d_indexes*). @@ -235,11 +271,21 @@ def relocated_grid_from(self, grid: Grid2DIrregular) -> Grid2DIrregular: if len(self.sub_border_grid) == 0: return grid - return Grid2DIrregular( - values=grid_2d_util.relocated_grid_via_jit_from( - grid=np.array(grid), - border_grid=np.array(grid[self.sub_border_slim]), - ), + values = grid_2d_util.relocated_grid_via_jit_from( + grid=np.array(grid), + border_grid=np.array(grid[self.border_slim]), + ) + + grid_over_sampled = grid_2d_util.relocated_grid_via_jit_from( + grid=np.array(grid.grid_over_sampled), + border_grid=np.array(grid.grid_over_sampled[self.sub_border_slim]), + ) + + return Grid2D( + values=values, + mask=grid.mask, + over_sampling_size=self.sub_size, + grid_over_sampled=grid_over_sampled, ) def relocated_mesh_grid_from( diff --git a/autoarray/inversion/pixelization/mesh/rectangular.py b/autoarray/inversion/pixelization/mesh/rectangular.py index e5fe9d50..acfb8680 100644 --- a/autoarray/inversion/pixelization/mesh/rectangular.py +++ b/autoarray/inversion/pixelization/mesh/rectangular.py @@ -104,19 +104,12 @@ def mapper_grids_from( self.run_time_dict = run_time_dict - relocated_grid_over_sampled = self.relocated_grid_from( + relocated_grid = self.relocated_grid_from( border_relocator=border_relocator, - source_plane_data_grid=source_plane_data_grid.grid_over_sampled, + source_plane_data_grid=source_plane_data_grid, preloads=preloads, ) - relocated_grid = Grid2D( - values=source_plane_data_grid, - mask=source_plane_data_grid.mask, - over_sampling_size=source_plane_data_grid.over_sampling_size, - grid_over_sampled=relocated_grid_over_sampled, - ) - mesh_grid = self.mesh_grid_from(source_plane_data_grid=relocated_grid) return MapperGrids( diff --git a/autoarray/inversion/pixelization/mesh/triangulation.py b/autoarray/inversion/pixelization/mesh/triangulation.py index 6c499f9f..9696af07 100644 --- a/autoarray/inversion/pixelization/mesh/triangulation.py +++ b/autoarray/inversion/pixelization/mesh/triangulation.py @@ -70,29 +70,22 @@ def mapper_grids_from( self.run_time_dict = run_time_dict - relocated_grid_over_sampled = self.relocated_grid_from( + relocated_grid = self.relocated_grid_from( border_relocator=border_relocator, - source_plane_data_grid=source_plane_data_grid.grid_over_sampled, + source_plane_data_grid=source_plane_data_grid, preloads=preloads, ) - relocated_grid = Grid2D( - values=source_plane_data_grid, - mask=source_plane_data_grid.mask, - over_sampling_size=source_plane_data_grid.over_sampling_size, - grid_over_sampled=relocated_grid_over_sampled, - ) - - relocated_source_plane_mesh_grid = self.relocated_mesh_grid_from( + relocated_mesh_grid = self.relocated_mesh_grid_from( border_relocator=border_relocator, - source_plane_data_grid=relocated_grid_over_sampled, + source_plane_data_grid=relocated_grid.grid_over_sampled, source_plane_mesh_grid=source_plane_mesh_grid, ) try: source_plane_mesh_grid = self.mesh_grid_from( - source_plane_data_grid=relocated_grid_over_sampled, - source_plane_mesh_grid=relocated_source_plane_mesh_grid, + source_plane_data_grid=relocated_grid.grid_over_sampled, + source_plane_mesh_grid=relocated_mesh_grid, ) except ValueError as e: raise e diff --git a/autoarray/inversion/pixelization/mesh/voronoi.py b/autoarray/inversion/pixelization/mesh/voronoi.py index ef76e7c1..dc8d9310 100644 --- a/autoarray/inversion/pixelization/mesh/voronoi.py +++ b/autoarray/inversion/pixelization/mesh/voronoi.py @@ -55,7 +55,6 @@ def mesh_grid_from( settings Settings controlling the pixelization for example if a border is used to relocate its exterior coordinates. """ - return Mesh2DVoronoi( values=source_plane_mesh_grid, ) diff --git a/test_autoarray/inversion/pixelization/test_border_relocator.py b/test_autoarray/inversion/pixelization/test_border_relocator.py index fb536691..e4861954 100644 --- a/test_autoarray/inversion/pixelization/test_border_relocator.py +++ b/test_autoarray/inversion/pixelization/test_border_relocator.py @@ -334,9 +334,9 @@ def test__relocated_grid_from__inside_border_no_relocations(): mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) - relocated_grid = border_relocator.relocated_grid_from(grid=grid.grid_over_sampled) + relocated_grid = border_relocator.relocated_grid_from(grid=grid) - assert (relocated_grid[1] == np.array([0.1, 0.1])).all() + assert (relocated_grid.grid_over_sampled[1] == np.array([0.1, 0.1])).all() def test__relocated_grid_from__outside_border_includes_relocations(): @@ -353,9 +353,11 @@ def test__relocated_grid_from__outside_border_includes_relocations(): mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) ) - relocated_grid = border_relocator.relocated_grid_from(grid=grid.grid_over_sampled) + relocated_grid = border_relocator.relocated_grid_from(grid=grid) - assert relocated_grid[1] == pytest.approx([0.97783243, 0.00968151], 1e-4) + assert relocated_grid.grid_over_sampled[1] == pytest.approx( + [0.97783243, 0.00968151], 1e-4 + ) def test__relocated_grid_from__positive_origin_included_in_relocate(): @@ -371,6 +373,6 @@ def test__relocated_grid_from__positive_origin_included_in_relocate(): border_relocator = aa.BorderRelocator(mask=mask, sub_size=grid.over_sampling_size) - relocated_grid = border_relocator.relocated_grid_from(grid=grid.grid_over_sampled) + relocated_grid = border_relocator.relocated_grid_from(grid=grid) - assert relocated_grid[1] == pytest.approx([1.97783243, 1.0], 1e-4) + assert relocated_grid.grid_over_sampled[1] == pytest.approx([1.97783243, 1.0], 1e-4) From 305f50eda2fc74f397e49f1384dd0d88a751a1d3 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 16 Dec 2024 15:36:16 +0000 Subject: [PATCH 14/26] simplify grid over sampled API --- autoarray/fixtures.py | 2 +- autoarray/inversion/pixelization/border_relocator.py | 8 ++++---- autoarray/inversion/pixelization/mappers/delaunay.py | 6 +++--- .../inversion/pixelization/mappers/rectangular.py | 4 ++-- autoarray/inversion/pixelization/mappers/voronoi.py | 2 +- autoarray/inversion/pixelization/mesh/rectangular.py | 2 +- .../inversion/pixelization/mesh/triangulation.py | 4 ++-- autoarray/operators/over_sampling/decorator.py | 4 ++-- autoarray/plot/get_visuals/two_d.py | 4 ++-- autoarray/plot/mat_plot/two_d.py | 6 +++--- autoarray/plot/visuals/two_d.py | 2 +- autoarray/structures/decorators/to_grid.py | 12 ++++++------ autoarray/structures/grids/uniform_2d.py | 8 ++++---- .../inversion/pixelization/mappers/test_factory.py | 12 ++++++------ .../pixelization/mappers/test_rectangular.py | 6 +++--- .../inversion/pixelization/test_border_relocator.py | 12 ++++++------ .../inversion/plot/test_mapper_plotters.py | 4 ++-- .../operators/over_sample/test_decorator.py | 2 +- test_autoarray/plot/get_visuals/test_two_d.py | 4 ++-- 19 files changed, 52 insertions(+), 52 deletions(-) diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 7e76c9b6..ff684ec4 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -359,7 +359,7 @@ def make_regularization_matern_kernel(): def make_rectangular_mesh_grid_3x3(): return aa.Mesh2DRectangular.overlay_grid( - grid=make_grid_2d_sub_2_7x7().grid_over_sampled, shape_native=(3, 3) + grid=make_grid_2d_sub_2_7x7().over_sampled, shape_native=(3, 3) ) diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index dfdf5610..6870f9a3 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -276,16 +276,16 @@ def relocated_grid_from(self, grid: Grid2D) -> Grid2D: border_grid=np.array(grid[self.border_slim]), ) - grid_over_sampled = grid_2d_util.relocated_grid_via_jit_from( - grid=np.array(grid.grid_over_sampled), - border_grid=np.array(grid.grid_over_sampled[self.sub_border_slim]), + over_sampled = grid_2d_util.relocated_grid_via_jit_from( + grid=np.array(grid.over_sampled), + border_grid=np.array(grid.over_sampled[self.sub_border_slim]), ) return Grid2D( values=values, mask=grid.mask, over_sampling_size=self.sub_size, - grid_over_sampled=grid_over_sampled, + over_sampled=over_sampled, ) def relocated_mesh_grid_from( diff --git a/autoarray/inversion/pixelization/mappers/delaunay.py b/autoarray/inversion/pixelization/mappers/delaunay.py index b579a1eb..f18c3b39 100644 --- a/autoarray/inversion/pixelization/mappers/delaunay.py +++ b/autoarray/inversion/pixelization/mappers/delaunay.py @@ -112,13 +112,13 @@ def pix_sub_weights(self) -> PixSubWeights: delaunay = self.delaunay simplex_index_for_sub_slim_index = delaunay.find_simplex( - self.source_plane_data_grid.grid_over_sampled + self.source_plane_data_grid.over_sampled ) pix_indexes_for_simplex_index = delaunay.simplices mappings, sizes = mapper_util.pix_indexes_for_sub_slim_index_delaunay_from( source_plane_data_grid=np.array( - self.source_plane_data_grid.grid_over_sampled + self.source_plane_data_grid.over_sampled ), simplex_index_for_sub_slim_index=simplex_index_for_sub_slim_index, pix_indexes_for_simplex_index=pix_indexes_for_simplex_index, @@ -130,7 +130,7 @@ def pix_sub_weights(self) -> PixSubWeights: weights = mapper_util.pixel_weights_delaunay_from( source_plane_data_grid=np.array( - self.source_plane_data_grid.grid_over_sampled + self.source_plane_data_grid.over_sampled ), source_plane_mesh_grid=np.array(self.source_plane_mesh_grid), slim_index_for_sub_slim_index=self.slim_index_for_sub_slim_index, diff --git a/autoarray/inversion/pixelization/mappers/rectangular.py b/autoarray/inversion/pixelization/mappers/rectangular.py index 6001792f..9a78c1b8 100644 --- a/autoarray/inversion/pixelization/mappers/rectangular.py +++ b/autoarray/inversion/pixelization/mappers/rectangular.py @@ -100,7 +100,7 @@ def pix_sub_weights(self) -> PixSubWeights: are equal to 1.0. """ mappings = geometry_util.grid_pixel_indexes_2d_slim_from( - grid_scaled_2d_slim=np.array(self.source_plane_data_grid.grid_over_sampled), + grid_scaled_2d_slim=np.array(self.source_plane_data_grid.over_sampled), shape_native=self.source_plane_mesh_grid.shape_native, pixel_scales=self.source_plane_mesh_grid.pixel_scales, origin=self.source_plane_mesh_grid.origin, @@ -112,6 +112,6 @@ def pix_sub_weights(self) -> PixSubWeights: mappings=mappings, sizes=np.ones(len(mappings), dtype="int"), weights=np.ones( - (len(self.source_plane_data_grid.grid_over_sampled), 1), dtype="int" + (len(self.source_plane_data_grid.over_sampled), 1), dtype="int" ), ) diff --git a/autoarray/inversion/pixelization/mappers/voronoi.py b/autoarray/inversion/pixelization/mappers/voronoi.py index b286f5bb..c8e54cbf 100644 --- a/autoarray/inversion/pixelization/mappers/voronoi.py +++ b/autoarray/inversion/pixelization/mappers/voronoi.py @@ -130,7 +130,7 @@ def pix_sub_weights(self) -> PixSubWeights: """ mappings, sizes, weights = mapper_util.pix_size_weights_voronoi_nn_from( - grid=self.source_plane_data_grid.grid_over_sampled, + grid=self.source_plane_data_grid.over_sampled, mesh_grid=self.source_plane_mesh_grid, ) diff --git a/autoarray/inversion/pixelization/mesh/rectangular.py b/autoarray/inversion/pixelization/mesh/rectangular.py index acfb8680..e6bf2d0a 100644 --- a/autoarray/inversion/pixelization/mesh/rectangular.py +++ b/autoarray/inversion/pixelization/mesh/rectangular.py @@ -142,7 +142,7 @@ def mesh_grid_from( by overlaying the `source_plane_data_grid` with the rectangular pixelization. """ return Mesh2DRectangular.overlay_grid( - shape_native=self.shape, grid=source_plane_data_grid.grid_over_sampled + shape_native=self.shape, grid=source_plane_data_grid.over_sampled ) @property diff --git a/autoarray/inversion/pixelization/mesh/triangulation.py b/autoarray/inversion/pixelization/mesh/triangulation.py index 9696af07..453f762e 100644 --- a/autoarray/inversion/pixelization/mesh/triangulation.py +++ b/autoarray/inversion/pixelization/mesh/triangulation.py @@ -78,13 +78,13 @@ def mapper_grids_from( relocated_mesh_grid = self.relocated_mesh_grid_from( border_relocator=border_relocator, - source_plane_data_grid=relocated_grid.grid_over_sampled, + source_plane_data_grid=relocated_grid.over_sampled, source_plane_mesh_grid=source_plane_mesh_grid, ) try: source_plane_mesh_grid = self.mesh_grid_from( - source_plane_data_grid=relocated_grid.grid_over_sampled, + source_plane_data_grid=relocated_grid.over_sampled, source_plane_mesh_grid=relocated_mesh_grid, ) except ValueError as e: diff --git a/autoarray/operators/over_sampling/decorator.py b/autoarray/operators/over_sampling/decorator.py index 1e1e5bd8..c028ee31 100644 --- a/autoarray/operators/over_sampling/decorator.py +++ b/autoarray/operators/over_sampling/decorator.py @@ -52,9 +52,9 @@ def wrapper( return func(obj=obj, grid=grid, *args, **kwargs) if obj is not None: - values = func(obj, grid.grid_over_sampled, *args, **kwargs) + values = func(obj, grid.over_sampled, *args, **kwargs) else: - values = func(grid.grid_over_sampled, *args, **kwargs) + values = func(grid.over_sampled, *args, **kwargs) return grid.over_sampler.binned_array_2d_from(array=values) diff --git a/autoarray/plot/get_visuals/two_d.py b/autoarray/plot/get_visuals/two_d.py index 02f8ee6b..c2b99a17 100644 --- a/autoarray/plot/get_visuals/two_d.py +++ b/autoarray/plot/get_visuals/two_d.py @@ -184,12 +184,12 @@ def via_mapper_for_source_from(self, mapper: MapperRectangular) -> Visuals2D: grid = self.get( "grid", - mapper.source_plane_data_grid.grid_over_sampled, + mapper.source_plane_data_grid.over_sampled, "mapper_source_plane_data_grid", ) try: - border_grid = mapper.mapper_grids.source_plane_data_grid.grid_over_sampled[ + border_grid = mapper.mapper_grids.source_plane_data_grid.over_sampled[ mapper.border_relocator.sub_border_slim ] border = self.get("border", border_grid) diff --git a/autoarray/plot/mat_plot/two_d.py b/autoarray/plot/mat_plot/two_d.py index 95f6eb67..24b62e6e 100644 --- a/autoarray/plot/mat_plot/two_d.py +++ b/autoarray/plot/mat_plot/two_d.py @@ -610,7 +610,7 @@ def _plot_rectangular_mapper( visuals_2d.plot_via_plotter( plotter=self, - grid_indexes=mapper.source_plane_data_grid.grid_over_sampled, + grid_indexes=mapper.source_plane_data_grid.over_sampled, mapper=mapper, geometry=mapper.mapper_grids.mask.geometry, ) @@ -694,7 +694,7 @@ def _plot_delaunay_mapper( visuals_2d.plot_via_plotter( plotter=self, - grid_indexes=mapper.source_plane_data_grid.grid_over_sampled, + grid_indexes=mapper.source_plane_data_grid.over_sampled, mapper=mapper, geometry=mapper.mapper_grids.mask.geometry, ) @@ -777,7 +777,7 @@ def _plot_voronoi_mapper( visuals_2d.plot_via_plotter( plotter=self, - grid_indexes=mapper.source_plane_data_grid.grid_over_sampled, + grid_indexes=mapper.source_plane_data_grid.over_sampled, mapper=mapper, geometry=mapper.mapper_grids.mask.geometry, ) diff --git a/autoarray/plot/visuals/two_d.py b/autoarray/plot/visuals/two_d.py index e92b4100..d1da534f 100644 --- a/autoarray/plot/visuals/two_d.py +++ b/autoarray/plot/visuals/two_d.py @@ -100,6 +100,6 @@ def plot_via_plotter(self, plotter, grid_indexes=None, mapper=None, geometry=Non else: plotter.index_scatter.scatter_grid_indexes( - grid=mapper.source_plane_data_grid.grid_over_sampled, + grid=mapper.source_plane_data_grid.over_sampled, indexes=indexes, ) diff --git a/autoarray/structures/decorators/to_grid.py b/autoarray/structures/decorators/to_grid.py index 0ea63190..62d7b870 100644 --- a/autoarray/structures/decorators/to_grid.py +++ b/autoarray/structures/decorators/to_grid.py @@ -23,19 +23,19 @@ def via_grid_2d(self, result) -> Union[Grid2D, List[Grid2D]]: """ if not isinstance(result, list): try: - grid_over_sampled = result.grid_over_sampled + over_sampled = result.over_sampled except AttributeError: - grid_over_sampled = None + over_sampled = None return Grid2D( values=result, mask=self.mask, over_sampling_size=self.over_sampling_size, - grid_over_sampled=grid_over_sampled, + over_sampled=over_sampled, ) try: - grid_over_sampled_list = [res.grid_over_sampled for res in result] + grid_over_sampled_list = [res.over_sampled for res in result] except AttributeError: grid_over_sampled_list = [None] * len(result) @@ -44,9 +44,9 @@ def via_grid_2d(self, result) -> Union[Grid2D, List[Grid2D]]: values=res, mask=self.mask, over_sampling_size=self.over_sampling_size, - grid_over_sampled=grid_over_sampled, + over_sampled=over_sampled, ) - for res, grid_over_sampled in zip(result, grid_over_sampled_list) + for res, over_sampled in zip(result, grid_over_sampled_list) ] def via_grid_2d_irr(self, result) -> Union[Grid2DIrregular, List[Grid2DIrregular]]: diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 36fba67b..523a9645 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -27,7 +27,7 @@ def __init__( mask: Mask2D, store_native: bool = False, over_sampling_size: Union[int, Array2D] = 4, - grid_over_sampled: Optional[Grid2D] = None, + over_sampled: Optional[Grid2D] = None, *args, **kwargs, ): @@ -177,8 +177,8 @@ def __init__( self.over_sampler = OverSampler(sub_size=over_sampling_size, mask=mask) - if grid_over_sampled is None: - self.grid_over_sampled = ( + if over_sampled is None: + self.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, @@ -188,7 +188,7 @@ def __init__( ) else: - self.grid_over_sampled = grid_over_sampled + self.over_sampled = over_sampled @classmethod def no_mask( diff --git a/test_autoarray/inversion/pixelization/mappers/test_factory.py b/test_autoarray/inversion/pixelization/mappers/test_factory.py index e5018c54..83fc4940 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_factory.py +++ b/test_autoarray/inversion/pixelization/mappers/test_factory.py @@ -21,8 +21,8 @@ def test__rectangular_mapper(): # Slightly manipulate input grid so sub gridding is evidence in first source pixel. grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) - grid.grid_over_sampled[0, 0] = -2.0 - grid.grid_over_sampled[0, 1] = 2.0 + grid.over_sampled[0, 0] = -2.0 + grid.over_sampled[0, 1] = 2.0 mesh = aa.mesh.Rectangular(shape=(3, 3)) @@ -72,8 +72,8 @@ def test__delaunay_mapper(): # Slightly manipulate input grid so sub gridding is evidence in first source pixel. grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) - grid.grid_over_sampled[0, 0] = -2.0 - grid.grid_over_sampled[0, 1] = 2.0 + grid.over_sampled[0, 0] = -2.0 + grid.over_sampled[0, 1] = 2.0 mesh = aa.mesh.Delaunay() image_mesh = aa.image_mesh.Overlay(shape=(3, 3)) @@ -128,8 +128,8 @@ def test__voronoi_mapper(): # Slightly manipulate input grid so sub gridding is evidence in first source pixel. grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) - grid.grid_over_sampled[0, 0] = -2.0 - grid.grid_over_sampled[0, 1] = 2.0 + grid.over_sampled[0, 0] = -2.0 + grid.over_sampled[0, 1] = 2.0 mesh = aa.mesh.Voronoi() image_mesh = aa.image_mesh.Overlay(shape=(3, 3)) diff --git a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py index 4901477d..aee46356 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py +++ b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py @@ -22,7 +22,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): ) mesh_grid = aa.Mesh2DRectangular.overlay_grid( - shape_native=(3, 3), grid=grid.grid_over_sampled + shape_native=(3, 3), grid=grid.over_sampled ) mapper_grids = aa.MapperGrids( @@ -34,7 +34,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): pix_indexes_for_sub_slim_index_util = np.array( [ aa.util.geometry.grid_pixel_indexes_2d_slim_from( - grid_scaled_2d_slim=np.array(grid.grid_over_sampled), + grid_scaled_2d_slim=np.array(grid.over_sampled), shape_native=mesh_grid.shape_native, pixel_scales=mesh_grid.pixel_scales, origin=mesh_grid.origin, @@ -49,7 +49,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): def test__pixel_signals_from__matches_util(grid_2d_sub_1_7x7, image_7x7): mesh_grid = aa.Mesh2DRectangular.overlay_grid( - shape_native=(3, 3), grid=grid_2d_sub_1_7x7.grid_over_sampled + shape_native=(3, 3), grid=grid_2d_sub_1_7x7.over_sampled ) mapper_grids = aa.MapperGrids( diff --git a/test_autoarray/inversion/pixelization/test_border_relocator.py b/test_autoarray/inversion/pixelization/test_border_relocator.py index e4861954..6eccee10 100644 --- a/test_autoarray/inversion/pixelization/test_border_relocator.py +++ b/test_autoarray/inversion/pixelization/test_border_relocator.py @@ -328,7 +328,7 @@ def test__relocated_grid_from__inside_border_no_relocations(): grid = aa.Grid2D.from_mask( mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) ) - grid.grid_over_sampled[1, :] = [0.1, 0.1] + grid.over_sampled[1, :] = [0.1, 0.1] border_relocator = aa.BorderRelocator( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) @@ -336,7 +336,7 @@ def test__relocated_grid_from__inside_border_no_relocations(): relocated_grid = border_relocator.relocated_grid_from(grid=grid) - assert (relocated_grid.grid_over_sampled[1] == np.array([0.1, 0.1])).all() + assert (relocated_grid.over_sampled[1] == np.array([0.1, 0.1])).all() def test__relocated_grid_from__outside_border_includes_relocations(): @@ -347,7 +347,7 @@ def test__relocated_grid_from__outside_border_includes_relocations(): grid = aa.Grid2D.from_mask( mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) ) - grid.grid_over_sampled[1, :] = [10.1, 0.1] + grid.over_sampled[1, :] = [10.1, 0.1] border_relocator = aa.BorderRelocator( mask=mask, sub_size=np.array(mask.pixels_in_mask * [2]) @@ -355,7 +355,7 @@ def test__relocated_grid_from__outside_border_includes_relocations(): relocated_grid = border_relocator.relocated_grid_from(grid=grid) - assert relocated_grid.grid_over_sampled[1] == pytest.approx( + assert relocated_grid.over_sampled[1] == pytest.approx( [0.97783243, 0.00968151], 1e-4 ) @@ -369,10 +369,10 @@ def test__relocated_grid_from__positive_origin_included_in_relocate(): ) grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) - grid.grid_over_sampled[1, :] = [11.1, 1.0] + grid.over_sampled[1, :] = [11.1, 1.0] border_relocator = aa.BorderRelocator(mask=mask, sub_size=grid.over_sampling_size) relocated_grid = border_relocator.relocated_grid_from(grid=grid) - assert relocated_grid.grid_over_sampled[1] == pytest.approx([1.97783243, 1.0], 1e-4) + assert relocated_grid.over_sampled[1] == pytest.approx([1.97783243, 1.0], 1e-4) diff --git a/test_autoarray/inversion/plot/test_mapper_plotters.py b/test_autoarray/inversion/plot/test_mapper_plotters.py index 7990a900..00b72457 100644 --- a/test_autoarray/inversion/plot/test_mapper_plotters.py +++ b/test_autoarray/inversion/plot/test_mapper_plotters.py @@ -69,10 +69,10 @@ def test__get_2d__via_mapper_for_source_from(rectangular_mapper_7x7_3x3): assert get_2d.origin.in_list == [(0.0, 0.0)] assert ( get_2d.grid - == rectangular_mapper_7x7_3x3.source_plane_data_grid.grid_over_sampled + == rectangular_mapper_7x7_3x3.source_plane_data_grid.over_sampled ).all() assert (get_2d.mesh_grid == rectangular_mapper_7x7_3x3.source_plane_mesh_grid).all() - border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.grid_over_sampled[ + border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.over_sampled[ rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim ] assert (get_2d.border == border_grid).all() diff --git a/test_autoarray/operators/over_sample/test_decorator.py b/test_autoarray/operators/over_sample/test_decorator.py index 7091ba66..c98de7db 100644 --- a/test_autoarray/operators/over_sample/test_decorator.py +++ b/test_autoarray/operators/over_sample/test_decorator.py @@ -34,7 +34,7 @@ def test__in_grid_2d__over_sample_uniform__out_ndarray_1d(): mask_sub_2 = aa.Mask2D(mask=mask_sub_2, pixel_scales=(0.5, 0.5)) - grid = aa.Grid2D(values=grid_2d.grid_over_sampled, mask=mask_sub_2) + grid = aa.Grid2D(values=grid_2d.over_sampled, mask=mask_sub_2) ndarray_1d_via_grid = obj.ndarray_1d_from(grid=grid) diff --git a/test_autoarray/plot/get_visuals/test_two_d.py b/test_autoarray/plot/get_visuals/test_two_d.py index 51e81838..af576f41 100644 --- a/test_autoarray/plot/get_visuals/test_two_d.py +++ b/test_autoarray/plot/get_visuals/test_two_d.py @@ -108,9 +108,9 @@ def test__via_mapper_for_source_from(rectangular_mapper_7x7_3x3): assert visuals_2d.origin == (1.0, 1.0) assert ( visuals_2d_via.grid - == rectangular_mapper_7x7_3x3.source_plane_data_grid.grid_over_sampled + == rectangular_mapper_7x7_3x3.source_plane_data_grid.over_sampled ).all() - border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.grid_over_sampled[ + border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.over_sampled[ rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim ] assert (visuals_2d_via.border == border_grid).all() From 4f7251e66ecbe27b519a554d877e8a94882fb421 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 16 Dec 2024 15:40:10 +0000 Subject: [PATCH 15/26] simplify API --- autoarray/dataset/grids.py | 6 +- autoarray/dataset/over_sampling.py | 2 +- autoarray/dataset/plot/imaging_plotters.py | 4 +- autoarray/fixtures.py | 4 +- autoarray/inversion/linear_obj/func_list.py | 2 +- .../pixelization/border_relocator.py | 2 +- autoarray/structures/decorators/abstract.py | 4 +- autoarray/structures/decorators/to_grid.py | 4 +- autoarray/structures/grids/uniform_2d.py | 72 +++++++++---------- .../dataset/abstract/test_dataset.py | 6 +- .../imaging/test_inversion_imaging_util.py | 8 +-- .../inversion/inversion/test_abstract.py | 4 +- .../pixelization/mappers/test_abstract.py | 2 +- .../pixelization/mappers/test_delaunay.py | 2 +- .../pixelization/mappers/test_factory.py | 6 +- .../pixelization/mappers/test_rectangular.py | 2 +- .../pixelization/mappers/test_voronoi.py | 2 +- .../pixelization/test_border_relocator.py | 8 +-- test_autoarray/inversion/test_linear_obj.py | 2 +- .../operators/over_sample/test_decorator.py | 2 +- 20 files changed, 72 insertions(+), 72 deletions(-) diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index 9b66d302..95231b70 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -66,7 +66,7 @@ def lp(self) -> Union[Grid1D, Grid2D]: """ return Grid2D.from_mask( mask=self.mask, - over_sampling_size=self.over_sampling.lp, + over_sample_size=self.over_sampling.lp, ) @cached_property @@ -87,7 +87,7 @@ def pixelization(self) -> Grid2D: """ return Grid2D.from_mask( mask=self.mask, - over_sampling_size=self.over_sampling.pixelization, + over_sample_size=self.over_sampling.pixelization, ) @cached_property @@ -117,7 +117,7 @@ def blurring(self) -> Optional[Grid2D]: @cached_property def border_relocator(self) -> BorderRelocator: return BorderRelocator( - mask=self.mask, sub_size=self.pixelization.over_sampling_size + mask=self.mask, sub_size=self.pixelization.over_sample_size ) diff --git a/autoarray/dataset/over_sampling.py b/autoarray/dataset/over_sampling.py index 10c3187a..b50481b8 100644 --- a/autoarray/dataset/over_sampling.py +++ b/autoarray/dataset/over_sampling.py @@ -29,7 +29,7 @@ def __init__( This class controls how over sampling is performed for 3 different types of grids: - `grid`: A grids of (y,x) coordinates which aligns with the centre of every image pixel of the image data. + - `grid`: A grids of (y,x) coordinates which aligns with the centre of every image pixel of the image data. - `grids.pixelization`: A grid of (y,x) coordinates which again align with the centre of every image pixel of the image data. This grid is used specifically for pixelizations computed via the `inversion` module, which diff --git a/autoarray/dataset/plot/imaging_plotters.py b/autoarray/dataset/plot/imaging_plotters.py index a10a2cfc..3f8bac92 100644 --- a/autoarray/dataset/plot/imaging_plotters.py +++ b/autoarray/dataset/plot/imaging_plotters.py @@ -126,7 +126,7 @@ def figures_2d( if over_sampling: self.mat_plot_2d.plot_array( - array=self.dataset.grids.lp.over_sampling_size, + array=self.dataset.grids.lp.over_sample_size, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( title=title_str or f"Over Sampling (Uniform)", @@ -137,7 +137,7 @@ def figures_2d( if over_sampling_pixelization: self.mat_plot_2d.plot_array( - array=self.dataset.grids.pixelization.over_sampling_size, + array=self.dataset.grids.pixelization.over_sample_size, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( title=title_str or f"Over Sampling (Pixelization)", diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index ff684ec4..73d150d4 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -90,11 +90,11 @@ def make_grid_2d_7x7(): def make_grid_2d_sub_1_7x7(): - return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sampling_size=1) + return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sample_size=1) def make_grid_2d_sub_2_7x7(): - return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sampling_size=2) + return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sample_size=2) def make_grid_2d_7x7_simple(): diff --git a/autoarray/inversion/linear_obj/func_list.py b/autoarray/inversion/linear_obj/func_list.py index 9ec30e9b..ce72bdcb 100644 --- a/autoarray/inversion/linear_obj/func_list.py +++ b/autoarray/inversion/linear_obj/func_list.py @@ -96,7 +96,7 @@ def unique_mappings(self) -> UniqueMappings: For a `LinearObjFuncList` every data pixel's group of sub-pixels maps directly to the linear function. """ - sub_size = np.max(self.grid.over_sampling_size) + sub_size = np.max(self.grid.over_sample_size) # TODO : This shape slim is prob unreliable and needs to be divided by sub_size**2 diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index 6870f9a3..28f7a3ff 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -284,7 +284,7 @@ def relocated_grid_from(self, grid: Grid2D) -> Grid2D: return Grid2D( values=values, mask=grid.mask, - over_sampling_size=self.sub_size, + over_sample_size=self.sub_size, over_sampled=over_sampled, ) diff --git a/autoarray/structures/decorators/abstract.py b/autoarray/structures/decorators/abstract.py index 2d6e7f23..5c61c643 100644 --- a/autoarray/structures/decorators/abstract.py +++ b/autoarray/structures/decorators/abstract.py @@ -60,8 +60,8 @@ def mask(self) -> Union[Mask1D, Mask2D]: return self.grid.mask @property - def over_sampling_size(self) -> np.ndarray: - return self.grid.over_sampling_size + def over_sample_size(self) -> np.ndarray: + return self.grid.over_sample_size def via_grid_2d(self, result): raise NotImplementedError diff --git a/autoarray/structures/decorators/to_grid.py b/autoarray/structures/decorators/to_grid.py index 62d7b870..57b13602 100644 --- a/autoarray/structures/decorators/to_grid.py +++ b/autoarray/structures/decorators/to_grid.py @@ -30,7 +30,7 @@ def via_grid_2d(self, result) -> Union[Grid2D, List[Grid2D]]: return Grid2D( values=result, mask=self.mask, - over_sampling_size=self.over_sampling_size, + over_sample_size=self.over_sample_size, over_sampled=over_sampled, ) @@ -43,7 +43,7 @@ def via_grid_2d(self, result) -> Union[Grid2D, List[Grid2D]]: Grid2D( values=res, mask=self.mask, - over_sampling_size=self.over_sampling_size, + over_sample_size=self.over_sample_size, over_sampled=over_sampled, ) for res, over_sampled in zip(result, grid_over_sampled_list) diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 523a9645..226e98e3 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -26,7 +26,7 @@ def __init__( values: Union[np.ndarray, List], mask: Mask2D, store_native: bool = False, - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, over_sampled: Optional[Grid2D] = None, *args, **kwargs, @@ -149,7 +149,7 @@ def __init__( store_native If True, the ndarray is stored in its native format [total_y_pixels, total_x_pixels, 2]. This avoids mapping large data arrays to and from the slim / native formats, which can be a computational bottleneck. - over_sampling_size + over_sample_size The over sampling scheme, which divides the grid into a sub grid of smaller pixels when computing values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls into each pixel. @@ -166,16 +166,16 @@ def __init__( grid_2d_util.check_grid_2d(grid_2d=values) - if isinstance(over_sampling_size, int): - over_sampling_size = np.full( - fill_value=over_sampling_size, shape=mask.shape_slim + if isinstance(over_sample_size, int): + over_sample_size = np.full( + fill_value=over_sample_size, shape=mask.shape_slim ).astype("int") - over_sampling_size = Array2D(values=over_sampling_size, mask=mask) + over_sample_size = Array2D(values=over_sample_size, mask=mask) from autoarray.operators.over_sampling.over_sampler import OverSampler - self.over_sampler = OverSampler(sub_size=over_sampling_size, mask=mask) + self.over_sampler = OverSampler(sub_size=over_sample_size, mask=mask) if over_sampled is None: self.over_sampled = ( @@ -197,7 +197,7 @@ def no_mask( pixel_scales: ty.PixelScales, shape_native: Tuple[int, int] = None, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the grid coordinates in 1D or 2D, automatically @@ -244,7 +244,7 @@ def no_mask( return Grid2D( values=values, mask=mask, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod @@ -255,7 +255,7 @@ def from_yx_1d( shape_native: Tuple[int, int], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the grid coordinates as 1D y and x values. @@ -319,7 +319,7 @@ def from_yx_1d( shape_native=shape_native, pixel_scales=pixel_scales, origin=origin, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod @@ -329,7 +329,7 @@ def from_yx_2d( x: Union[np.ndarray, List], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the grid coordinates as 2D y and x values. @@ -374,7 +374,7 @@ def from_yx_2d( values=np.stack((y, x), axis=-1), pixel_scales=pixel_scales, origin=origin, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod @@ -382,7 +382,7 @@ def from_extent( cls, extent: Tuple[float, float, float, float], shape_native: Tuple[int, int], - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) by inputting the extent of the (y,x) grid coordinates as an input @@ -431,7 +431,7 @@ def from_extent( return Grid2D.no_mask( values=grid_2d, pixel_scales=pixel_scales, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod @@ -440,7 +440,7 @@ def uniform( shape_native: Tuple[int, int], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a `Grid2D` (see *Grid2D.__new__*) as a uniform grid of (y,x) values given an input `shape_native` and @@ -469,7 +469,7 @@ def uniform( shape_native=shape_native, pixel_scales=pixel_scales, origin=origin, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod @@ -478,7 +478,7 @@ def bounding_box( bounding_box: np.ndarray, shape_native: Tuple[int, int], buffer_around_corners: bool = False, - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) from an input bounding box with coordinates [y_min, y_max, x_min, x_max], @@ -521,14 +521,14 @@ def bounding_box( shape_native=shape_native, pixel_scales=pixel_scales, origin=origin, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod def from_mask( cls, mask: Mask2D, - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) from a mask, where only unmasked pixels are included in the grid (if the @@ -551,7 +551,7 @@ def from_mask( return Grid2D( values=grid_1d, mask=mask, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod @@ -560,7 +560,7 @@ def from_fits( file_path: Union[Path, str], pixel_scales: ty.PixelScales, origin: Tuple[float, float] = (0.0, 0.0), - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Create a Grid2D (see *Grid2D.__new__*) from a mask, where only unmasked pixels are included in the grid (if the @@ -580,7 +580,7 @@ def from_fits( values=grid_2d, pixel_scales=pixel_scales, origin=origin, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) @classmethod @@ -588,7 +588,7 @@ def blurring_grid_from( cls, mask: Mask2D, kernel_shape_native: Tuple[int, int], - over_sampling_size: Union[int, Array2D] = 4, + over_sample_size: Union[int, Array2D] = 4, ) -> "Grid2D": """ Setup a blurring-grid from a mask, where a blurring grid consists of all pixels that are masked (and @@ -676,7 +676,7 @@ def blurring_grid_from( return cls.from_mask( mask=blurring_mask, - over_sampling_size=over_sampling_size, + over_sample_size=over_sample_size, ) def subtracted_from(self, offset: Tuple[(float, float), np.ndarray]) -> "Grid2D": @@ -692,11 +692,11 @@ def subtracted_from(self, offset: Tuple[(float, float), np.ndarray]) -> "Grid2D" return Grid2D( values=self - np.array(offset), mask=mask, - over_sampling_size=self.over_sampling_size, + over_sample_size=self.over_sample_size, ) @property - def over_sampling_size(self): + def over_sample_size(self): return self.over_sampler.sub_size @property @@ -711,7 +711,7 @@ def slim(self) -> "Grid2D": return Grid2D( values=self, mask=self.mask, - over_sampling_size=self.over_sampling_size, + over_sample_size=self.over_sample_size, ) @property @@ -728,7 +728,7 @@ def native(self) -> "Grid2D": return Grid2D( values=self, mask=self.mask, - over_sampling_size=self.over_sampling_size, + over_sample_size=self.over_sample_size, store_native=True, ) @@ -766,7 +766,7 @@ def grid_2d_via_deflection_grid_from(self, deflection_grid: "Grid2D") -> "Grid2D return Grid2D( values=self - deflection_grid, mask=self.mask, - over_sampling_size=self.over_sampling_size, + over_sample_size=self.over_sample_size, ) def blurring_grid_via_kernel_shape_from( @@ -786,7 +786,7 @@ def blurring_grid_via_kernel_shape_from( return Grid2D.blurring_grid_from( mask=self.mask, kernel_shape_native=kernel_shape_native, - over_sampling_size=1, + over_sample_size=1, ) def grid_with_coordinates_within_distance_removed_from( @@ -823,7 +823,7 @@ def grid_with_coordinates_within_distance_removed_from( return Grid2D.from_mask( mask=mask, - over_sampling_size=self.over_sampling_size.apply_mask(mask=mask), + over_sample_size=self.over_sample_size.apply_mask(mask=mask), ) def squared_distances_to_coordinate_from( @@ -1080,16 +1080,16 @@ def padded_grid_from(self, kernel_shape_native: Tuple[int, int]) -> "Grid2D": (padded_shape[1] - shape[1]) // 2, ) - over_sampling_size = np.pad( - self.over_sampling_size.native, + over_sample_size = np.pad( + self.over_sample_size.native, pad_width, mode="constant", constant_values=1, ) - over_sampling_size[over_sampling_size == 0] = 1 + over_sample_size[over_sample_size == 0] = 1 - return Grid2D.from_mask(mask=padded_mask, over_sampling_size=over_sampling_size) + return Grid2D.from_mask(mask=padded_mask, over_sample_size=over_sample_size) @cached_property def is_uniform(self) -> bool: diff --git a/test_autoarray/dataset/abstract/test_dataset.py b/test_autoarray/dataset/abstract/test_dataset.py index b72af8ab..a1dc7573 100644 --- a/test_autoarray/dataset/abstract/test_dataset.py +++ b/test_autoarray/dataset/abstract/test_dataset.py @@ -101,7 +101,7 @@ def test__grids_pixelization__uses_mask_and_settings( ) assert isinstance(masked_imaging_7x7.grids.pixelization, aa.Grid2D) - assert masked_imaging_7x7.grids.pixelization.over_sampling_size[0] == 4 + assert masked_imaging_7x7.grids.pixelization.over_sample_size[0] == 4 def test__grid_settings__sub_size(image_7x7, noise_map_7x7): @@ -114,8 +114,8 @@ def test__grid_settings__sub_size(image_7x7, noise_map_7x7): ), ) - assert dataset_7x7.grids.lp.over_sampling_size[0] == 2 - assert dataset_7x7.grids.pixelization.over_sampling_size[0] == 4 + assert dataset_7x7.grids.lp.over_sample_size[0] == 2 + assert dataset_7x7.grids.pixelization.over_sample_size[0] == 4 def test__new_imaging_with_arrays_trimmed_via_kernel_shape(): diff --git a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py index 957c68c3..711135eb 100644 --- a/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py +++ b/test_autoarray/inversion/inversion/imaging/test_inversion_imaging_util.py @@ -188,7 +188,7 @@ def test__data_vector_via_w_tilde_data_two_methods_agree(): # TODO : Use pytest.parameterize for sub_size in range(1, 3): - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=sub_size) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=sub_size) mapper_grids = pixelization.mapper_grids_from( mask=mask, @@ -232,7 +232,7 @@ def test__data_vector_via_w_tilde_data_two_methods_agree(): pix_sizes_for_sub_slim_index=mapper.pix_sizes_for_sub_slim_index, pix_weights_for_sub_slim_index=mapper.pix_weights_for_sub_slim_index, pix_pixels=mapper.params, - sub_size=np.array(grid.over_sampling_size), + sub_size=np.array(grid.over_sample_size), ) data_vector_via_w_tilde = ( @@ -308,7 +308,7 @@ def test__curvature_matrix_via_w_tilde_preload_two_methods_agree(): pixelization = aa.mesh.Rectangular(shape=(20, 20)) for sub_size in range(1, 2, 3): - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=sub_size) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=sub_size) mapper_grids = pixelization.mapper_grids_from( mask=mask, @@ -343,7 +343,7 @@ def test__curvature_matrix_via_w_tilde_preload_two_methods_agree(): pix_sizes_for_sub_slim_index=mapper.pix_sizes_for_sub_slim_index, pix_weights_for_sub_slim_index=mapper.pix_weights_for_sub_slim_index, pix_pixels=mapper.params, - sub_size=np.array(grid.over_sampling_size), + sub_size=np.array(grid.over_sample_size), ) curvature_matrix_via_w_tilde = aa.util.inversion_imaging.curvature_matrix_via_w_tilde_curvature_preload_imaging_from( diff --git a/test_autoarray/inversion/inversion/test_abstract.py b/test_autoarray/inversion/inversion/test_abstract.py index 4bb153b7..735b3cda 100644 --- a/test_autoarray/inversion/inversion/test_abstract.py +++ b/test_autoarray/inversion/inversion/test_abstract.py @@ -114,7 +114,7 @@ def test__curvature_matrix__via_w_tilde__identical_to_mapping(): pixel_scales=2.0, ) - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=1) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=1) mesh_0 = aa.mesh.Rectangular(shape=(3, 3)) mesh_1 = aa.mesh.Rectangular(shape=(4, 4)) @@ -178,7 +178,7 @@ def test__curvature_matrix_via_w_tilde__includes_source_interpolation__identical pixel_scales=2.0, ) - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=1) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=1) mesh_0 = aa.mesh.Delaunay() mesh_1 = aa.mesh.Delaunay() diff --git a/test_autoarray/inversion/pixelization/mappers/test_abstract.py b/test_autoarray/inversion/pixelization/mappers/test_abstract.py index 8e154b21..7217dc79 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_abstract.py +++ b/test_autoarray/inversion/pixelization/mappers/test_abstract.py @@ -202,7 +202,7 @@ def test__mapped_to_source_from(grid_2d_7x7): values=[[0.1, 0.1], [1.1, 0.6], [2.1, 0.1], [0.4, 1.1], [1.1, 7.1], [2.1, 1.1]], shape_native=(3, 2), pixel_scales=1.0, - over_sampling_size=1, + over_sample_size=1, ) mesh_grid = aa.Mesh2DDelaunay(values=mesh_grid) diff --git a/test_autoarray/inversion/pixelization/mappers/test_delaunay.py b/test_autoarray/inversion/pixelization/mappers/test_delaunay.py index dd8a654f..33b03fbb 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_delaunay.py +++ b/test_autoarray/inversion/pixelization/mappers/test_delaunay.py @@ -7,7 +7,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_sub_1_7x7): values=[[0.1, 0.1], [1.1, 0.6], [2.1, 0.1], [0.4, 1.1], [1.1, 7.1], [2.1, 1.1]], shape_native=(3, 2), pixel_scales=1.0, - over_sampling_size=1, + over_sample_size=1, ) mesh_grid = aa.Mesh2DDelaunay(values=mesh_grid) diff --git a/test_autoarray/inversion/pixelization/mappers/test_factory.py b/test_autoarray/inversion/pixelization/mappers/test_factory.py index 83fc4940..c08bca93 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_factory.py +++ b/test_autoarray/inversion/pixelization/mappers/test_factory.py @@ -20,7 +20,7 @@ def test__rectangular_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=2) grid.over_sampled[0, 0] = -2.0 grid.over_sampled[0, 1] = 2.0 @@ -70,7 +70,7 @@ def test__delaunay_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=2) grid.over_sampled[0, 0] = -2.0 grid.over_sampled[0, 1] = 2.0 @@ -126,7 +126,7 @@ def test__voronoi_mapper(): ) # Slightly manipulate input grid so sub gridding is evidence in first source pixel. - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=2) grid.over_sampled[0, 0] = -2.0 grid.over_sampled[0, 1] = 2.0 diff --git a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py index aee46356..026e230d 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_rectangular.py +++ b/test_autoarray/inversion/pixelization/mappers/test_rectangular.py @@ -18,7 +18,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(): ], pixel_scales=1.0, shape_native=(3, 3), - over_sampling_size=1, + over_sample_size=1, ) mesh_grid = aa.Mesh2DRectangular.overlay_grid( diff --git a/test_autoarray/inversion/pixelization/mappers/test_voronoi.py b/test_autoarray/inversion/pixelization/mappers/test_voronoi.py index 8b54842e..57e971aa 100644 --- a/test_autoarray/inversion/pixelization/mappers/test_voronoi.py +++ b/test_autoarray/inversion/pixelization/mappers/test_voronoi.py @@ -8,7 +8,7 @@ def test__pix_indexes_for_sub_slim_index__matches_util(grid_2d_sub_1_7x7): values=[[0.1, 0.1], [1.1, 0.6], [2.1, 0.1], [0.4, 1.1], [1.1, 7.1], [2.1, 1.1]], shape_native=(3, 2), pixel_scales=1.0, - over_sampling_size=1, + over_sample_size=1, ) source_plane_mesh_grid = aa.Mesh2DVoronoi( diff --git a/test_autoarray/inversion/pixelization/test_border_relocator.py b/test_autoarray/inversion/pixelization/test_border_relocator.py index 6eccee10..db190269 100644 --- a/test_autoarray/inversion/pixelization/test_border_relocator.py +++ b/test_autoarray/inversion/pixelization/test_border_relocator.py @@ -326,7 +326,7 @@ def test__relocated_grid_from__inside_border_no_relocations(): ) grid = aa.Grid2D.from_mask( - mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) + mask=mask, over_sample_size=np.array(mask.pixels_in_mask * [2]) ) grid.over_sampled[1, :] = [0.1, 0.1] @@ -345,7 +345,7 @@ def test__relocated_grid_from__outside_border_includes_relocations(): ) grid = aa.Grid2D.from_mask( - mask=mask, over_sampling_size=np.array(mask.pixels_in_mask * [2]) + mask=mask, over_sample_size=np.array(mask.pixels_in_mask * [2]) ) grid.over_sampled[1, :] = [10.1, 0.1] @@ -368,10 +368,10 @@ def test__relocated_grid_from__positive_origin_included_in_relocate(): centre=(1.0, 1.0), ) - grid = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + grid = aa.Grid2D.from_mask(mask=mask, over_sample_size=2) grid.over_sampled[1, :] = [11.1, 1.0] - border_relocator = aa.BorderRelocator(mask=mask, sub_size=grid.over_sampling_size) + border_relocator = aa.BorderRelocator(mask=mask, sub_size=grid.over_sample_size) relocated_grid = border_relocator.relocated_grid_from(grid=grid) diff --git a/test_autoarray/inversion/test_linear_obj.py b/test_autoarray/inversion/test_linear_obj.py index 1319742a..5267cd46 100644 --- a/test_autoarray/inversion/test_linear_obj.py +++ b/test_autoarray/inversion/test_linear_obj.py @@ -45,7 +45,7 @@ def test__data_to_pix_unique_from(): mask = aa.Mask2D.all_false(shape_native=(1, 2), pixel_scales=0.1) grid = aa.Grid2D.uniform( - shape_native=(1, 2), pixel_scales=0.1, over_sampling_size=2 + shape_native=(1, 2), pixel_scales=0.1, over_sample_size=2 ) linear_obj = aa.AbstractLinearObjFuncList(grid=grid, regularization=None) diff --git a/test_autoarray/operators/over_sample/test_decorator.py b/test_autoarray/operators/over_sample/test_decorator.py index c98de7db..47dae1e8 100644 --- a/test_autoarray/operators/over_sample/test_decorator.py +++ b/test_autoarray/operators/over_sample/test_decorator.py @@ -20,7 +20,7 @@ def test__in_grid_2d__over_sample_uniform__out_ndarray_1d(): pixel_scales=(1.0, 1.0), ) - grid_2d = aa.Grid2D.from_mask(mask=mask, over_sampling_size=2) + grid_2d = aa.Grid2D.from_mask(mask=mask, over_sample_size=2) obj = aa.m.MockGrid2DLikeObj() From 9aff29b434f9d7f36d15651337302dbf105c7f30 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 16 Dec 2024 16:03:40 +0000 Subject: [PATCH 16/26] noise scaling hacks --- autoarray/dataset/imaging/dataset.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 2c3e50b8..9d09b1ce 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -346,6 +346,8 @@ def apply_noise_scaling( noise_value: float = 1e8, signal_to_noise_value: Optional[float] = None, should_zero_data: bool = True, + data_noise_mean : Optional[float] = None, + data_noise_sigma : Optional[float] = None ) -> "Imaging": """ Apply a mask to the imaging dataset using noise scaling, whereby the maskmay zero the data and increase @@ -394,8 +396,14 @@ def apply_noise_scaling( self.noise_map.native, ) - if should_zero_data: + if should_zero_data and data_noise_sigma is None: data = np.where(np.invert(mask), 0.0, self.data.native) + elif data_noise_sigma is not None: + random_noise = np.random.normal( + loc=data_noise_mean, scale=data_noise_sigma, size=data.shape_native + ) + data = np.where(mask_2d, random_noise, data.native) + else: data = self.data.native From 6f01e62a9a447fa6da6689baf68df170742b7b82 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 16 Dec 2024 16:10:33 +0000 Subject: [PATCH 17/26] fix --- autoarray/dataset/imaging/dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 9d09b1ce..81b3f886 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -400,7 +400,7 @@ def apply_noise_scaling( data = np.where(np.invert(mask), 0.0, self.data.native) elif data_noise_sigma is not None: random_noise = np.random.normal( - loc=data_noise_mean, scale=data_noise_sigma, size=data.shape_native + loc=data_noise_mean, scale=data_noise_sigma, size=self.data.shape_native ) data = np.where(mask_2d, random_noise, data.native) From 3806cb7f6d42f7f041f5c236aa98928e5f0acdf3 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 16 Dec 2024 16:12:50 +0000 Subject: [PATCH 18/26] undo changes --- autoarray/dataset/imaging/dataset.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 81b3f886..2c3e50b8 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -346,8 +346,6 @@ def apply_noise_scaling( noise_value: float = 1e8, signal_to_noise_value: Optional[float] = None, should_zero_data: bool = True, - data_noise_mean : Optional[float] = None, - data_noise_sigma : Optional[float] = None ) -> "Imaging": """ Apply a mask to the imaging dataset using noise scaling, whereby the maskmay zero the data and increase @@ -396,14 +394,8 @@ def apply_noise_scaling( self.noise_map.native, ) - if should_zero_data and data_noise_sigma is None: + if should_zero_data: data = np.where(np.invert(mask), 0.0, self.data.native) - elif data_noise_sigma is not None: - random_noise = np.random.normal( - loc=data_noise_mean, scale=data_noise_sigma, size=self.data.shape_native - ) - data = np.where(mask_2d, random_noise, data.native) - else: data = self.data.native From ad359384738635176da87d1a7618e0efdcdccd4e Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 18 Dec 2024 09:47:38 +0000 Subject: [PATCH 19/26] remove Grid2DIRregularUniform --- autoarray/__init__.py | 1 - autoarray/structures/grids/irregular_2d.py | 151 +----------------- .../coordinate_array/jax_coordinate_array.py | 1 - .../structures/grids/test_irregular_2d.py | 85 ---------- 4 files changed, 1 insertion(+), 237 deletions(-) diff --git a/autoarray/__init__.py b/autoarray/__init__.py index 4e3237c7..0e102360 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -71,7 +71,6 @@ from .structures.grids.uniform_2d import Grid2D from .operators.over_sampling.over_sampler import OverSampler from .structures.grids.irregular_2d import Grid2DIrregular -from .structures.grids.irregular_2d import Grid2DIrregularUniform from .structures.mesh.rectangular_2d import Mesh2DRectangular from .structures.mesh.voronoi_2d import Mesh2DVoronoi from .structures.mesh.delaunay_2d import Mesh2DDelaunay diff --git a/autoarray/structures/grids/irregular_2d.py b/autoarray/structures/grids/irregular_2d.py index a2aa50bf..54cb483c 100644 --- a/autoarray/structures/grids/irregular_2d.py +++ b/autoarray/structures/grids/irregular_2d.py @@ -269,153 +269,4 @@ def grid_of_closest_from(self, grid_pair: "Grid2DIrregular") -> "Grid2DIrregular grid_of_closest[i, :] = self[np.argmin(radial_distances), :] - return Grid2DIrregular(values=grid_of_closest) - - -class Grid2DIrregularUniform(Grid2DIrregular): - def __init__( - self, - values: np.ndarray, - shape_native: Optional[Tuple[float, float]] = None, - pixel_scales: Optional[Tuple[float, float]] = None, - ): - """ - A collection of (y,x) coordinates which is structured as follows: - - :: - - [[x0, x1], [x0, x1]] - - The grid object does not store the coordinates as a list of tuples, but instead a 2D NumPy array of - shape [total_coordinates, 2]. They are stored as a NumPy array so the coordinates can be used efficiently for - calculations. - - The coordinates input to this function can have any of the following forms: - - :: - - [(y0,x0), (y1,x1)] - - In all cases, they will be converted to a list of tuples followed by a 2D NumPy array. - - Print methods are overidden so a user always "sees" the coordinates as the list structure. - - Like the `Grid2D` structure, `Grid2DIrregularUniform` lie on a uniform grid corresponding to values that - originate from a uniform grid. This contrasts the `Grid2DIrregular` class above. However, although this class - stores the pixel-scale and 2D shape of this grid, it does not store the mask that a `Grid2D` does that enables - the coordinates to be mapped from 1D to 2D. This is for calculations that utilize the 2d information of the - grid but do not want the memory overheads associated with the 2D mask. - - Parameters - ---------- - values - A collection of (y,x) coordinates that. - """ - - if len(values) == 0: - super().__init__(values=values) - return - - if isinstance(values[0], float): - values = [values] - - if isinstance(values[0], tuple): - values = [values] - elif isinstance(values[0], (np.ndarray, AbstractNDArray)): - if len(values[0].shape) == 1: - values = [values] - elif isinstance(values[0], list) and isinstance(values[0][0], (float)): - values = [values] - - coordinates_arr = np.concatenate([np.array(i) for i in values]) - - self._internal_list = values - - pixel_scales = geometry_util.convert_pixel_scales_2d(pixel_scales=pixel_scales) - - self.shape_native = shape_native - self.pixel_scales = pixel_scales - - super().__init__(coordinates_arr) - - def __array_finalize__(self, obj): - if hasattr(obj, "_internal_list"): - self._internal_list = obj._internal_list - - if hasattr(obj, "shape_native"): - self.shape_native = obj.shape_native - - if hasattr(obj, "pixel_scales"): - self.pixel_scales = obj.pixel_scales - - @property - def pixel_scale(self) -> float: - if self.pixel_scales[0] == self.pixel_scales[1]: - return self.pixel_scales[0] - else: - logger.warning( - f""" - The `Grid2DIrregular` has pixel scales of {self.pixel_scales}, which are not the same in both - dimensions. This means that the pixel scale of the grid is not a single value and may cause - issues with calculations that assume a uniform pixel scale. - """ - ) - - @classmethod - def from_grid_sparse_uniform_upscale( - cls, - grid_sparse_uniform: np.ndarray, - upscale_factor: int, - pixel_scales, - shape_native=None, - ) -> "Grid2DIrregularUniform": - pixel_scales = geometry_util.convert_pixel_scales_2d(pixel_scales=pixel_scales) - - grid_upscaled_1d = grid_2d_util.grid_2d_slim_upscaled_from( - grid_slim=np.array(grid_sparse_uniform), - upscale_factor=upscale_factor, - pixel_scales=pixel_scales, - ) - - pixel_scales = ( - pixel_scales[0] / upscale_factor, - pixel_scales[1] / upscale_factor, - ) - - return Grid2DIrregularUniform( - values=grid_upscaled_1d, - pixel_scales=pixel_scales, - shape_native=shape_native, - ) - - def grid_from(self, grid_slim: np.ndarray) -> "Grid2DIrregularUniform": - """ - Create a `Grid2DIrregularUniform` object from a 2D NumPy array of values of shape [total_coordinates, 2]. The - `Grid2DIrregularUniform` are structured following this *GridIrregular2D* instance. - """ - return Grid2DIrregularUniform( - values=grid_slim, - pixel_scales=self.pixel_scales, - shape_native=self.shape_native, - ) - - def grid_2d_via_deflection_grid_from( - self, deflection_grid: np.ndarray - ) -> "Grid2DIrregularUniform": - """ - Returns a new Grid2DIrregular from this grid coordinates, where the (y,x) coordinates of this grid have a - grid of (y,x) values, termed the deflection grid, subtracted from them to determine the new grid of (y,x) - values. - - This is used by PyAutoLens to perform grid ray-tracing. - - Parameters - ---------- - deflection_grid - The grid of (y,x) coordinates which is subtracted from this grid. - """ - return Grid2DIrregularUniform( - values=self - deflection_grid, - pixel_scales=self.pixel_scales, - shape_native=self.shape_native, - ) + return Grid2DIrregular(values=grid_of_closest) \ No newline at end of file diff --git a/autoarray/structures/triangles/coordinate_array/jax_coordinate_array.py b/autoarray/structures/triangles/coordinate_array/jax_coordinate_array.py index 74b2cdf2..e80facd1 100644 --- a/autoarray/structures/triangles/coordinate_array/jax_coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array/jax_coordinate_array.py @@ -1,5 +1,4 @@ from jax import numpy as np - import jax from autoarray.structures.triangles.abstract import HEIGHT_FACTOR diff --git a/test_autoarray/structures/grids/test_irregular_2d.py b/test_autoarray/structures/grids/test_irregular_2d.py index 8039624c..24584822 100644 --- a/test_autoarray/structures/grids/test_irregular_2d.py +++ b/test_autoarray/structures/grids/test_irregular_2d.py @@ -112,88 +112,3 @@ def test__grid_of_closest_from(): assert ( grid_of_closest == np.array([[0.0, 0.0], [0.0, 0.0], [0.0, 1.0], [0.0, 0.0]]) ).all() - - -def test__uniform__from_grid_sparse_uniform_upscale(): - grid_sparse_uniform = aa.Grid2DIrregularUniform( - values=[[(1.0, 1.0), (1.0, 3.0)]], pixel_scales=2.0 - ) - - grid_upscale = aa.Grid2DIrregularUniform.from_grid_sparse_uniform_upscale( - grid_sparse_uniform=grid_sparse_uniform, upscale_factor=2, pixel_scales=2.0 - ) - - assert ( - grid_upscale - == 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_sparse_uniform = aa.Grid2DIrregularUniform( - values=[[(1.0, 1.0), (1.0, 3.0), (1.0, 5.0), (3.0, 3.0)]], pixel_scales=2.0 - ) - - grid_upscale = aa.Grid2DIrregularUniform.from_grid_sparse_uniform_upscale( - grid_sparse_uniform=grid_sparse_uniform, upscale_factor=4, pixel_scales=2.0 - ) - - grid_upscale_util = aa.util.grid_2d.grid_2d_slim_upscaled_from( - grid_slim=np.array(grid_sparse_uniform), - upscale_factor=4, - pixel_scales=(2.0, 2.0), - ) - - assert (grid_upscale == grid_upscale_util).all() - - -def test__uniform__grid_2d_via_deflection_grid_from(): - grid = aa.Grid2DIrregularUniform( - values=[(1.0, 1.0), (2.0, 2.0)], pixel_scales=(1.0, 1.0), shape_native=(3, 3) - ) - - grid = grid.grid_2d_via_deflection_grid_from( - deflection_grid=np.array([[1.0, 0.0], [1.0, 1.0]]) - ) - - assert type(grid) == aa.Grid2DIrregularUniform - assert grid.in_list == [(0.0, 1.0), (1.0, 1.0)] - assert grid.pixel_scales == (1.0, 1.0) - assert grid.shape_native == (3, 3) - - -def test__uniform__grid_from(): - grid = aa.Grid2DIrregularUniform( - values=[(1.0, 1.0), (2.0, 2.0)], pixel_scales=(1.0, 1.0), shape_native=(3, 3) - ) - - grid_from_1d = grid.grid_from(grid_slim=np.array([[1.0, 1.0], [2.0, 2.0]])) - - assert type(grid_from_1d) == aa.Grid2DIrregularUniform - assert grid_from_1d.in_list == [(1.0, 1.0), (2.0, 2.0)] - assert grid.pixel_scales == (1.0, 1.0) - assert grid.shape_native == (3, 3) - - grid = aa.Grid2DIrregularUniform( - values=[(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)], - pixel_scales=(1.0, 3.0), - shape_native=(5, 5), - ) - - grid_from_1d = grid.grid_from( - grid_slim=np.array([[1.0, 1.0], [2.0, 2.0], [3.0, 3.0]]) - ) - - assert type(grid_from_1d) == aa.Grid2DIrregularUniform - assert grid_from_1d.in_list == [(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)] - assert grid.pixel_scales == (1.0, 3.0) - assert grid.shape_native == (5, 5) From 62b688f7825f82b5b56feec98de7570332532c58 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Thu, 19 Dec 2024 11:05:11 +0000 Subject: [PATCH 20/26] remove point Hilbert funciton --- autoarray/dataset/over_sampling.py | 4 +- .../pixelization/image_mesh/hilbert.py | 37 ------------------- autoarray/plot/mat_plot/two_d.py | 2 +- autoarray/structures/grids/uniform_2d.py | 8 +++- 4 files changed, 9 insertions(+), 42 deletions(-) diff --git a/autoarray/dataset/over_sampling.py b/autoarray/dataset/over_sampling.py index b50481b8..c19938e3 100644 --- a/autoarray/dataset/over_sampling.py +++ b/autoarray/dataset/over_sampling.py @@ -41,8 +41,8 @@ def __init__( Parameters ---------- lp - The over sampling scheme, which divides the grid into a sub grid of smaller pixels when computing values - (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls into each pixel. pixelization How over sampling is performed for the grid which is associated with a pixelization, which is therefore diff --git a/autoarray/inversion/pixelization/image_mesh/hilbert.py b/autoarray/inversion/pixelization/image_mesh/hilbert.py index 3a4185c8..bde49849 100644 --- a/autoarray/inversion/pixelization/image_mesh/hilbert.py +++ b/autoarray/inversion/pixelization/image_mesh/hilbert.py @@ -86,43 +86,6 @@ def generate2d(x, y, ax, ay, bx, by): -(ay - ay2), ) - -def super_resolution_grid_from(img_2d, mask, mask_radius, pixel_scales, sub_scale=11): - """ - This function will create a higher resolution grid for the img_2d. The new grid and its - interpolated values will be used to generate a sparse image grid. - - img_2d: the hyper image in 2d (e.g. hyper_source_model_image.native) - mask: the mask used for the fitting. - mask_radius: the circular mask radius. Currently, the code only works with a circular mask. - sub_scale: oversampling scale for each image pixel. - """ - - shape_nnn = np.shape(mask)[0] - - grid = Grid2D.uniform( - shape_native=(shape_nnn, shape_nnn), - pixel_scales=pixel_scales, - ) - - new_mask = Mask2D.circular( - shape_native=(shape_nnn, shape_nnn), - pixel_scales=pixel_scales, - centre=mask.origin, - radius=mask_radius, - ) - - over_sampler = OverSampler(mask=new_mask, sub_size=sub_scale) - - new_grid = over_sampler.uniform_over_sampled - - new_img = griddata( - points=grid, values=img_2d.ravel(), xi=new_grid, fill_value=0.0, method="linear" - ) - - return new_img, new_grid - - def grid_hilbert_order_from(length, mask_radius): """ This function will create a grid in the Hilbert space-filling curve order. diff --git a/autoarray/plot/mat_plot/two_d.py b/autoarray/plot/mat_plot/two_d.py index 24b62e6e..9fcec365 100644 --- a/autoarray/plot/mat_plot/two_d.py +++ b/autoarray/plot/mat_plot/two_d.py @@ -421,7 +421,7 @@ def plot_grid( ax = self.setup_subplot() if plot_over_sampled_grid: - grid_plot = grid.over_sampler.uniform_over_sampled + grid_plot = grid.over_sampled else: grid_plot = grid diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 226e98e3..4f0d1d39 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -150,9 +150,13 @@ def __init__( If True, the ndarray is stored in its native format [total_y_pixels, total_x_pixels, 2]. This avoids mapping large data arrays to and from the slim / native formats, which can be a computational bottleneck. over_sample_size - The over sampling scheme, which divides the grid into a sub grid of smaller pixels when computing values - (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls into each pixel. + over_sampled + The over sampled grid of (y,x) coordinates, which can be passed in manually because if the grid is + not uniform (e.g. due to gravitational lensing) is cannot be computed internally in this function. If the + over sampled grid is not passed in it is computed assuming uniformity. """ values = grid_2d_util.convert_grid_2d( grid_2d=values, From 78bf573c83c533c6bdd56f6c85ddddf05de8a622 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Thu, 19 Dec 2024 14:16:02 +0000 Subject: [PATCH 21/26] added apply_over_sampling to Grid2D --- autoarray/inversion/plot/mapper_plotters.py | 2 +- .../operators/over_sampling/over_sampler.py | 13 +++++++++ autoarray/plot/wrap/two_d/grid_plot.py | 8 +++-- autoarray/structures/grids/uniform_2d.py | 29 +++++++++++++++++++ .../structures/grids/test_uniform_2d.py | 13 +++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/autoarray/inversion/plot/mapper_plotters.py b/autoarray/inversion/plot/mapper_plotters.py index 7234e8da..9fd6608d 100644 --- a/autoarray/inversion/plot/mapper_plotters.py +++ b/autoarray/inversion/plot/mapper_plotters.py @@ -80,7 +80,7 @@ def figure_2d( interpolate_to_uniform=interpolate_to_uniform, pixel_values=solution_vector, auto_labels=AutoLabels( - title="Pixelization Mesh (Image-Plane)", filename="mapper" + title="Pixelization Mesh (Source-Plane)", filename="mapper" ), ) diff --git a/autoarray/operators/over_sampling/over_sampler.py b/autoarray/operators/over_sampling/over_sampler.py index 19730fe3..1bdb1740 100644 --- a/autoarray/operators/over_sampling/over_sampler.py +++ b/autoarray/operators/over_sampling/over_sampler.py @@ -346,6 +346,19 @@ def slim_for_sub_slim(self) -> np.ndarray: @property def uniform_over_sampled(self): + """ + 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. + """ from autoarray.structures.grids.irregular_2d import Grid2DIrregular grid = over_sample_util.grid_2d_slim_over_sampled_via_mask_from( diff --git a/autoarray/plot/wrap/two_d/grid_plot.py b/autoarray/plot/wrap/two_d/grid_plot.py index 123f3194..a727bec3 100644 --- a/autoarray/plot/wrap/two_d/grid_plot.py +++ b/autoarray/plot/wrap/two_d/grid_plot.py @@ -47,11 +47,15 @@ def plot_rectangular_grid_lines( ys = np.linspace(extent[2], extent[3], shape_native[1] + 1) xs = np.linspace(extent[0], extent[1], shape_native[0] + 1) + config_dict = self.config_dict + config_dict.pop("c") + config_dict["c"] = "k" + # grid lines for x in xs: - plt.plot([x, x], [ys[0], ys[-1]], **self.config_dict) + plt.plot([x, x], [ys[0], ys[-1]], **config_dict) for y in ys: - plt.plot([xs[0], xs[-1]], [y, y], **self.config_dict) + plt.plot([xs[0], xs[-1]], [y, y], **config_dict) def plot_grid(self, grid: Union[np.ndarray, Grid2D]): """ diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 4f0d1d39..604e561a 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -1117,3 +1117,32 @@ def is_uniform(self) -> bool: return False return True + + def apply_over_sampling( + self, + over_sample_size : Union[int, np.ndarray] + ) -> "AbstractDataset": + """ + Apply new over sampling to the grid. + + This method is used to change the over sampling of the grid, for example when the user wishes to perform + over sampling with a higher sub grid size. + + Parameters + ---------- + over_sample_size + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls + into each pixel. + """ + if not self.is_uniform: + raise exc.GridException( + """ + Cannot apply over sampling to a Grid2D which is not uniform. + """) + + return Grid2D( + values=self, + mask=self.mask, + over_sample_size=over_sample_size, + ) \ No newline at end of file diff --git a/test_autoarray/structures/grids/test_uniform_2d.py b/test_autoarray/structures/grids/test_uniform_2d.py index a7e8e904..0fc83da4 100644 --- a/test_autoarray/structures/grids/test_uniform_2d.py +++ b/test_autoarray/structures/grids/test_uniform_2d.py @@ -840,3 +840,16 @@ def test__is_uniform(): ) assert grid_2d.is_uniform == False + + +def test__apply_over_sampling(): + + grid = aa.Grid2D.uniform( + shape_native=(2, 2), + pixel_scales=1.0, + over_sample_size=1 + ) + + grid = grid.apply_over_sampling(over_sample_size=2) + + assert grid.over_sampled.shape[0] == 16 From be571b5a3b849ba545fd19aadf89fed2a4765dfc Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 20 Dec 2024 11:21:02 +0000 Subject: [PATCH 22/26] remove OverSamplingDataset --- autoarray/__init__.py | 1 - autoarray/dataset/abstract/dataset.py | 63 ++++++++++++--- autoarray/dataset/grids.py | 25 ++++-- autoarray/dataset/imaging/dataset.py | 77 +++++++++++-------- autoarray/dataset/imaging/simulator.py | 12 ++- autoarray/dataset/interferometer/dataset.py | 15 ++-- autoarray/dataset/over_sampling.py | 52 ------------- autoarray/dataset/plot/imaging_plotters.py | 28 +++---- autoarray/fixtures.py | 10 +-- .../pixelization/border_relocator.py | 9 +-- .../pixelization/image_mesh/hilbert.py | 1 + .../pixelization/mappers/delaunay.py | 8 +- .../over_sampling/over_sample_util.py | 39 +++++++++- .../operators/over_sampling/over_sampler.py | 9 +-- autoarray/structures/grids/irregular_2d.py | 2 +- autoarray/structures/grids/uniform_2d.py | 21 +++-- .../structures/triangles/array/__init__.py | 1 + .../triangles/coordinate_array/__init__.py | 1 + .../dataset/abstract/test_dataset.py | 36 ++++----- .../dataset/plot/test_imaging_plotters.py | 8 +- .../inversion/plot/test_mapper_plotters.py | 11 +-- test_autoarray/inversion/test_linear_obj.py | 4 +- test_autoarray/plot/get_visuals/test_two_d.py | 8 +- .../structures/grids/test_uniform_2d.py | 7 +- 24 files changed, 242 insertions(+), 206 deletions(-) delete mode 100644 autoarray/dataset/over_sampling.py diff --git a/autoarray/__init__.py b/autoarray/__init__.py index 0e102360..58a32da2 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -15,7 +15,6 @@ from .dataset.interferometer.dataset import Interferometer from .dataset.interferometer.simulator import SimulatorInterferometer from .dataset.interferometer.w_tilde import WTildeInterferometer -from .dataset.over_sampling import OverSamplingDataset from .dataset.dataset_model import DatasetModel from .fit.fit_dataset import AbstractFit from .fit.fit_dataset import FitDataset diff --git a/autoarray/dataset/abstract/dataset.py b/autoarray/dataset/abstract/dataset.py index ea7c1671..eca0d7c8 100644 --- a/autoarray/dataset/abstract/dataset.py +++ b/autoarray/dataset/abstract/dataset.py @@ -4,7 +4,8 @@ import warnings from typing import Optional, Union -from autoarray.dataset.over_sampling import OverSamplingDataset +from autoconf import cached_property + from autoarray.dataset.grids import GridsDataset from autoarray import exc @@ -12,7 +13,8 @@ from autoarray.mask.mask_2d import Mask2D from autoarray.structures.abstract_structure import Structure from autoarray.structures.arrays.uniform_2d import Array2D -from autoconf import cached_property + +from autoarray.operators.over_sampling import over_sample_util logger = logging.getLogger(__name__) @@ -24,7 +26,8 @@ def __init__( data: Structure, noise_map: Structure, noise_covariance_matrix: Optional[np.ndarray] = None, - over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(), + over_sample_size_lp: Union[int, Array2D] = 4, + over_sample_size_pixelization: Union[int, Array2D] = 4, ): """ An abstract dataset, containing the image data, noise-map, PSF and associated quantities for calculations @@ -45,6 +48,32 @@ def __init__( over sampling calculations built in which approximate the 2D line integral of these calculations within a pixel. This is explained in more detail in the `GridsDataset` class. + **Over Sampling** + + If a grid is uniform and the centre of each point on the grid is the centre of a 2D pixel, evaluating + the value of a function on the grid requires a 2D line integral to compute it precisely. This can be + computationally expensive and difficult to implement. + + Over sampling is a numerical technique where the function is evaluated on a sub-grid within each grid pixel + which is higher resolution than the grid itself. This approximates more closely the value of the function + within a 2D line intergral of the values in the square pixel that the grid is centred. + + For example, in PyAutoGalaxy and PyAutoLens the light profiles and galaxies are evaluated in order to determine + how much light falls in each pixel. This uses over sampling and therefore a higher resolution grid than the + image data to ensure the calculation is accurate. + + This class controls how over sampling is performed for 2 different types of grids: + + - `lp`: A grids of (y,x) coordinates which aligns with the centre of every image pixel of the image data + and is used to evaluate light profiles for model-fititng. + + - `pixelization`: A grid of (y,x) coordinates which again align with the centre of every image pixel of + the image data. This grid is used specifically for pixelizations computed via the `inversion` module, which + can benefit from using different oversampling schemes than the normal grid. + + Different calculations typically benefit from different over sampling, which this class enables + the customization of. + Parameters ---------- data @@ -56,10 +85,13 @@ def __init__( noise_covariance_matrix A noise-map covariance matrix representing the covariance between noise in every `data` value, which can be used via a bespoke fit to account for correlated noise in the data. - over_sampling - The over sampling schemes which divide the grids into sub grids of smaller pixels within their host image - pixels when using the grid to evaluate a function (e.g. images) to better approximate the 2D line integral - This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). + over_sample_size_lp + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls + into each pixel. + over_sample_size_pixelization + How over sampling is performed for the grid which is associated with a pixelization, which is therefore + passed into the calculations performed in the `inversion` module. """ self.data = data @@ -93,7 +125,16 @@ def __init__( self.noise_map = noise_map - self.over_sampling = over_sampling + self.over_sample_size_lp = ( + over_sample_util.over_sample_size_convert_to_array_2d_from( + over_sample_size=over_sample_size_lp, mask=self.mask + ) + ) + self.over_sample_size_pixelization = ( + over_sample_util.over_sample_size_convert_to_array_2d_from( + over_sample_size=over_sample_size_pixelization, mask=self.mask + ) + ) @property def grid(self): @@ -101,7 +142,11 @@ def grid(self): @cached_property def grids(self): - return GridsDataset(mask=self.data.mask, over_sampling=self.over_sampling) + return GridsDataset( + mask=self.data.mask, + over_sample_size_lp=self.over_sample_size_lp, + over_sample_size_pixelization=self.over_sample_size_pixelization, + ) @property def shape_native(self): diff --git a/autoarray/dataset/grids.py b/autoarray/dataset/grids.py index 95231b70..c460bf82 100644 --- a/autoarray/dataset/grids.py +++ b/autoarray/dataset/grids.py @@ -1,7 +1,7 @@ from typing import Optional, Union -from autoarray.dataset.over_sampling import OverSamplingDataset from autoarray.mask.mask_2d import Mask2D +from autoarray.structures.arrays.uniform_2d import Array2D from autoarray.structures.arrays.kernel_2d import Kernel2D from autoarray.structures.grids.uniform_1d import Grid1D from autoarray.structures.grids.uniform_2d import Grid2D @@ -14,7 +14,8 @@ class GridsDataset: def __init__( self, mask: Mask2D, - over_sampling: OverSamplingDataset, + over_sample_size_lp: Union[int, Array2D], + over_sample_size_pixelization: Union[int, Array2D], psf: Optional[Kernel2D] = None, ): """ @@ -43,12 +44,20 @@ def __init__( Parameters ---------- - mask - over_sampling + over_sample_size_lp + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls + into each pixel. + over_sample_size_pixelization + How over sampling is performed for the grid which is associated with a pixelization, which is therefore + passed into the calculations performed in the `inversion` module. psf + The Point Spread Function kernel of the image which accounts for diffraction due to the telescope optics + via 2D convolution. """ self.mask = mask - self.over_sampling = over_sampling + self.over_sample_size_lp = over_sample_size_lp + self.over_sample_size_pixelization = over_sample_size_pixelization self.psf = psf @cached_property @@ -66,7 +75,7 @@ def lp(self) -> Union[Grid1D, Grid2D]: """ return Grid2D.from_mask( mask=self.mask, - over_sample_size=self.over_sampling.lp, + over_sample_size=self.over_sample_size_lp, ) @cached_property @@ -87,7 +96,7 @@ def pixelization(self) -> Grid2D: """ return Grid2D.from_mask( mask=self.mask, - over_sample_size=self.over_sampling.pixelization, + over_sample_size=self.over_sample_size_pixelization, ) @cached_property @@ -117,7 +126,7 @@ def blurring(self) -> Optional[Grid2D]: @cached_property def border_relocator(self) -> BorderRelocator: return BorderRelocator( - mask=self.mask, sub_size=self.pixelization.over_sample_size + 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 2c3e50b8..938a812d 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -8,7 +8,6 @@ from autoarray.dataset.abstract.dataset import AbstractDataset from autoarray.dataset.grids import GridsDataset from autoarray.dataset.imaging.w_tilde import WTildeImaging -from autoarray.dataset.over_sampling import OverSamplingDataset from autoarray.structures.arrays.uniform_2d import Array2D from autoarray.operators.convolver import Convolver from autoarray.structures.arrays.kernel_2d import Kernel2D @@ -28,7 +27,8 @@ def __init__( noise_map: Optional[Array2D] = None, psf: Optional[Kernel2D] = None, noise_covariance_matrix: Optional[np.ndarray] = None, - over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(), + over_sample_size_lp: Union[int, Array2D] = 4, + over_sample_size_pixelization: Union[int, Array2D] = 4, pad_for_convolver: bool = False, use_normalized_psf: Optional[bool] = True, check_noise_map: bool = True, @@ -69,10 +69,13 @@ def __init__( noise_covariance_matrix A noise-map covariance matrix representing the covariance between noise in every `data` value, which can be used via a bespoke fit to account for correlated noise in the data. - over_sampling - The over sampling schemes which divide the grids into sub grids of smaller pixels within their host image - pixels when using the grid to evaluate a function (e.g. images) to better approximate the 2D line integral - This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). + over_sample_size_lp + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls + into each pixel. + over_sample_size_pixelization + How over sampling is performed for the grid which is associated with a pixelization, which is therefore + passed into the calculations performed in the `inversion` module. pad_for_convolver The PSF convolution may extend beyond the edges of the image mask, which can lead to edge effects in the convolved image. If `True`, the image and noise-map are padded to ensure the PSF convolution does not @@ -114,7 +117,8 @@ def __init__( data=data, noise_map=noise_map, noise_covariance_matrix=noise_covariance_matrix, - over_sampling=over_sampling, + over_sample_size_lp=over_sample_size_lp, + over_sample_size_pixelization=over_sample_size_pixelization, ) self.use_normalized_psf = use_normalized_psf @@ -143,7 +147,10 @@ def __init__( @cached_property def grids(self): return GridsDataset( - mask=self.data.mask, over_sampling=self.over_sampling, psf=self.psf + mask=self.data.mask, + over_sample_size_lp=self.over_sample_size_lp, + over_sample_size_pixelization=self.over_sample_size_pixelization, + psf=self.psf, ) @cached_property @@ -214,7 +221,8 @@ def from_fits( psf_hdu: int = 0, noise_covariance_matrix: Optional[np.ndarray] = None, check_noise_map: bool = True, - over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(), + over_sample_size_lp: Union[int, Array2D] = 4, + over_sample_size_pixelization: Union[int, Array2D] = 4, ) -> "Imaging": """ Load an imaging dataset from multiple .fits file. @@ -253,10 +261,13 @@ def from_fits( A noise-map covariance matrix representing the covariance between noise in every `data` value. check_noise_map If True, the noise-map is checked to ensure all values are above zero. - over_sampling - The over sampling schemes which divide the grids into sub grids of smaller pixels within their host image - pixels when using the grid to evaluate a function (e.g. images) to better approximate the 2D line integral - This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). + over_sample_size_lp + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls + into each pixel. + over_sample_size_pixelization + How over sampling is performed for the grid which is associated with a pixelization, which is therefore + passed into the calculations performed in the `inversion` module. """ data = Array2D.from_fits( @@ -284,7 +295,8 @@ def from_fits( psf=psf, noise_covariance_matrix=noise_covariance_matrix, check_noise_map=check_noise_map, - over_sampling=over_sampling, + over_sample_size_lp=over_sample_size_lp, + over_sample_size_pixelization=over_sample_size_pixelization, ) def apply_mask(self, mask: Mask2D) -> "Imaging": @@ -323,12 +335,18 @@ def apply_mask(self, mask: Mask2D) -> "Imaging": else: noise_covariance_matrix = None + over_sample_size_lp = Array2D(values=self.over_sample_size_lp.native, mask=mask) + over_sample_size_pixelization = Array2D( + values=self.over_sample_size_pixelization.native, mask=mask + ) + dataset = Imaging( data=data, noise_map=noise_map, psf=self.psf, noise_covariance_matrix=noise_covariance_matrix, - over_sampling=self.over_sampling, + over_sample_size_lp=over_sample_size_lp, + over_sample_size_pixelization=over_sample_size_pixelization, pad_for_convolver=True, ) @@ -416,7 +434,8 @@ def apply_noise_scaling( noise_map=noise_map, psf=self.psf, noise_covariance_matrix=self.noise_covariance_matrix, - over_sampling=self.over_sampling, + over_sample_size_lp=self.over_sample_size_lp, + over_sample_size_pixelization=self.over_sample_size_pixelization, pad_for_convolver=False, check_noise_map=False, ) @@ -429,7 +448,8 @@ def apply_noise_scaling( def apply_over_sampling( self, - over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(), + over_sample_size_lp: Union[int, Array2D] = None, + over_sample_size_pixelization: Union[int, Array2D] = None, ) -> "AbstractDataset": """ Apply new over sampling objects to the grid and grid pixelization of the dataset. @@ -449,25 +469,22 @@ def apply_over_sampling( Parameters ---------- - over_sampling - The over sampling schemes which divide the grids into sub grids of smaller pixels within their host image - pixels when using the grid to evaluate a function (e.g. images) to better approximate the 2D line integral - This class controls over sampling for all the different grids (e.g. `grid`, `grids.pixelization). + over_sample_size_lp + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls + into each pixel. + over_sample_size_pixelization + How over sampling is performed for the grid which is associated with a pixelization, which is therefore + passed into the calculations performed in the `inversion` module. """ - uniform = over_sampling.lp or self.over_sampling.lp - pixelization = over_sampling.pixelization or self.over_sampling.pixelization - - over_sampling = OverSamplingDataset( - lp=uniform, - pixelization=pixelization, - ) - return Imaging( data=self.data, noise_map=self.noise_map, psf=self.psf, - over_sampling=over_sampling, + over_sample_size_lp=over_sample_size_lp or self.over_sample_size_lp, + over_sample_size_pixelization=over_sample_size_pixelization + or self.over_sample_size_pixelization, pad_for_convolver=False, check_noise_map=False, ) diff --git a/autoarray/dataset/imaging/simulator.py b/autoarray/dataset/imaging/simulator.py index 47f4d4da..a43cc614 100644 --- a/autoarray/dataset/imaging/simulator.py +++ b/autoarray/dataset/imaging/simulator.py @@ -1,5 +1,6 @@ import logging import numpy as np +from typing import Optional, Union from autoarray.dataset.imaging.dataset import Imaging from autoarray.structures.arrays.uniform_2d import Array2D @@ -98,7 +99,9 @@ def __init__( self.noise_if_add_noise_false = noise_if_add_noise_false self.noise_seed = noise_seed - def via_image_from(self, image: Array2D) -> Imaging: + def via_image_from( + self, image: Array2D, over_sample_size: Optional[Union[int, np.ndarray]] = None + ) -> Imaging: """ Simulate an `Imaging` dataset from an input image. @@ -163,6 +166,11 @@ def via_image_from(self, image: Array2D) -> Imaging: image = Array2D(values=image, mask=mask) - return Imaging( + dataset = Imaging( data=image, psf=self.psf, noise_map=noise_map, check_noise_map=False ) + + if over_sample_size is not None: + dataset = dataset.apply_over_sampling(over_sample_size_lp=over_sample_size) + + return dataset diff --git a/autoarray/dataset/interferometer/dataset.py b/autoarray/dataset/interferometer/dataset.py index abc155fd..f69753dd 100644 --- a/autoarray/dataset/interferometer/dataset.py +++ b/autoarray/dataset/interferometer/dataset.py @@ -7,7 +7,6 @@ from autoarray.dataset.abstract.dataset import AbstractDataset from autoarray.dataset.interferometer.w_tilde import WTildeInterferometer from autoarray.dataset.grids import GridsDataset -from autoarray.dataset.over_sampling import OverSamplingDataset from autoarray.operators.transformer import TransformerNUFFT from autoarray.structures.visibilities import Visibilities @@ -74,15 +73,11 @@ def __init__( """ self.real_space_mask = real_space_mask - over_sampling = OverSamplingDataset( - lp=1, - pixelization=1, - ) - super().__init__( data=data, noise_map=noise_map, - over_sampling=over_sampling, + over_sample_size_lp=1, + over_sample_size_pixelization=1, ) self.uv_wavelengths = uv_wavelengths @@ -93,7 +88,11 @@ def __init__( @cached_property def grids(self): - return GridsDataset(mask=self.real_space_mask, over_sampling=self.over_sampling) + return GridsDataset( + mask=self.real_space_mask, + over_sample_size_lp=self.over_sample_size_lp, + over_sample_size_pixelization=self.over_sample_size_pixelization, + ) @classmethod def from_fits( diff --git a/autoarray/dataset/over_sampling.py b/autoarray/dataset/over_sampling.py deleted file mode 100644 index c19938e3..00000000 --- a/autoarray/dataset/over_sampling.py +++ /dev/null @@ -1,52 +0,0 @@ -import logging -from typing import Union - -from autoarray.structures.arrays.uniform_2d import Array2D - -logger = logging.getLogger(__name__) - - -class OverSamplingDataset: - def __init__( - self, - lp: Union[int, Array2D] = 4, - pixelization: Union[int, Array2D] = 4, - ): - """ - Customizes how over sampling calculations are performed using the grids of the data. - - If a grid is uniform and the centre of each point on the grid is the centre of a 2D pixel, evaluating - the value of a function on the grid requires a 2D line integral to compute it precisely. This can be - computationally expensive and difficult to implement. - - Over sampling is a numerical technique where the function is evaluated on a sub-grid within each grid pixel - which is higher resolution than the grid itself. This approximates more closely the value of the function - within a 2D line intergral of the values in the square pixel that the grid is centred. - - For example, in PyAutoGalaxy and PyAutoLens the light profiles and galaxies are evaluated in order to determine - how much light falls in each pixel. This uses over sampling and therefore a higher resolution grid than the - image data to ensure the calculation is accurate. - - This class controls how over sampling is performed for 3 different types of grids: - - - `grid`: A grids of (y,x) coordinates which aligns with the centre of every image pixel of the image data. - - - `grids.pixelization`: A grid of (y,x) coordinates which again align with the centre of every image pixel of - the image data. This grid is used specifically for pixelizations computed via the `inversion` module, which - can benefit from using different oversampling schemes than the normal grid. - - Different calculations typically benefit from different over sampling, which this class enables - the customization of. - - Parameters - ---------- - lp - The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing - values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls - into each pixel. - pixelization - How over sampling is performed for the grid which is associated with a pixelization, which is therefore - passed into the calculations performed in the `inversion` module. - """ - self.lp = lp - self.pixelization = pixelization diff --git a/autoarray/dataset/plot/imaging_plotters.py b/autoarray/dataset/plot/imaging_plotters.py index 3f8bac92..8fed6f60 100644 --- a/autoarray/dataset/plot/imaging_plotters.py +++ b/autoarray/dataset/plot/imaging_plotters.py @@ -61,8 +61,8 @@ def figures_2d( noise_map: bool = False, psf: bool = False, signal_to_noise_map: bool = False, - over_sampling: bool = False, - over_sampling_pixelization: bool = False, + over_sample_size_lp: bool = False, + over_sample_size_pixelization: bool = False, title_str: Optional[str] = None, ): """ @@ -81,10 +81,10 @@ def figures_2d( Whether to make a 2D plot (via `imshow`) of the psf. signal_to_noise_map Whether to make a 2D plot (via `imshow`) of the signal-to-noise map. - over_sampling + over_sample_size_lp Whether to make a 2D plot (via `imshow`) of the Over Sampling for input light profiles. If adaptive sub size is used, the sub size grid for a centre of (0.0, 0.0) is used. - over_sampling_pixelization + over_sample_size_pixelization Whether to make a 2D plot (via `imshow`) of the Over Sampling for pixelizations. """ @@ -124,24 +124,24 @@ def figures_2d( ), ) - if over_sampling: + if over_sample_size_lp: self.mat_plot_2d.plot_array( - array=self.dataset.grids.lp.over_sample_size, + array=self.dataset.grids.over_sample_size_lp, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( - title=title_str or f"Over Sampling (Uniform)", - filename="over_sampling", + title=title_str or f"Over Sample Size (Light Profiles)", + filename="over_sample_size_lp", cb_unit="", ), ) - if over_sampling_pixelization: + if over_sample_size_pixelization: self.mat_plot_2d.plot_array( - array=self.dataset.grids.pixelization.over_sample_size, + array=self.dataset.grids.over_sample_size_pixelization, visuals_2d=self.get_visuals_2d(), auto_labels=AutoLabels( - title=title_str or f"Over Sampling (Pixelization)", - filename="over_sampling_pixelization", + title=title_str or f"Over Sample Size (Pixelization)", + filename="over_sample_size_pixelization", cb_unit="", ), ) @@ -218,8 +218,8 @@ def subplot_dataset(self): self.figures_2d(signal_to_noise_map=True) - self.figures_2d(over_sampling=True) - self.figures_2d(over_sampling_pixelization=True) + self.figures_2d(over_sample_size_lp=True) + self.figures_2d(over_sample_size_pixelization=True) self.mat_plot_2d.output.subplot_to_figure(auto_filename="subplot_dataset") self.close_subplot_figure() diff --git a/autoarray/fixtures.py b/autoarray/fixtures.py index 73d150d4..acfd277d 100644 --- a/autoarray/fixtures.py +++ b/autoarray/fixtures.py @@ -160,7 +160,7 @@ def make_imaging_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(lp=1), + over_sample_size_lp=1, ) @@ -169,7 +169,7 @@ def make_imaging_7x7_sub_2(): data=make_image_7x7(), psf=make_psf_3x3(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(lp=2), + over_sample_size_lp=2, ) @@ -178,7 +178,7 @@ def make_imaging_covariance_7x7(): data=make_image_7x7(), psf=make_psf_3x3(), noise_covariance_matrix=make_noise_covariance_matrix_7x7(), - over_sampling=aa.OverSamplingDataset(lp=1), + over_sample_size_lp=1, ) @@ -187,7 +187,7 @@ def make_imaging_7x7_no_blur(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(lp=1), + over_sample_size_lp=1, ) @@ -196,7 +196,7 @@ def make_imaging_7x7_no_blur_sub_2(): data=make_image_7x7(), psf=make_psf_3x3_no_blur(), noise_map=make_noise_map_7x7(), - over_sampling=aa.OverSamplingDataset(lp=2), + over_sample_size_lp=2, ) diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index 28f7a3ff..da6d2812 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -132,12 +132,9 @@ class BorderRelocator: def __init__(self, mask: Mask2D, sub_size: Union[int, Array2D]): self.mask = mask - if isinstance(sub_size, int): - sub_size = Array2D( - values=np.full(fill_value=sub_size, shape=mask.shape_slim), mask=mask - ) - - self.sub_size = sub_size + self.sub_size = over_sample_util.over_sample_size_convert_to_array_2d_from( + over_sample_size=sub_size, mask=mask + ) @cached_property def border_slim(self): diff --git a/autoarray/inversion/pixelization/image_mesh/hilbert.py b/autoarray/inversion/pixelization/image_mesh/hilbert.py index bde49849..964457de 100644 --- a/autoarray/inversion/pixelization/image_mesh/hilbert.py +++ b/autoarray/inversion/pixelization/image_mesh/hilbert.py @@ -86,6 +86,7 @@ def generate2d(x, y, ax, ay, bx, by): -(ay - ay2), ) + def grid_hilbert_order_from(length, mask_radius): """ This function will create a grid in the Hilbert space-filling curve order. diff --git a/autoarray/inversion/pixelization/mappers/delaunay.py b/autoarray/inversion/pixelization/mappers/delaunay.py index f18c3b39..737247b0 100644 --- a/autoarray/inversion/pixelization/mappers/delaunay.py +++ b/autoarray/inversion/pixelization/mappers/delaunay.py @@ -117,9 +117,7 @@ def pix_sub_weights(self) -> PixSubWeights: pix_indexes_for_simplex_index = delaunay.simplices mappings, sizes = mapper_util.pix_indexes_for_sub_slim_index_delaunay_from( - source_plane_data_grid=np.array( - self.source_plane_data_grid.over_sampled - ), + source_plane_data_grid=np.array(self.source_plane_data_grid.over_sampled), simplex_index_for_sub_slim_index=simplex_index_for_sub_slim_index, pix_indexes_for_simplex_index=pix_indexes_for_simplex_index, delaunay_points=delaunay.points, @@ -129,9 +127,7 @@ def pix_sub_weights(self) -> PixSubWeights: sizes = sizes.astype("int") weights = mapper_util.pixel_weights_delaunay_from( - source_plane_data_grid=np.array( - self.source_plane_data_grid.over_sampled - ), + source_plane_data_grid=np.array(self.source_plane_data_grid.over_sampled), source_plane_mesh_grid=np.array(self.source_plane_mesh_grid), slim_index_for_sub_slim_index=self.slim_index_for_sub_slim_index, pix_indexes_for_sub_slim_index=mappings, diff --git a/autoarray/operators/over_sampling/over_sample_util.py b/autoarray/operators/over_sampling/over_sample_util.py index c6584c92..1d4637fc 100644 --- a/autoarray/operators/over_sampling/over_sample_util.py +++ b/autoarray/operators/over_sampling/over_sample_util.py @@ -1,7 +1,6 @@ from __future__ import annotations import numpy as np -from typing import TYPE_CHECKING, List, Tuple -from autoconf import conf +from typing import TYPE_CHECKING, Union, List, Tuple from autoarray.structures.arrays.uniform_2d import Array2D @@ -9,13 +8,47 @@ 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.mask import mask_2d_util -from autoarray import exc from autoarray import type as ty +def over_sample_size_convert_to_array_2d_from( + over_sample_size: Union[int, np.ndarray], mask: Union[np.ndarray, Mask2D] +): + """ + Returns the over sample size as an `Array2D` object, for example converting it from a single integer. + + The interface allows a user to specify the `over_sample_size` as either: + + - A single integer, whereby over sampling is performed to this degree for every pixel. + - An ndarray with the same number of entries as the mask, to enable adaptive over sampling. + + This function converts these input structures to an `Array2D` which is used internally in the source code + to perform computations. + + Parameters + ---------- + over_sample_size + The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls + into each pixel. + + Returns + ------- + + """ + if isinstance(over_sample_size, int): + over_sample_size = np.full( + fill_value=over_sample_size, shape=mask.pixels_in_mask + ).astype("int") + + return Array2D(values=over_sample_size, mask=mask) + + @numba_util.jit() def total_sub_pixels_2d_from(sub_size: np.ndarray) -> int: """ diff --git a/autoarray/operators/over_sampling/over_sampler.py b/autoarray/operators/over_sampling/over_sampler.py index 1bdb1740..84888baf 100644 --- a/autoarray/operators/over_sampling/over_sampler.py +++ b/autoarray/operators/over_sampling/over_sampler.py @@ -141,12 +141,9 @@ def __init__(self, mask: Mask2D, sub_size: Union[int, Array2D]): """ self.mask = mask - if isinstance(sub_size, int): - sub_size = Array2D( - values=np.full(fill_value=sub_size, shape=mask.shape_slim), mask=mask - ) - - self.sub_size = sub_size + self.sub_size = over_sample_util.over_sample_size_convert_to_array_2d_from( + over_sample_size=sub_size, mask=mask + ) def tree_flatten(self): return (self.mask,), () diff --git a/autoarray/structures/grids/irregular_2d.py b/autoarray/structures/grids/irregular_2d.py index 54cb483c..2116291b 100644 --- a/autoarray/structures/grids/irregular_2d.py +++ b/autoarray/structures/grids/irregular_2d.py @@ -269,4 +269,4 @@ def grid_of_closest_from(self, grid_pair: "Grid2DIrregular") -> "Grid2DIrregular grid_of_closest[i, :] = self[np.argmin(radial_distances), :] - return Grid2DIrregular(values=grid_of_closest) \ No newline at end of file + return Grid2DIrregular(values=grid_of_closest) diff --git a/autoarray/structures/grids/uniform_2d.py b/autoarray/structures/grids/uniform_2d.py index 604e561a..e4cad24b 100644 --- a/autoarray/structures/grids/uniform_2d.py +++ b/autoarray/structures/grids/uniform_2d.py @@ -151,7 +151,7 @@ def __init__( mapping large data arrays to and from the slim / native formats, which can be a computational bottleneck. over_sample_size The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing - values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls into each pixel. over_sampled The over sampled grid of (y,x) coordinates, which can be passed in manually because if the grid is @@ -170,12 +170,9 @@ def __init__( grid_2d_util.check_grid_2d(grid_2d=values) - if isinstance(over_sample_size, int): - over_sample_size = np.full( - fill_value=over_sample_size, shape=mask.shape_slim - ).astype("int") - - over_sample_size = Array2D(values=over_sample_size, mask=mask) + over_sample_size = over_sample_util.over_sample_size_convert_to_array_2d_from( + over_sample_size=over_sample_size, mask=mask + ) from autoarray.operators.over_sampling.over_sampler import OverSampler @@ -1119,8 +1116,7 @@ def is_uniform(self) -> bool: return True def apply_over_sampling( - self, - over_sample_size : Union[int, np.ndarray] + self, over_sample_size: Union[int, np.ndarray] ) -> "AbstractDataset": """ Apply new over sampling to the grid. @@ -1132,17 +1128,18 @@ def apply_over_sampling( ---------- over_sample_size The over sampling scheme size, which divides the grid into a sub grid of smaller pixels when computing - values (e.g. images) from the grid so as to approximate the 2D line integral of the amount of light that falls + values (e.g. images) from the grid to approximate the 2D line integral of the amount of light that falls into each pixel. """ if not self.is_uniform: raise exc.GridException( """ Cannot apply over sampling to a Grid2D which is not uniform. - """) + """ + ) return Grid2D( values=self, mask=self.mask, over_sample_size=over_sample_size, - ) \ No newline at end of file + ) diff --git a/autoarray/structures/triangles/array/__init__.py b/autoarray/structures/triangles/array/__init__.py index 58feab94..0fade4b8 100644 --- a/autoarray/structures/triangles/array/__init__.py +++ b/autoarray/structures/triangles/array/__init__.py @@ -1,4 +1,5 @@ from .array import ArrayTriangles + try: from .jax_array import ArrayTriangles as JAXArrayTriangles except ImportError: diff --git a/autoarray/structures/triangles/coordinate_array/__init__.py b/autoarray/structures/triangles/coordinate_array/__init__.py index b40a452b..f70bc8a9 100644 --- a/autoarray/structures/triangles/coordinate_array/__init__.py +++ b/autoarray/structures/triangles/coordinate_array/__init__.py @@ -1,4 +1,5 @@ from .coordinate_array import CoordinateArrayTriangles + try: from .jax_coordinate_array import ( CoordinateArrayTriangles as JAXCoordinateArrayTriangles, diff --git a/test_autoarray/dataset/abstract/test_dataset.py b/test_autoarray/dataset/abstract/test_dataset.py index a1dc7573..dd99c120 100644 --- a/test_autoarray/dataset/abstract/test_dataset.py +++ b/test_autoarray/dataset/abstract/test_dataset.py @@ -65,7 +65,7 @@ def test__grid__uses_mask_and_settings( masked_imaging_7x7 = ds.AbstractDataset( data=masked_image_7x7, noise_map=masked_noise_map_7x7, - over_sampling=aa.OverSamplingDataset(lp=2), + over_sample_size_lp=2, ) assert isinstance(masked_imaging_7x7.grids.lp, aa.Grid2D) @@ -94,28 +94,24 @@ def test__grids_pixelization__uses_mask_and_settings( masked_imaging_7x7 = ds.AbstractDataset( data=masked_image_7x7, noise_map=masked_noise_map_7x7, - over_sampling=aa.OverSamplingDataset( - lp=2, - pixelization=4, - ), + over_sample_size_lp=2, + over_sample_size_pixelization=4, ) assert isinstance(masked_imaging_7x7.grids.pixelization, aa.Grid2D) - assert masked_imaging_7x7.grids.pixelization.over_sample_size[0] == 4 + assert masked_imaging_7x7.grids.over_sample_size_pixelization[0] == 4 def test__grid_settings__sub_size(image_7x7, noise_map_7x7): dataset_7x7 = ds.AbstractDataset( data=image_7x7, noise_map=noise_map_7x7, - over_sampling=aa.OverSamplingDataset( - lp=2, - pixelization=4, - ), + over_sample_size_lp=2, + over_sample_size_pixelization=4, ) - assert dataset_7x7.grids.lp.over_sample_size[0] == 2 - assert dataset_7x7.grids.pixelization.over_sample_size[0] == 4 + assert dataset_7x7.grids.over_sample_size_lp[0] == 2 + assert dataset_7x7.grids.over_sample_size_pixelization[0] == 4 def test__new_imaging_with_arrays_trimmed_via_kernel_shape(): @@ -140,10 +136,8 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): dataset_7x7 = aa.Imaging( data=image_7x7, noise_map=noise_map_7x7, - over_sampling=aa.OverSamplingDataset( - lp=2, - pixelization=2, - ), + over_sample_size_lp=2, + over_sample_size_pixelization=2, ) # The grid and grid_pixelizaiton are a cached_property which needs to be reset, @@ -159,14 +153,12 @@ def test__apply_over_sampling(image_7x7, noise_map_7x7): assert dataset_7x7.grids.pixelization[0][0] == pytest.approx(100.0, 1.0e-4) dataset_7x7 = dataset_7x7.apply_over_sampling( - over_sampling=aa.OverSamplingDataset( - lp=4, - pixelization=4, - ) + over_sample_size_lp=2, + over_sample_size_pixelization=4, ) - assert dataset_7x7.over_sampling.lp == 4 - assert dataset_7x7.over_sampling.pixelization == 4 + assert dataset_7x7.over_sample_size_lp.slim[0] == 2 + assert dataset_7x7.over_sample_size_pixelization.slim[0] == 4 assert dataset_7x7.grids.lp[0][0] == pytest.approx(3.0, 1.0e-4) assert dataset_7x7.grids.pixelization[0][0] == pytest.approx(3.0, 1.0e-4) diff --git a/test_autoarray/dataset/plot/test_imaging_plotters.py b/test_autoarray/dataset/plot/test_imaging_plotters.py index 6f71291f..d6c6673a 100644 --- a/test_autoarray/dataset/plot/test_imaging_plotters.py +++ b/test_autoarray/dataset/plot/test_imaging_plotters.py @@ -30,16 +30,16 @@ def test__individual_attributes_are_output( noise_map=True, psf=True, signal_to_noise_map=True, - over_sampling=True, - over_sampling_pixelization=True, + over_sample_size_lp=True, + over_sample_size_pixelization=True, ) assert path.join(plot_path, "data.png") in plot_patch.paths assert path.join(plot_path, "noise_map.png") in plot_patch.paths assert path.join(plot_path, "psf.png") in plot_patch.paths assert path.join(plot_path, "signal_to_noise_map.png") in plot_patch.paths - assert path.join(plot_path, "over_sampling.png") in plot_patch.paths - assert path.join(plot_path, "over_sampling_pixelization.png") in plot_patch.paths + assert path.join(plot_path, "over_sample_size_lp.png") in plot_patch.paths + assert path.join(plot_path, "over_sample_size_pixelization.png") in plot_patch.paths plot_patch.paths = [] diff --git a/test_autoarray/inversion/plot/test_mapper_plotters.py b/test_autoarray/inversion/plot/test_mapper_plotters.py index 00b72457..3dabe551 100644 --- a/test_autoarray/inversion/plot/test_mapper_plotters.py +++ b/test_autoarray/inversion/plot/test_mapper_plotters.py @@ -68,13 +68,14 @@ def test__get_2d__via_mapper_for_source_from(rectangular_mapper_7x7_3x3): assert mapper_plotter.visuals_2d.origin == None assert get_2d.origin.in_list == [(0.0, 0.0)] assert ( - get_2d.grid - == rectangular_mapper_7x7_3x3.source_plane_data_grid.over_sampled + get_2d.grid == rectangular_mapper_7x7_3x3.source_plane_data_grid.over_sampled ).all() assert (get_2d.mesh_grid == rectangular_mapper_7x7_3x3.source_plane_mesh_grid).all() - border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.over_sampled[ - rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim - ] + border_grid = ( + rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.over_sampled[ + rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim + ] + ) assert (get_2d.border == border_grid).all() include = aplt.Include2D( diff --git a/test_autoarray/inversion/test_linear_obj.py b/test_autoarray/inversion/test_linear_obj.py index 5267cd46..c5420435 100644 --- a/test_autoarray/inversion/test_linear_obj.py +++ b/test_autoarray/inversion/test_linear_obj.py @@ -44,9 +44,7 @@ def test__data_to_pix_unique_from(): mask = aa.Mask2D.all_false(shape_native=(1, 2), pixel_scales=0.1) - grid = aa.Grid2D.uniform( - shape_native=(1, 2), pixel_scales=0.1, over_sample_size=2 - ) + grid = aa.Grid2D.uniform(shape_native=(1, 2), pixel_scales=0.1, over_sample_size=2) linear_obj = aa.AbstractLinearObjFuncList(grid=grid, regularization=None) diff --git a/test_autoarray/plot/get_visuals/test_two_d.py b/test_autoarray/plot/get_visuals/test_two_d.py index af576f41..5494bcf2 100644 --- a/test_autoarray/plot/get_visuals/test_two_d.py +++ b/test_autoarray/plot/get_visuals/test_two_d.py @@ -110,9 +110,11 @@ def test__via_mapper_for_source_from(rectangular_mapper_7x7_3x3): visuals_2d_via.grid == rectangular_mapper_7x7_3x3.source_plane_data_grid.over_sampled ).all() - border_grid = rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.over_sampled[ - rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim - ] + border_grid = ( + rectangular_mapper_7x7_3x3.mapper_grids.source_plane_data_grid.over_sampled[ + rectangular_mapper_7x7_3x3.border_relocator.sub_border_slim + ] + ) assert (visuals_2d_via.border == border_grid).all() assert ( visuals_2d_via.mesh_grid == rectangular_mapper_7x7_3x3.source_plane_mesh_grid diff --git a/test_autoarray/structures/grids/test_uniform_2d.py b/test_autoarray/structures/grids/test_uniform_2d.py index 0fc83da4..4084908b 100644 --- a/test_autoarray/structures/grids/test_uniform_2d.py +++ b/test_autoarray/structures/grids/test_uniform_2d.py @@ -843,12 +843,7 @@ def test__is_uniform(): def test__apply_over_sampling(): - - grid = aa.Grid2D.uniform( - shape_native=(2, 2), - pixel_scales=1.0, - over_sample_size=1 - ) + grid = aa.Grid2D.uniform(shape_native=(2, 2), pixel_scales=1.0, over_sample_size=1) grid = grid.apply_over_sampling(over_sample_size=2) From 1d20ddaa7456b8e466ceef3ed44bdf9d798d09b5 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 20 Dec 2024 12:10:28 +0000 Subject: [PATCH 23/26] fix bug --- autoarray/config/visualize/plots.yaml | 4 ++-- autoarray/dataset/abstract/dataset.py | 12 ++++++++++ autoarray/dataset/imaging/dataset.py | 23 +++++++++++++++++++ autoarray/dataset/imaging/simulator.py | 4 +++- .../pixelization/border_relocator.py | 4 ++-- .../pixelization/mappers/abstract.py | 2 +- 6 files changed, 43 insertions(+), 6 deletions(-) diff --git a/autoarray/config/visualize/plots.yaml b/autoarray/config/visualize/plots.yaml index 1f2185b2..da0e330e 100644 --- a/autoarray/config/visualize/plots.yaml +++ b/autoarray/config/visualize/plots.yaml @@ -8,8 +8,8 @@ dataset: # Settings for plots of all datasets data: false # Plot the individual data of every dataset? noise_map: false # Plot the individual noise-map of every dataset? signal_to_noise_map: false # Plot the individual signal-to-noise-map of every dataset? - over_sampling: false # Plot the over-sampling sub-size, used to evaluate light profiles, of every dataset? - over_sampling_pixelization: false # Plot the over-sampling sub-size, used to evaluate pixelizations, of every dataset? + over_sample_size_lp: false # Plot the over-sampling size, used to evaluate light profiles, of every dataset? + over_sample_size_pixelization: false # Plot the over-sampling size, used to evaluate pixelizations, of every dataset? imaging: # Settings for plots of imaging datasets (e.g. ImagingPlotter) psf: false fit: # Settings for plots of all fits (e.g. FitImagingPlotter, FitInterferometerPlotter). diff --git a/autoarray/dataset/abstract/dataset.py b/autoarray/dataset/abstract/dataset.py index eca0d7c8..726211e7 100644 --- a/autoarray/dataset/abstract/dataset.py +++ b/autoarray/dataset/abstract/dataset.py @@ -206,4 +206,16 @@ def trimmed_after_convolution_from(self, kernel_shape) -> "AbstractDataset": kernel_shape=kernel_shape ) + dataset.over_sample_size_lp = ( + dataset.over_sample_size_lp.trimmed_after_convolution_from( + kernel_shape=kernel_shape + ) + ) + + dataset.over_sample_size_pixelization = ( + dataset.over_sample_size_pixelization.trimmed_after_convolution_from( + kernel_shape=kernel_shape + ) + ) + return dataset diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 938a812d..9fe0bd96 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -15,6 +15,7 @@ from autoarray import type as ty from autoarray import exc +from autoarray.operators.over_sampling import over_sample_util from autoarray.inversion.inversion.imaging import inversion_imaging_util logger = logging.getLogger(__name__) @@ -97,6 +98,28 @@ def __init__( kernel_shape_native=psf.shape_native ) except exc.MaskException: + over_sample_size_lp = ( + over_sample_util.over_sample_size_convert_to_array_2d_from( + over_sample_size=over_sample_size_lp, mask=data.mask + ) + ) + over_sample_size_lp = ( + over_sample_size_lp.padded_before_convolution_from( + kernel_shape=psf.shape_native, mask_pad_value=1 + ) + ) + print(over_sample_size_lp.shape_native) + over_sample_size_pixelization = ( + over_sample_util.over_sample_size_convert_to_array_2d_from( + over_sample_size=over_sample_size_pixelization, mask=data.mask + ) + ) + over_sample_size_pixelization = ( + over_sample_size_pixelization.padded_before_convolution_from( + kernel_shape=psf.shape_native, mask_pad_value=1 + ) + ) + data = data.padded_before_convolution_from( kernel_shape=psf.shape_native, mask_pad_value=1 ) diff --git a/autoarray/dataset/imaging/simulator.py b/autoarray/dataset/imaging/simulator.py index a43cc614..449a3d4b 100644 --- a/autoarray/dataset/imaging/simulator.py +++ b/autoarray/dataset/imaging/simulator.py @@ -171,6 +171,8 @@ def via_image_from( ) if over_sample_size is not None: - dataset = dataset.apply_over_sampling(over_sample_size_lp=over_sample_size) + dataset = dataset.apply_over_sampling( + over_sample_size_lp=over_sample_size.native + ) return dataset diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index da6d2812..50acf10e 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -211,7 +211,7 @@ def sub_border_slim(self) -> np.ndarray: print(derive_indexes_2d.sub_border_slim) """ return sub_border_pixel_slim_indexes_from( - mask_2d=np.array(self.mask), sub_size=self.sub_size + mask_2d=np.array(self.mask), sub_size=np.array(self.sub_size).astype("int") ).astype("int") @cached_property @@ -235,7 +235,7 @@ def sub_border_grid(self) -> np.ndarray: sub_grid = 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=np.array(self.sub_size), + sub_size=np.array(self.sub_size).astype("int"), origin=self.mask.origin, ) diff --git a/autoarray/inversion/pixelization/mappers/abstract.py b/autoarray/inversion/pixelization/mappers/abstract.py index 192fa4f5..fd0ec103 100644 --- a/autoarray/inversion/pixelization/mappers/abstract.py +++ b/autoarray/inversion/pixelization/mappers/abstract.py @@ -260,7 +260,7 @@ def unique_mappings(self) -> UniqueMappings: pix_sizes_for_sub_slim_index=self.pix_sizes_for_sub_slim_index, pix_weights_for_sub_slim_index=self.pix_weights_for_sub_slim_index, pix_pixels=self.params, - sub_size=np.array(self.over_sampler.sub_size), + sub_size=np.array(self.over_sampler.sub_size).astype("int"), ) return UniqueMappings( From b4548cc1331d322788227c0f362c89d011e1a67e Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 13 Jan 2025 15:40:45 +0000 Subject: [PATCH 24/26] dataset changes --- autoarray/dataset/imaging/dataset.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 9fe0bd96..3609a297 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -440,16 +440,26 @@ def apply_noise_scaling( else: data = self.data.native - data = Array2D.no_mask( + data_unmasked = Array2D.no_mask( values=data, shape_native=self.data.shape_native, - pixel_scales=self.data.pixel_scales, + pixel_scales=self.data.pixel_scales ) - noise_map = Array2D.no_mask( + noise_map_unmasked = Array2D.no_mask( values=noise_map, - shape_native=self.data.shape_native, - pixel_scales=self.data.pixel_scales, + shape_native=self.noise_map.shape_native, + pixel_scales=self.noise_map.pixel_scales + ) + + data = Array2D( + values=data, + mask=self.data.mask + ) + + noise_map = Array2D( + values=noise_map, + mask=self.data.mask ) dataset = Imaging( @@ -463,6 +473,11 @@ def apply_noise_scaling( check_noise_map=False, ) + if self.unmasked is not None: + dataset.unmasked = self.unmasked + dataset.unmasked.data = data_unmasked + dataset.unmasked.noise_map = noise_map_unmasked + logger.info( f"IMAGING - Data noise scaling applied, a total of {mask.pixels_in_mask} pixels were scaled to large noise values." ) From 18ca6ef353863085bb069230471e3ef703c8d824 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Tue, 14 Jan 2025 21:44:41 +0000 Subject: [PATCH 25/26] docstring --- autoarray/dataset/imaging/dataset.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 3609a297..7c7b01a9 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -499,12 +499,6 @@ def apply_over_sampling( This function resets the cached properties so that the new over sampling is used in the grid and grid pixelization. - The `default_galaxy_mode` parameter is used to set up default over sampling for galaxy light profiles in - the project PyAutoGalaxy. This sets up the over sampling such that there is high over sampling in the centre - of the mask, where the galaxy is located, and lower over sampling in the outer regions of the mask. It - does this based on the pixel scale, which gives a good estimate of how large the central region - requiring over sampling is. - Parameters ---------- over_sample_size_lp From a3c145492a25067c74e07ee013acc5b542b06533 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 15 Jan 2025 21:12:56 +0000 Subject: [PATCH 26/26] final clean up --- autoarray/dataset/imaging/dataset.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 7c7b01a9..78449ee4 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -443,24 +443,18 @@ def apply_noise_scaling( data_unmasked = Array2D.no_mask( values=data, shape_native=self.data.shape_native, - pixel_scales=self.data.pixel_scales + pixel_scales=self.data.pixel_scales, ) noise_map_unmasked = Array2D.no_mask( values=noise_map, shape_native=self.noise_map.shape_native, - pixel_scales=self.noise_map.pixel_scales + pixel_scales=self.noise_map.pixel_scales, ) - data = Array2D( - values=data, - mask=self.data.mask - ) + data = Array2D(values=data, mask=self.data.mask) - noise_map = Array2D( - values=noise_map, - mask=self.data.mask - ) + noise_map = Array2D(values=noise_map, mask=self.data.mask) dataset = Imaging( data=data,