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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions autoarray/inversion/inversion/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ def __init__(
no_regularization_add_to_curvature_diag_value: float = None,
use_w_tilde_numpy: bool = False,
use_source_loop: bool = False,
image_mesh_min_mesh_pixels_per_pixel=None,
image_mesh_min_mesh_number: int = 5,
image_mesh_adapt_background_percent_threshold: float = None,
image_mesh_adapt_background_percent_check: float = 0.8,
tolerance: float = 1e-8,
maxiter: int = 250,
):
Expand All @@ -46,19 +42,6 @@ def __init__(
which exploit sparsity to do the calculation normally in a more efficient way).
use_source_loop
Shhhh its a secret.
image_mesh_min_mesh_pixels_per_pixel
If not None, the image-mesh must place this many mesh pixels per image pixels in the N highest weighted
regions of the adapt data, or an `InversionException` is raised. This can be used to force the image-mesh
to cluster large numbers of source pixels to the adapt-datas brightest regions.
image_mesh_min_mesh_number
The value N given above in the docstring for `image_mesh_min_mesh_pixels_per_pixel`, indicating how many
image pixels are checked for having a threshold number of mesh pixels.
image_mesh_adapt_background_percent_threshold
If not None, the image-mesh must place this percentage of mesh-pixels in the background regions of the
`adapt_data`, where the background is the `image_mesh_adapt_background_percent_check` masked data pixels
with the lowest values.
image_mesh_adapt_background_percent_check
The percentage of masked data pixels which are checked for the background criteria.
tolerance
For an interferometer inversion using the linear operators method, sets the tolerance of the solver
(this input does nothing for dataset data and other interferometer methods).
Expand All @@ -73,14 +56,6 @@ def __init__(
self._no_regularization_add_to_curvature_diag_value = (
no_regularization_add_to_curvature_diag_value
)
self.image_mesh_min_mesh_pixels_per_pixel = image_mesh_min_mesh_pixels_per_pixel
self.image_mesh_min_mesh_number = image_mesh_min_mesh_number
self.image_mesh_adapt_background_percent_threshold = (
image_mesh_adapt_background_percent_threshold
)
self.image_mesh_adapt_background_percent_check = (
image_mesh_adapt_background_percent_check
)

self.tolerance = tolerance
self.maxiter = maxiter
Expand Down
12 changes: 0 additions & 12 deletions autoarray/inversion/mock/mock_image_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,3 @@ def __init__(self, image_plane_mesh_grid=None):
super().__init__()

self.image_plane_mesh_grid = image_plane_mesh_grid

def image_plane_mesh_grid_from(
self, mask: Mask2D, adapt_data: Optional[np.ndarray], settings=None
) -> Grid2DIrregular:
if adapt_data is not None and self.image_plane_mesh_grid is not None:
return adapt_data * self.image_plane_mesh_grid

return self.image_plane_mesh_grid

@property
def uses_adapt_images(self) -> bool:
return False
5 changes: 0 additions & 5 deletions autoarray/inversion/mock/mock_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,8 @@ def image_plane_mesh_grid_from(
self,
mask: Mask2D,
adapt_data,
settings=None,
):
if adapt_data is not None and self.image_plane_mesh_grid is not None:
return adapt_data * self.image_plane_mesh_grid

return self.image_plane_mesh_grid

@property
def requires_image_mesh(self):
return False
5 changes: 1 addition & 4 deletions autoarray/inversion/mock/mock_pixelization.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ def __init__(
self,
mesh=None,
regularization=None,
image_mesh=None,
mapper=None,
image_plane_mesh_grid=None,
):
super().__init__(
mesh=mesh, regularization=regularization, image_mesh=image_mesh
)
super().__init__(mesh=mesh, regularization=regularization)

self.mapper = mapper
self.image_plane_mesh_grid = image_plane_mesh_grid
Expand Down
130 changes: 1 addition & 129 deletions autoarray/inversion/pixelization/image_mesh/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def __init__(self):
"""
An abstract image mesh, which is used by pixelizations to determine the (y,x) mesh coordinates from image
data.

"""
pass

Expand All @@ -29,7 +30,6 @@ def image_plane_mesh_grid_from(
self,
mask: Mask2D,
adapt_data: Optional[np.ndarray] = None,
settings: SettingsInversion = None,
) -> Grid2DIrregular:
raise NotImplementedError

Expand Down Expand Up @@ -65,131 +65,3 @@ def mesh_pixels_per_image_pixels_from(
)

return Array2D(values=mesh_pixels_per_image_pixels, mask=mask)

def check_mesh_pixels_per_image_pixels(
self, mask: Mask2D, mesh_grid: Grid2DIrregular, settings: SettingsInversion
):
"""
Checks the number of mesh pixels in every image pixel and raises an `InversionException` if there are fewer
mesh pixels inside a certain number of image-pixels than the input settings.

This allows a user to force a model-fit to use image-mesh's which cluster a large number of mesh pixels to
the brightest regions of the image data (E.g. the highst weighted regions).

The check works as follows:

1) Compute the 2D array of the number of mesh pixels in every masked data image pixel.
2) Find the number of mesh pixels in the N data pixels with the larger number of mesh pixels, where N is
given by `settings.image_mesh_min_mesh_number`. For example, if `settings.image_mesh_min_mesh_number=5` then
the number of mesh pixels in the 5 data pixels with the most data pixels is computed.
3) Compare the lowest value above to the value `settings.image_mesh_min_mesh_pixels_per_pixel`. If the value is
below this value, raise an `InversionException`.

Therefore, by settings `settings.image_mesh_min_mesh_pixels_per_pixel` to a value above 1 the code is forced
to adapt the image mesh enough to put many mesh pixels in the brightest image pixels.

Parameters
----------
mask
The mask of the dataset being analysed, which the pixelization grid maps too. The number of
mesh pixels mapped inside each of this mask's image-pixels is returned.
mesh_grid
The image mesh-grid computed by the class which adapts to the data's mask. The number of image mesh pixels
that fall within each of the data's mask pixels is returned.
settings
The inversion settings, which have the criteria dictating if the image-mesh has clustered enough or if
an exception is raised.
"""

if os.environ.get("PYAUTOFIT_TEST_MODE") == "1":
return

if settings is not None:
if settings.image_mesh_min_mesh_pixels_per_pixel is not None:
mesh_pixels_per_image_pixels = self.mesh_pixels_per_image_pixels_from(
mask=mask, mesh_grid=mesh_grid
)

indices_of_highest_values = np.argsort(mesh_pixels_per_image_pixels)[
-settings.image_mesh_min_mesh_number :
]
lowest_mesh_pixels = np.min(
mesh_pixels_per_image_pixels[indices_of_highest_values]
)

if lowest_mesh_pixels < settings.image_mesh_min_mesh_pixels_per_pixel:
raise exc.InversionException()

return mesh_grid

def check_adapt_background_pixels(
self,
mask: Mask2D,
mesh_grid: Grid2DIrregular,
adapt_data: Optional[np.ndarray],
settings: SettingsInversion,
):
"""
Checks the number of mesh pixels in the background of the image-mesh and raises an `InversionException` if
there are fewer mesh pixels in the background than the input settings.

This allows a user to force a model-fit to use image-mesh's which cluster a minimum number of mesh pixels to
the faintest regions of the image data (E.g. the lowest weighted regions). This prevents too few image-mesh
pixels being allocated to the background of the data.

The check works as follows:

1) Find all pixels in the background of the `adapt_data`, which are N pixels with the lowest values, where N is
a percentage given by `settings.image_mesh_adapt_background_percent_check`. If N is 50%, then the half of
pixels in `adapt_data` with the lowest values will be checked.
2) Sum the total number of mesh pixels in these background pixels, thereby estimating the number of mesh pixels
assigned to background pixels.
3) Compare this value to the total number of mesh pixels multiplied
by `settings.image_mesh_adapt_background_percent_threshold` and raise an `InversionException` if the number
of mesh pixels is below this value, meaning the background did not have sufficient mesh pixels in it.

Therefore, by setting `settings.image_mesh_adapt_background_percent_threshold` the code is forced
to adapt the image mesh in a way that places many mesh pixels in the background regions.

Parameters
----------
mask
The mask of the dataset being analysed, which the pixelization grid maps too. The number of
mesh pixels mapped inside each of this mask's image-pixels is returned.
mesh_grid
The image mesh-grid computed by the class which adapts to the data's mask. The number of image mesh pixels
that fall within each of the data's mask pixels is returned.
adapt_data
A image which represents one or more components in the masked 2D data in the image-plane.
settings
The inversion settings, which have the criteria dictating if the image-mesh has clustered enough or if
an exception is raised.
"""

if os.environ.get("PYAUTOFIT_TEST_MODE") == "1":
return

if settings is not None:
if settings.image_mesh_adapt_background_percent_threshold is not None:
pixels = mesh_grid.shape[0]

pixels_in_background = int(
mask.shape_slim * settings.image_mesh_adapt_background_percent_check
)

indices_of_lowest_values = np.argsort(adapt_data)[:pixels_in_background]
mask_background = np.zeros_like(adapt_data, dtype=bool)
mask_background[indices_of_lowest_values] = True

mesh_pixels_per_image_pixels = self.mesh_pixels_per_image_pixels_from(
mask=mask, mesh_grid=mesh_grid
)

mesh_pixels_in_background = sum(
mesh_pixels_per_image_pixels[mask_background]
)

if mesh_pixels_in_background < (
pixels * settings.image_mesh_adapt_background_percent_threshold
):
raise exc.InversionException()
Loading
Loading