Source code for autoarray.inversion.mesh.mesh.rectangular_adapt_density

import numpy as np
from typing import Optional, Tuple

from autoarray.settings import Settings
from autoarray.inversion.regularization.abstract import AbstractRegularization
from autoarray.structures.grids.irregular_2d import Grid2DIrregular
from autoarray.structures.grids.uniform_2d import Grid2D
from autoarray.inversion.mesh.mesh.abstract import AbstractMesh
from autoarray.inversion.mesh.border_relocator import BorderRelocator

from autoarray.structures.grids import grid_2d_util

from autoarray import exc


def overlay_grid_from(
    shape_native: Tuple[int, int],
    grid: np.ndarray,
    buffer: float = 1e-8,
    xp=np,
) -> np.ndarray:
    """
    Creates a `Grid2DRecntagular` by overlaying the rectangular pixelization over an input grid of (y,x)
    coordinates.

    This is performed by first computing the minimum and maximum y and x coordinates of the input grid. A
    rectangular pixelization with dimensions `shape_native` is then laid over the grid using these coordinates,
    such that the extreme edges of this rectangular pixelization overlap these maximum and minimum (y,x) coordinates.

    A a `buffer` can be included which increases the size of the rectangular pixelization, placing additional
    spacing beyond these maximum and minimum coordinates.

    Parameters
    ----------
    shape_native
        The 2D dimensions of the rectangular pixelization with shape (y_pixels, x_pixel).
    grid
        A grid of (y,x) coordinates which the rectangular pixelization is laid-over.
    buffer
        The size of the extra spacing placed between the edges of the rectangular pixelization and input grid.
    """
    grid = grid.array

    y_min = xp.min(grid[:, 0]) - buffer
    y_max = xp.max(grid[:, 0]) + buffer
    x_min = xp.min(grid[:, 1]) - buffer
    x_max = xp.max(grid[:, 1]) + buffer

    pixel_scales = xp.array(
        (
            (y_max - y_min) / shape_native[0],
            (x_max - x_min) / shape_native[1],
        )
    )
    origin = xp.array(((y_max + y_min) / 2.0, (x_max + x_min) / 2.0))

    grid_slim = grid_2d_util.grid_2d_slim_via_shape_native_not_mask_from(
        shape_native=shape_native, pixel_scales=pixel_scales, origin=origin, xp=xp
    )

    return grid_slim


[docs] class RectangularAdaptDensity(AbstractMesh): def __init__(self, shape: Tuple[int, int] = (3, 3)): """ A uniform rectangular mesh of pixels used to reconstruct a source on a regular grid. The mesh is defined by a 2D shape `(total_y_pixels, total_x_pixels)` and is indexed in row-major order: - Index 0 corresponds to the top-left pixel. - Indices increase from left to right across each row, and from top to bottom across rows. Each source-plane coordinate is associated with the rectangular pixel in which it lies. No interpolation is performed — every coordinate contributes entirely to a single pixel. Adaptive behaviour ------------------ Although the rectangular mesh has a fixed, uniform geometry, it adapts *implicitly* to the spatial density of the points it is paired with. Regions of the source plane where many coordinates map onto the same pixel receive stronger observational constraints, while sparsely sampled regions are more weakly constrained. In gravitational lensing applications, this naturally concentrates information in regions of high magnification, where many image-plane pixels map to a small area of the source plane. The inversion therefore achieves higher effective resolution in these regions without requiring explicit refinement of the mesh geometry. Edge handling ------------- Boundary (edge) pixels are automatically identified through the mesh neighbour structure. These edge pixels may be internally excluded (zeroed) during inversion to improve numerical stability and reduce edge artefacts. This zeroing is determined by the mesh connectivity and does not require manual specification of boundary indices. Parameters ---------- shape : Tuple[int, int] The 2D dimensions of the rectangular pixel grid `(total_y_pixels, total_x_pixels)`. Raises ------ MeshException If either dimension is less than 3, as a minimum of 3×3 pixels is required to define interior and boundary structure. """ if shape[0] <= 2 or shape[1] <= 2: raise exc.MeshException( "The rectangular pixelization must be at least dimensions 3x3" ) self.shape = (int(shape[0]), int(shape[1])) self.pixels = self.shape[0] * self.shape[1] super().__init__() @property def zeroed_pixels(self): """ Return the **positive** 1D pixel indices of the edge pixels in a rectangular mesh. Indices are in row-major (C-order) flattened form for the rectangular pixel grid: - 0 corresponds to the top-left pixel (row=0, col=0) - indices increase across rows These indices are defined purely within the rectangular mesh's pixel indexing scheme (size = rows * cols) and are intended to be shifted / mapped to the full inversion indexing inside the inversion logic. Returns ------- np.ndarray A 1D array of positive indices corresponding to edge pixels. """ from autoarray.inversion.mesh.mesh_geometry.rectangular import ( rectangular_edge_pixel_list_from, ) edge_pixel_list = rectangular_edge_pixel_list_from(shape_native=self.shape) return np.array(edge_pixel_list, dtype=int) @property def interpolator_cls(self): from autoarray.inversion.mesh.interpolator.rectangular import ( InterpolatorRectangular, ) return InterpolatorRectangular
[docs] def mesh_weight_map_from(self, adapt_data, xp=np) -> np.ndarray: """ The weight map of a rectangular pixelization is None, because magnificaiton adaption uses the distribution and density of traced (y,x) coordinates in the source plane and not weights or the adapt data. Parameters ---------- xp The array library to use. """ return None
[docs] def interpolator_from( self, source_plane_data_grid: Grid2D, source_plane_mesh_grid: Grid2DIrregular, border_relocator: Optional[BorderRelocator] = None, adapt_data: np.ndarray = None, xp=np, ): """ Mapper objects describe the mappings between pixels in the masked 2D data and the pixels in a pixelization, in both the `data` and `source` frames. This function returns a `MapperRectangular` as follows: 1) If the bordr relocator is input, the border of the input `source_plane_data_grid` is used to relocate all of the grid's (y,x) coordinates beyond the border to the edge of the border. 2) Determine the (y,x) coordinates of the pixelization's rectangular pixels, by laying this rectangular grid over the 2D grid of relocated (y,x) coordinates computed in step 1 (or the input `source_plane_data_grid` if step 1 is bypassed). 3) Return the `MapperRectangular`. Parameters ---------- border_relocator The border relocator, which relocates coordinates outside the border of the source-plane data grid to its edge. source_plane_data_grid A 2D grid of (y,x) coordinates associated with the unmasked 2D data after it has been transformed to the `source` reference frame. source_plane_mesh_grid Not used for a rectangular pixelization, because the pixelization grid in the `source` frame is computed by overlaying the `source_plane_data_grid` with the rectangular pixelization. image_plane_mesh_grid Not used for a rectangular pixelization. adapt_data Not used for a rectangular pixelization. """ relocated_grid = self.relocated_grid_from( border_relocator=border_relocator, source_plane_data_grid=source_plane_data_grid, xp=xp, ) mesh_grid = overlay_grid_from( shape_native=self.shape, grid=relocated_grid.over_sampled, xp=xp, ) mesh_weight_map = self.mesh_weight_map_from(adapt_data=adapt_data, xp=xp) return self.interpolator_cls( mesh=self, data_grid=relocated_grid, mesh_grid=Grid2DIrregular(mesh_grid), mesh_weight_map=mesh_weight_map, adapt_data=adapt_data, xp=xp, )