Skip to content

Commit

Permalink
Merge pull request #143 from Jammy2211/feature/coordinate_jax
Browse files Browse the repository at this point in the history
feature/coordinate jax
  • Loading branch information
rhayes777 authored Nov 11, 2024
2 parents 9331755 + f95c28f commit 311f7cd
Show file tree
Hide file tree
Showing 18 changed files with 965 additions and 18 deletions.
2 changes: 1 addition & 1 deletion autoarray/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,4 @@

conf.instance.register(__file__)

__version__ = "2024.9.21.2"
__version__ = "2024.11.6.1"
6 changes: 6 additions & 0 deletions autoarray/dataset/imaging/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ def from_fits(
psf_path: Optional[Union[Path, str]] = None,
psf_hdu: int = 0,
noise_covariance_matrix: Optional[np.ndarray] = None,
check_noise_map: bool = True,
over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(),
) -> "Imaging":
"""
Expand Down Expand Up @@ -250,6 +251,8 @@ def from_fits(
The hdu the noise map is contained in the .fits file specified by `noise_map_path`.
noise_covariance_matrix
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
Expand Down Expand Up @@ -280,6 +283,7 @@ def from_fits(
noise_map=noise_map,
psf=psf,
noise_covariance_matrix=noise_covariance_matrix,
check_noise_map=check_noise_map,
over_sampling=over_sampling,
)

Expand Down Expand Up @@ -377,6 +381,7 @@ def apply_noise_scaling(self, mask: Mask2D, noise_value: float = 1e8) -> "Imagin
noise_covariance_matrix=self.noise_covariance_matrix,
over_sampling=self.over_sampling,
pad_for_convolver=False,
check_noise_map=False
)

logger.info(
Expand Down Expand Up @@ -429,6 +434,7 @@ def apply_over_sampling(
psf=self.psf,
over_sampling=over_sampling,
pad_for_convolver=False,
check_noise_map=False
)

def output_to_fits(
Expand Down
1 change: 0 additions & 1 deletion autoarray/inversion/pixelization/image_mesh/hilbert.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ def image_plane_mesh_grid_from(
-------
"""

if not mask.is_circular:
raise exc.PixelizationException(
"""
Expand Down
20 changes: 20 additions & 0 deletions autoarray/inversion/plot/inversion_plotters.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def figures_2d_of_pixelization(
reconstructed_image: bool = False,
reconstruction: bool = False,
errors: bool = False,
signal_to_noise_map: bool = False,
regularization_weights: bool = False,
sub_pixels_per_image_pixels: bool = False,
mesh_pixels_per_image_pixels: bool = False,
Expand All @@ -146,6 +147,8 @@ def figures_2d_of_pixelization(
Whether to make a 2D plot (via `imshow` or `fill`) of the mapper's source-plane reconstruction.
errors
Whether to make a 2D plot (via `imshow` or `fill`) of the mapper's source-plane errors.
signal_to_noise_map
Whether to make a 2D plot (via `imshow` or `fill`) of the mapper's source-plane signal-to-noise-map.
sub_pixels_per_image_pixels
Whether to make a 2D plot (via `imshow`) of the number of sub pixels per image pixels in the 2D
data's mask.
Expand Down Expand Up @@ -242,6 +245,23 @@ def figures_2d_of_pixelization(
except TypeError:
pass

if signal_to_noise_map:
try:
signal_to_noise_values = (
self.inversion.reconstruction_dict[mapper_plotter.mapper]
/ self.inversion.errors_dict[mapper_plotter.mapper]
)

mapper_plotter.plot_source_from(
pixel_values=signal_to_noise_values,
auto_labels=AutoLabels(
title="Signal To Noise Map", filename="signal_to_noise_map"
),
)

except TypeError:
pass

# TODO : NEed to understand why this raises an error in voronoi_drawer.

if regularization_weights:
Expand Down
1 change: 1 addition & 0 deletions autoarray/plot/abstract_plotters.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(

self.subplot_figsize = None


def set_title(self, label):
if self.mat_plot_1d is not None:
self.mat_plot_1d.title.manual_label = label
Expand Down
6 changes: 0 additions & 6 deletions autoarray/plot/mat_plot/two_d.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,6 @@ def plot_array(
"a pixel scales attribute."
)

# Hack being used for BELLSABSORB with Tania, remove later and code up automatic method to make it
# so that if a mask is irregular and zooming in creates white edges, that instead it doesnt have the eddge.
# This could just be a matplotlib settings to change the edge color?

# array = array.resized_from(new_shape=(401, 401))

array, extent = self.zoomed_array_and_extent_from(array=array)

ax = None
Expand Down
2 changes: 1 addition & 1 deletion autoarray/structures/decorators/to_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def via_grid_2d_irr(self, result) -> Union[Grid2DIrregular, List[Grid2DIrregular
----------
result
The input result (e.g. of a decorated function) that is converted to an Grid2DIrregular or list of
Grid2DIrregular objects.
`Grid2DIrregular` objects.
"""
if not isinstance(result, list):
return Grid2DIrregular(values=result)
Expand Down
14 changes: 10 additions & 4 deletions autoarray/structures/grids/irregular_2d.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from autoarray.numpy_wrapper import np
import logging
from typing import List, Optional, Tuple, Union

from autoarray.numpy_wrapper import np
from autoarray.abstract_ndarray import AbstractNDArray
from autoarray.geometry.geometry_2d_irregular import Geometry2DIrregular
from autoarray.mask.mask_2d import Mask2D
Expand All @@ -10,6 +11,8 @@
from autoarray.structures.grids import grid_2d_util
from autoarray.geometry import geometry_util

logger = logging.getLogger(__name__)


class Grid2DIrregular(AbstractNDArray):
def __init__(self, values: Union[np.ndarray, List]):
Expand Down Expand Up @@ -351,9 +354,12 @@ def pixel_scale(self) -> float:
if self.pixel_scales[0] == self.pixel_scales[1]:
return self.pixel_scales[0]
else:
raise exc.GridException(
"Cannot return a pixel_scale for a grid where each dimension has a "
"different pixel scale (e.g. pixel_scales[0] != pixel_scales[1])"
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
Expand Down
3 changes: 3 additions & 0 deletions autoarray/structures/triangles/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def __init__(
self.indices = indices
self.vertices = vertices

def __len__(self):
return len(self.triangles)

@property
def area(self) -> float:
"""
Expand Down
171 changes: 171 additions & 0 deletions autoarray/structures/triangles/abstract_coordinate_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
from abc import ABC, abstractmethod

import numpy as np

from autoarray.structures.triangles.abstract import HEIGHT_FACTOR, AbstractTriangles
from autoconf import cached_property


class AbstractCoordinateArray(ABC):
def __init__(
self,
coordinates: np.ndarray,
side_length: float,
x_offset: float = 0.0,
y_offset: float = 0.0,
flipped: bool = False,
):
"""
Represents a set of triangles by integer coordinates.
Parameters
----------
coordinates
Integer x y coordinates for each triangle.
side_length
The side length of the triangles.
flipped
Whether the triangles are flipped upside down.
y_offset
An y_offset to apply to the y coordinates so that up-sampled triangles align.
"""
self.coordinates = coordinates
self.side_length = side_length
self.flipped = flipped

self.scaling_factors = np.array(
[0.5 * side_length, HEIGHT_FACTOR * side_length]
)
self.x_offset = x_offset
self.y_offset = y_offset

@cached_property
def triangles(self) -> np.ndarray:
"""
The vertices of the triangles as an Nx3x2 array.
"""
centres = self.centres
return np.stack(
(
centres
+ self.flip_array
* np.array(
[0.0, 0.5 * self.side_length * HEIGHT_FACTOR],
),
centres
+ self.flip_array
* np.array(
[0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR]
),
centres
+ self.flip_array
* np.array(
[-0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR]
),
),
axis=1,
)

@property
def centres(self) -> np.ndarray:
"""
The centres of the triangles.
"""
return self.scaling_factors * self.coordinates + np.array(
[self.x_offset, self.y_offset]
)

@cached_property
def flip_mask(self) -> np.ndarray:
"""
A mask for the triangles that are flipped.
Every other triangle is flipped so that they tessellate.
"""
mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0
if self.flipped:
mask = ~mask
return mask

@cached_property
@abstractmethod
def flip_array(self) -> np.ndarray:
"""
An array of 1s and -1s to flip the triangles.
"""

def __iter__(self):
return iter(self.triangles)

@cached_property
@abstractmethod
def _vertices_and_indices(self):
pass

@property
def vertices(self) -> np.ndarray:
"""
The unique vertices of the triangles.
"""
return self._vertices_and_indices[0]

@property
def indices(self) -> np.ndarray:
"""
The indices of the vertices of the triangles.
"""
return self._vertices_and_indices[1]

def with_vertices(self, vertices: np.ndarray) -> AbstractTriangles:
"""
Create a new set of triangles with the vertices replaced.
Parameters
----------
vertices
The new vertices to use.
Returns
-------
The new set of triangles with the new vertices.
"""

@classmethod
def for_limits_and_scale(
cls,
x_min: float,
x_max: float,
y_min: float,
y_max: float,
scale: float = 1.0,
**_,
):
coordinates = []
x = x_min
while x < x_max:
y = y_min
while y < y_max:
coordinates.append([x, y])
y += scale
x += scale

x_mean = (x_min + x_max) / 2
y_mean = (y_min + y_max) / 2

return cls(
coordinates=np.array(coordinates),
side_length=scale,
x_offset=x_mean,
y_offset=y_mean,
)

@property
def means(self):
return np.mean(self.triangles, axis=1)

@property
def area(self):
return (3**0.5 / 4 * self.side_length**2) * len(self)

def __len__(self):
return np.count_nonzero(~np.isnan(self.coordinates).any(axis=1))
4 changes: 3 additions & 1 deletion autoarray/structures/triangles/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ def neighborhood(self) -> "ArrayTriangles":
Includes the current triangles and the triangles that share an edge with the current triangles.
"""
unique_vertices, inverse_indices = np.unique(
self._neighborhood_triangles().reshape(-1, 2), axis=0, return_inverse=True
self._neighborhood_triangles().reshape(-1, 2),
axis=0,
return_inverse=True,
)
new_indices = inverse_indices.reshape(-1, 3)

Expand Down
Loading

0 comments on commit 311f7cd

Please sign in to comment.