Source code for autoarray.inversion.regularization.constant
from __future__ import annotations
import numpy as np
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from autoarray.inversion.linear_obj.linear_obj import LinearObj
from autoarray.inversion.regularization.abstract import AbstractRegularization
def constant_regularization_matrix_from(
coefficient: float, neighbors: np.ndarray, neighbors_sizes: np.ndarray, xp=np
) -> np.ndarray:
"""
From the pixel-neighbors array, setup the regularization matrix using the instance regularization scheme.
A complete description of regularizatin and the `regularization_matrix` can be found in the `Regularization`
class in the module `autoarray.inversion.regularization`.
Memory requirement: 2SP + S^2
FLOPS: 1 + 2S + 2SP
Parameters
----------
coefficient
The regularization coefficients which controls the degree of smoothing of the inversion reconstruction.
neighbors : ndarray, shape (S, P), dtype=int64
An array of length (total_pixels) which provides the index of all neighbors of every pixel in
the Delaunay grid (entries of -1 correspond to no neighbor).
neighbors_sizes : ndarray, shape (S,), dtype=int64
An array of length (total_pixels) which gives the number of neighbors of every pixel in the
Delaunay grid.
Returns
-------
regularization_matrix : ndarray, shape (S, S), dtype=float64
The regularization matrix computed using Regularization where the effective regularization
coefficient of every source pixel is the same.
"""
S, P = neighbors.shape
# as the regularization matrix is S by S, S would be out of bound (any out of bound index would do)
OUT_OF_BOUND_IDX = S
regularization_coefficient = coefficient * coefficient
# flatten it for feeding into the matrix as j indices
neighbors = neighbors.flatten()
# now create the corresponding i indices
I_IDX = xp.repeat(xp.arange(S), P)
# Entries of `-1` in `neighbors` (indicating no neighbor) are replaced with an out-of-bounds index.
# This ensures that JAX can efficiently drop these entries during matrix updates.
neighbors = xp.where(neighbors == -1, OUT_OF_BOUND_IDX, neighbors)
diag_vals = 1e-8 + regularization_coefficient * neighbors_sizes
if xp.__name__.startswith("jax"):
return (
xp.diag(diag_vals)
.at[I_IDX, neighbors]
.add(-regularization_coefficient, mode="drop", unique_indices=True)
)
else:
mat = xp.diag(diag_vals).copy()
valid_mask = (neighbors >= 0) & (neighbors < mat.shape[1])
I_valid = I_IDX[valid_mask]
neigh_valid = neighbors[valid_mask]
# scatter-add
xp.add.at(mat, (I_valid, neigh_valid), -regularization_coefficient)
return mat
[docs]
class Constant(AbstractRegularization):
def __init__(self, coefficient: float = 1.0):
"""
Regularization which uses the neighbors of the mesh (e.g. shared Delaunay vertexes) and
a single value to smooth an inversion's solution.
For this regularization scheme, there is only 1 regularization coefficient that is applied to
all neighboring pixels / parameters. This means that the matrix B only needs to regularize pixels / parameters
in one direction (e.g. pixel 0 regularizes pixel 1, but NOT visa versa). For example:
B = [-1, 1] [0->1]
[0, -1] 1 does not regularization with 0
A small numerical value of 1.0e-8 is added to all elements in constant regularization matrix, to ensure that
it is positive definite.
A full description of regularization and this matrix can be found in the parent `AbstractRegularization` class.
Parameters
----------
coefficient
The regularization coefficient which controls the degree of smooth of the inversion reconstruction.
"""
self.coefficient = coefficient
super().__init__()
[docs]
def regularization_weights_from(self, linear_obj: LinearObj, xp=np) -> np.ndarray:
"""
Returns the regularization weights of this regularization scheme.
The regularization weights define the level of regularization applied to each parameter in the linear object
(e.g. the ``pixels`` in a ``Mapper``).
For standard regularization (e.g. ``Constant``) are weights are equal, however for adaptive schemes
(e.g. ``Adapt``) they vary to adapt to the data being reconstructed.
Parameters
----------
linear_obj
The linear object (e.g. a ``Mapper``) which uses these weights when performing regularization.
Returns
-------
The regularization weights.
"""
return self.coefficient * xp.ones(linear_obj.params)
[docs]
def regularization_matrix_from(self, linear_obj: LinearObj, xp=np) -> np.ndarray:
"""
Returns the regularization matrix with shape [pixels, pixels].
Parameters
----------
linear_obj
The linear object (e.g. a ``Mapper``) which uses this matrix to perform regularization.
Returns
-------
The regularization matrix.
"""
return constant_regularization_matrix_from(
coefficient=self.coefficient,
neighbors=linear_obj.neighbors,
neighbors_sizes=linear_obj.neighbors.sizes,
xp=xp,
)