From beea5fd60a47c16a0c693903c2d54df4881a4035 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 08:00:14 +0100 Subject: [PATCH 01/70] length and precision --- autoarray/structures/triangles/abstract.py | 5 +++++ autoarray/structures/triangles/array.py | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/autoarray/structures/triangles/abstract.py b/autoarray/structures/triangles/abstract.py index a8085810..3fb0dee8 100644 --- a/autoarray/structures/triangles/abstract.py +++ b/autoarray/structures/triangles/abstract.py @@ -13,6 +13,7 @@ def __init__( self, indices, vertices, + triangle_precision: int = 4, **kwargs, ): """ @@ -28,6 +29,10 @@ def __init__( """ self.indices = indices self.vertices = vertices + self.triangle_precision = triangle_precision + + def __len__(self): + return len(self.triangles) @property def area(self) -> float: diff --git a/autoarray/structures/triangles/array.py b/autoarray/structures/triangles/array.py index 0d129dac..eb55775b 100644 --- a/autoarray/structures/triangles/array.py +++ b/autoarray/structures/triangles/array.py @@ -84,7 +84,12 @@ 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 + np.round( + self._neighborhood_triangles().reshape(-1, 2), + self.triangle_precision, + ), + axis=0, + return_inverse=True, ) new_indices = inverse_indices.reshape(-1, 3) From c0aea68e8cea45fcdf7343d4aebd5ccc1fe65eea Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 08:01:00 +0100 Subject: [PATCH 02/70] remove precision rounding --- autoarray/structures/triangles/abstract.py | 2 -- autoarray/structures/triangles/array.py | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/autoarray/structures/triangles/abstract.py b/autoarray/structures/triangles/abstract.py index 3fb0dee8..463d1017 100644 --- a/autoarray/structures/triangles/abstract.py +++ b/autoarray/structures/triangles/abstract.py @@ -13,7 +13,6 @@ def __init__( self, indices, vertices, - triangle_precision: int = 4, **kwargs, ): """ @@ -29,7 +28,6 @@ def __init__( """ self.indices = indices self.vertices = vertices - self.triangle_precision = triangle_precision def __len__(self): return len(self.triangles) diff --git a/autoarray/structures/triangles/array.py b/autoarray/structures/triangles/array.py index eb55775b..be3b2e1e 100644 --- a/autoarray/structures/triangles/array.py +++ b/autoarray/structures/triangles/array.py @@ -84,10 +84,7 @@ 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( - np.round( - self._neighborhood_triangles().reshape(-1, 2), - self.triangle_precision, - ), + self._neighborhood_triangles().reshape(-1, 2), axis=0, return_inverse=True, ) From 27177f570d7ae6bc3d999f9513afa1253411b826 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 11:52:14 +0100 Subject: [PATCH 03/70] trivial example of triangle from coordinates --- .../structures/triangles/coordinate_array.py | 42 +++++++++++++++++++ .../test_coordinate_implementation.py | 32 ++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 autoarray/structures/triangles/coordinate_array.py create mode 100644 test_autoarray/structures/triangles/test_coordinate_implementation.py diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py new file mode 100644 index 00000000..82d684e6 --- /dev/null +++ b/autoarray/structures/triangles/coordinate_array.py @@ -0,0 +1,42 @@ +import numpy as np + +from autoarray.structures.triangles.abstract import HEIGHT_FACTOR + + +class CoordinateArrayTriangles: + def __init__( + self, + coordinates: np.ndarray, + side_length: float, + ): + self.coordinates = coordinates + self.side_length = side_length + + self.scaling_factors = np.array([side_length, HEIGHT_FACTOR * side_length]) + + @property + def triangles(self) -> np.ndarray: + centres = self.centres + return np.concatenate( + ( + centres + + np.array( + [0.0, 0.5 * self.side_length * HEIGHT_FACTOR], + ), + centres + + np.array( + [0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR] + ), + centres + + np.array( + [-0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR] + ), + ) + ) + + @property + def centres(self) -> np.ndarray: + return self.scaling_factors * self.coordinates + + def __iter__(self): + return iter(self.triangles) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py new file mode 100644 index 00000000..15c00dde --- /dev/null +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -0,0 +1,32 @@ +from matplotlib import pyplot as plt +import numpy as np + +from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles + + +def plot(triangles): + plt.figure(figsize=(8, 8)) + for triangle in triangles: + triangle = np.append(triangle, [triangle[0]], axis=0) + plt.plot(triangle[:, 0], triangle[:, 1], "o-", color="black") + + plt.gca().set_aspect("equal", adjustable="box") + plt.show() + + +def test_trivial_triangles(): + array = CoordinateArrayTriangles( + coordinates=np.array([[0, 0]]), + side_length=1.0, + ) + assert np.all(array.centres == np.array([[0, 0]])) + assert np.all( + array.triangles + == [ + [ + [0.0, 0.4330127018922193], + [0.5, -0.4330127018922193], + [-0.5, -0.4330127018922193], + ], + ] + ) From 8bfc837c72e6d4200a40563e5a2a3ce5de627544 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 12:15:38 +0100 Subject: [PATCH 04/70] flip mask; --- .../structures/triangles/coordinate_array.py | 8 ++++++ .../test_coordinate_implementation.py | 26 ++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 82d684e6..b5522f66 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -1,6 +1,7 @@ import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR +from autoconf import cached_property class CoordinateArrayTriangles: @@ -38,5 +39,12 @@ def triangles(self) -> np.ndarray: def centres(self) -> np.ndarray: return self.scaling_factors * self.coordinates + @cached_property + def flip_mask(self): + array = np.ones(self.coordinates.shape[0]) + mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0 + array[mask] = -1 + return array + def __iter__(self): return iter(self.triangles) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 15c00dde..697b3816 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -1,6 +1,7 @@ from matplotlib import pyplot as plt import numpy as np +from autoarray.structures.triangles.abstract import HEIGHT_FACTOR from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles @@ -19,14 +20,33 @@ def test_trivial_triangles(): coordinates=np.array([[0, 0]]), side_length=1.0, ) + assert array.flip_mask == np.array([1]) assert np.all(array.centres == np.array([[0, 0]])) assert np.all( array.triangles == [ [ - [0.0, 0.4330127018922193], - [0.5, -0.4330127018922193], - [-0.5, -0.4330127018922193], + [0.0, HEIGHT_FACTOR / 2], + [0.5, -HEIGHT_FACTOR / 2], + [-0.5, -HEIGHT_FACTOR / 2], + ], + ] + ) + + +def test_upside_down(): + array = CoordinateArrayTriangles( + coordinates=np.array([[1, 0]]), + side_length=1.0, + ) + assert np.all(array.centres == np.array([[1, 0]])) + assert np.all( + array.triangles + == [ + [ + [0.5, -HEIGHT_FACTOR / 2], + [1.0, HEIGHT_FACTOR / 2], + [0.0, HEIGHT_FACTOR / 2], ], ] ) From 37db5a176b06536a029bc1fa47e8a7ba4c3683ce Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 12:20:47 +0100 Subject: [PATCH 05/70] upside down triangle --- autoarray/structures/triangles/coordinate_array.py | 13 +++++++++---- .../triangles/test_coordinate_implementation.py | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index b5522f66..5113df2d 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -13,7 +13,9 @@ def __init__( self.coordinates = coordinates self.side_length = side_length - self.scaling_factors = np.array([side_length, HEIGHT_FACTOR * side_length]) + self.scaling_factors = np.array( + [0.5 * side_length, HEIGHT_FACTOR * side_length] + ) @property def triangles(self) -> np.ndarray: @@ -21,15 +23,18 @@ def triangles(self) -> np.ndarray: return np.concatenate( ( centres - + np.array( + + self.flip_mask + * np.array( [0.0, 0.5 * self.side_length * HEIGHT_FACTOR], ), centres - + np.array( + + self.flip_mask + * np.array( [0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR] ), centres - + np.array( + + self.flip_mask + * np.array( [-0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR] ), ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 697b3816..6cfc4720 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -39,14 +39,14 @@ def test_upside_down(): coordinates=np.array([[1, 0]]), side_length=1.0, ) - assert np.all(array.centres == np.array([[1, 0]])) + assert np.all(array.centres == np.array([[0.5, 0]])) assert np.all( array.triangles == [ [ [0.5, -HEIGHT_FACTOR / 2], - [1.0, HEIGHT_FACTOR / 2], [0.0, HEIGHT_FACTOR / 2], + [1.0, HEIGHT_FACTOR / 2], ], ] ) From 2375e78b4c73a39e0ed7e82dae4522fbe4cee84b Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 12:32:54 +0100 Subject: [PATCH 06/70] testing and tweaking... --- .../structures/triangles/coordinate_array.py | 5 ++-- .../test_coordinate_implementation.py | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 5113df2d..a7268d8a 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -20,7 +20,7 @@ def __init__( @property def triangles(self) -> np.ndarray: centres = self.centres - return np.concatenate( + return np.stack( ( centres + self.flip_mask @@ -37,7 +37,8 @@ def triangles(self) -> np.ndarray: * np.array( [-0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR] ), - ) + ), + axis=1, ) @property diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 6cfc4720..0aecbff1 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -15,6 +15,29 @@ def plot(triangles): plt.show() +def test_two(): + array = CoordinateArrayTriangles( + coordinates=np.array([[0, 0], [1, 0]]), + side_length=1.0, + ) + assert np.all(array.centres == np.array([[0, 0], [0.5, 0]])) + assert np.all( + array.triangles + == [ + [ + [0.0, HEIGHT_FACTOR / 2], + [0.5, -HEIGHT_FACTOR / 2], + [-0.5, -HEIGHT_FACTOR / 2], + ], + [ + [0.5, -HEIGHT_FACTOR / 2], + [0.0, HEIGHT_FACTOR / 2], + [1.0, HEIGHT_FACTOR / 2], + ], + ] + ) + + def test_trivial_triangles(): array = CoordinateArrayTriangles( coordinates=np.array([[0, 0]]), @@ -33,6 +56,8 @@ def test_trivial_triangles(): ] ) + plot(array) + def test_upside_down(): array = CoordinateArrayTriangles( From b35f3d4a03529bb7c5cbcd348435e57fac6efc02 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 12:48:41 +0100 Subject: [PATCH 07/70] fix flip mask broadcasting --- autoarray/structures/triangles/coordinate_array.py | 2 +- .../structures/triangles/test_coordinate_implementation.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index a7268d8a..bb7ba5e9 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -50,7 +50,7 @@ def flip_mask(self): array = np.ones(self.coordinates.shape[0]) mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0 array[mask] = -1 - return array + return array[:, np.newaxis] def __iter__(self): return iter(self.triangles) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 0aecbff1..1b9e4c05 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -36,6 +36,7 @@ def test_two(): ], ] ) + plot(array) def test_trivial_triangles(): @@ -56,8 +57,6 @@ def test_trivial_triangles(): ] ) - plot(array) - def test_upside_down(): array = CoordinateArrayTriangles( From c5e50db30eff9ef44f091cc59319b6e302728d7e Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 25 Oct 2024 14:41:00 +0100 Subject: [PATCH 08/70] added signal to noise map to IvnrsionPlotter --- autoarray/config/visualize/include.yaml | 2 +- autoarray/inversion/plot/inversion_plotters.py | 17 +++++++++++++++++ autoarray/mask/abstract_mask.py | 12 ++++++++---- autoarray/structures/decorators/to_grid.py | 2 +- autoarray/structures/grids/irregular_2d.py | 13 ++++++++----- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/autoarray/config/visualize/include.yaml b/autoarray/config/visualize/include.yaml index 270b6b2f..f010d9f8 100644 --- a/autoarray/config/visualize/include.yaml +++ b/autoarray/config/visualize/include.yaml @@ -7,7 +7,7 @@ include_1d: mask: false # Include a Mask ? origin: false # Include the (x,) origin of the data's coordinate system ? include_2d: - border: true # Include the border of the mask (all pixels on the outside of the mask) ? + border: false # Include the border of the mask (all pixels on the outside of the mask) ? grid: false # Include the data's 2D grid of (y,x) coordinates ? mapper_image_plane_mesh_grid: false # For an Inversion, include the pixel centres computed in the image-plane / data frame? mapper_source_plane_data_grid: false # For an Inversion, include the centres of the image-plane grid mapped to the source-plane / frame in source-plane figures? diff --git a/autoarray/inversion/plot/inversion_plotters.py b/autoarray/inversion/plot/inversion_plotters.py index 000c9879..4ed29a0b 100644 --- a/autoarray/inversion/plot/inversion_plotters.py +++ b/autoarray/inversion/plot/inversion_plotters.py @@ -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, @@ -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. @@ -243,6 +246,20 @@ 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: diff --git a/autoarray/mask/abstract_mask.py b/autoarray/mask/abstract_mask.py index 76994b3e..c1e60bc6 100644 --- a/autoarray/mask/abstract_mask.py +++ b/autoarray/mask/abstract_mask.py @@ -75,10 +75,14 @@ def pixel_scale(self) -> float: """ for pixel_scale in self.pixel_scales: if abs(pixel_scale - self.pixel_scales[0]) > 1.0e-8: - raise exc.MaskException( - "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 Mask has different pixel scales in each dimensions, which are {self.pixel_scales}. + + This is not expected, and will lead to unexpected behaviour in the grid and mask classes. + The code will continue to run, but you should check that the pixel scales are as you expect and + that associated data structures (e.g. grids) are behaving as you expect. + """) return self.pixel_scales[0] diff --git a/autoarray/structures/decorators/to_grid.py b/autoarray/structures/decorators/to_grid.py index 43a025d3..3b70b1b2 100644 --- a/autoarray/structures/decorators/to_grid.py +++ b/autoarray/structures/decorators/to_grid.py @@ -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) diff --git a/autoarray/structures/grids/irregular_2d.py b/autoarray/structures/grids/irregular_2d.py index 25bfd09f..fba81187 100644 --- a/autoarray/structures/grids/irregular_2d.py +++ b/autoarray/structures/grids/irregular_2d.py @@ -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 @@ -10,6 +11,7 @@ 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]): @@ -351,10 +353,11 @@ 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 def from_grid_sparse_uniform_upscale( From 67c1c8b70a33ce642e910a602edd932f54db9fcb Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 14:42:44 +0100 Subject: [PATCH 09/70] fudge to get first upsampled triangle into the right place --- .../structures/triangles/coordinate_array.py | 24 ++++++++- .../test_coordinate_implementation.py | 49 +++++++++++++------ 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index bb7ba5e9..7c7536f9 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -9,13 +9,17 @@ def __init__( self, coordinates: np.ndarray, side_length: float, + flipped: bool = False, + offset: float = 0.0, ): 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.offset = offset @property def triangles(self) -> np.ndarray: @@ -43,14 +47,32 @@ def triangles(self) -> np.ndarray: @property def centres(self) -> np.ndarray: - return self.scaling_factors * self.coordinates + return self.scaling_factors * self.coordinates + np.array([0.0, self.offset]) @cached_property def flip_mask(self): array = np.ones(self.coordinates.shape[0]) mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0 array[mask] = -1 + if self.flipped: + array *= -1 + return array[:, np.newaxis] def __iter__(self): return iter(self.triangles) + + def up_sample(self): + return CoordinateArrayTriangles( + coordinates=np.vstack( + ( + 2 * self.coordinates, + # 2 * self.coordinates + np.array([1, 0]), + # 2 * self.coordinates + np.array([-1, 0]), + # 2 * self.coordinates + np.array([0, 1]), + ) + ), + side_length=self.side_length / 2, + flipped=not self.flipped, + offset=-0.25 * HEIGHT_FACTOR * self.side_length, + ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 1b9e4c05..3608ecf7 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -1,3 +1,4 @@ +import pytest from matplotlib import pyplot as plt import numpy as np @@ -5,24 +6,32 @@ from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles -def plot(triangles): +@pytest.fixture +def plot(): plt.figure(figsize=(8, 8)) - for triangle in triangles: - triangle = np.append(triangle, [triangle[0]], axis=0) - plt.plot(triangle[:, 0], triangle[:, 1], "o-", color="black") + def plot(triangles, color="black"): + for triangle in triangles: + triangle = np.append(triangle, [triangle[0]], axis=0) + plt.plot(triangle[:, 0], triangle[:, 1], "o-", color=color) + + yield plot plt.gca().set_aspect("equal", adjustable="box") plt.show() -def test_two(): - array = CoordinateArrayTriangles( +@pytest.fixture +def two_triangles(): + return CoordinateArrayTriangles( coordinates=np.array([[0, 0], [1, 0]]), side_length=1.0, ) - assert np.all(array.centres == np.array([[0, 0], [0.5, 0]])) + + +def test_two(two_triangles): + assert np.all(two_triangles.centres == np.array([[0, 0], [0.5, 0]])) assert np.all( - array.triangles + two_triangles.triangles == [ [ [0.0, HEIGHT_FACTOR / 2], @@ -36,18 +45,21 @@ def test_two(): ], ] ) - plot(array) -def test_trivial_triangles(): - array = CoordinateArrayTriangles( +@pytest.fixture +def one_triangle(): + return CoordinateArrayTriangles( coordinates=np.array([[0, 0]]), side_length=1.0, ) - assert array.flip_mask == np.array([1]) - assert np.all(array.centres == np.array([[0, 0]])) + + +def test_trivial_triangles(one_triangle): + assert one_triangle.flip_mask == np.array([1]) + assert np.all(one_triangle.centres == np.array([[0, 0]])) assert np.all( - array.triangles + one_triangle.triangles == [ [ [0.0, HEIGHT_FACTOR / 2], @@ -74,3 +86,12 @@ def test_upside_down(): ], ] ) + + +def test_up_sample(one_triangle, plot): + plot(one_triangle) + + up_sampled = one_triangle.up_sample() + assert up_sampled.side_length == 0.5 + + plot(up_sampled, color="red") From 93223e990fd91ed5879fa6be08f162bf4351bab7 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 14:44:06 +0100 Subject: [PATCH 10/70] one full triangle --- autoarray/structures/triangles/coordinate_array.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 7c7536f9..8b51b381 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -67,9 +67,9 @@ def up_sample(self): coordinates=np.vstack( ( 2 * self.coordinates, - # 2 * self.coordinates + np.array([1, 0]), - # 2 * self.coordinates + np.array([-1, 0]), - # 2 * self.coordinates + np.array([0, 1]), + 2 * self.coordinates + np.array([1, 0]), + 2 * self.coordinates + np.array([-1, 0]), + 2 * self.coordinates + np.array([0, 1]), ) ), side_length=self.side_length / 2, From a82b2719577b26aa38c3fb1af1a96e70930fdd83 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 14:45:13 +0100 Subject: [PATCH 11/70] assertion; --- .../triangles/test_coordinate_implementation.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 3608ecf7..07703ae8 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -93,5 +93,14 @@ def test_up_sample(one_triangle, plot): up_sampled = one_triangle.up_sample() assert up_sampled.side_length == 0.5 + assert np.all( + up_sampled.triangles + == [ + [[0.0, -0.4330127018922193], [-0.25, 0.0], [0.25, 0.0]], + [[0.25, 0.0], [0.5, -0.4330127018922193], [0.0, -0.4330127018922193]], + [[-0.25, 0.0], [0.0, -0.4330127018922193], [-0.5, -0.4330127018922193]], + [[0.0, 0.4330127018922193], [0.25, 0.0], [-0.25, 0.0]], + ] + ) plot(up_sampled, color="red") From 769b3483e339332ed2fa335c173bdc96fc2dad0a Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 25 Oct 2024 14:45:44 +0100 Subject: [PATCH 12/70] unit test --- test_autoarray/inversion/plot/test_inversion_plotters.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_autoarray/inversion/plot/test_inversion_plotters.py b/test_autoarray/inversion/plot/test_inversion_plotters.py index 1afe6d91..11dee829 100644 --- a/test_autoarray/inversion/plot/test_inversion_plotters.py +++ b/test_autoarray/inversion/plot/test_inversion_plotters.py @@ -67,6 +67,7 @@ def test__individual_attributes_are_output_for_all_mappers( mesh_pixels_per_image_pixels=True, image_pixels_per_mesh_pixel=True, errors=True, + signal_to_noise_map=True, regularization_weights=True, ) @@ -76,6 +77,7 @@ def test__individual_attributes_are_output_for_all_mappers( assert path.join(plot_path, "mesh_pixels_per_image_pixels.png") in plot_patch.paths assert path.join(plot_path, "image_pixels_per_mesh_pixel.png") in plot_patch.paths assert path.join(plot_path, "errors.png") in plot_patch.paths + assert path.join(plot_path, "signal_to_noise_map.png") in plot_patch.paths assert path.join(plot_path, "regularization_weights.png") in plot_patch.paths plot_patch.paths = [] From 8c4b9eb52b45c5fb0464e25baee8e9709e94daa9 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 15:15:55 +0100 Subject: [PATCH 13/70] got upside down working... --- .../structures/triangles/coordinate_array.py | 42 +++++++++++++------ .../test_coordinate_implementation.py | 32 ++++++++++---- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 8b51b381..f476d02e 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -27,17 +27,17 @@ def triangles(self) -> np.ndarray: return np.stack( ( centres - + self.flip_mask + + self.flip_array * np.array( [0.0, 0.5 * self.side_length * HEIGHT_FACTOR], ), centres - + self.flip_mask + + self.flip_array * np.array( [0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR] ), centres - + self.flip_mask + + self.flip_array * np.array( [-0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR] ), @@ -51,9 +51,12 @@ def centres(self) -> np.ndarray: @cached_property def flip_mask(self): + return (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0 + + @cached_property + def flip_array(self): array = np.ones(self.coordinates.shape[0]) - mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0 - array[mask] = -1 + array[self.flip_mask] = -1 if self.flipped: array *= -1 @@ -63,15 +66,28 @@ def __iter__(self): return iter(self.triangles) def up_sample(self): + new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) + n_normal = np.sum(~self.flip_mask) + + new_coordinates[:n_normal] = np.vstack( + ( + 2 * self.coordinates[~self.flip_mask], + 2 * self.coordinates[~self.flip_mask] + np.array([1, 0]), + 2 * self.coordinates[~self.flip_mask] + np.array([-1, 0]), + 2 * self.coordinates[~self.flip_mask] + np.array([0, 1]), + ) + ) + new_coordinates[n_normal:] = np.vstack( + ( + 2 * self.coordinates[self.flip_mask], + 2 * self.coordinates[self.flip_mask] + np.array([1, 1]), + 2 * self.coordinates[self.flip_mask] + np.array([-1, 1]), + 2 * self.coordinates[self.flip_mask] + np.array([0, 1]), + ) + ) + return CoordinateArrayTriangles( - coordinates=np.vstack( - ( - 2 * self.coordinates, - 2 * self.coordinates + np.array([1, 0]), - 2 * self.coordinates + np.array([-1, 0]), - 2 * self.coordinates + np.array([0, 1]), - ) - ), + coordinates=new_coordinates, side_length=self.side_length / 2, flipped=not self.flipped, offset=-0.25 * HEIGHT_FACTOR * self.side_length, diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 07703ae8..2e1e4a15 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -56,7 +56,7 @@ def one_triangle(): def test_trivial_triangles(one_triangle): - assert one_triangle.flip_mask == np.array([1]) + assert one_triangle.flip_array == np.array([1]) assert np.all(one_triangle.centres == np.array([[0, 0]])) assert np.all( one_triangle.triangles @@ -70,14 +70,18 @@ def test_trivial_triangles(one_triangle): ) -def test_upside_down(): - array = CoordinateArrayTriangles( +@pytest.fixture +def upside_down(): + return CoordinateArrayTriangles( coordinates=np.array([[1, 0]]), side_length=1.0, ) - assert np.all(array.centres == np.array([[0.5, 0]])) + + +def test_upside_down(upside_down): + assert np.all(upside_down.centres == np.array([[0.5, 0]])) assert np.all( - array.triangles + upside_down.triangles == [ [ [0.5, -HEIGHT_FACTOR / 2], @@ -88,9 +92,7 @@ def test_upside_down(): ) -def test_up_sample(one_triangle, plot): - plot(one_triangle) - +def test_up_sample(one_triangle): up_sampled = one_triangle.up_sample() assert up_sampled.side_length == 0.5 assert np.all( @@ -103,4 +105,16 @@ def test_up_sample(one_triangle, plot): ] ) - plot(up_sampled, color="red") + +def test_up_sample_upside_down(upside_down): + up_sampled = upside_down.up_sample() + assert up_sampled.side_length == 0.5 + assert np.all( + up_sampled.triangles + == [ + [[0.5, -0.4330127018922193], [0.25, 0.0], [0.75, 0.0]], + [[0.75, 0.0], [0.5, 0.4330127018922193], [1.0, 0.4330127018922193]], + [[0.25, 0.0], [0.0, 0.4330127018922193], [0.5, 0.4330127018922193]], + [[0.5, 0.4330127018922193], [0.75, 0.0], [0.25, 0.0]], + ] + ) From 0ee8b247b96459da0b02d2dfca739d66c19c2c96 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 15:25:55 +0100 Subject: [PATCH 14/70] fix --- autoarray/structures/triangles/coordinate_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index f476d02e..79c12f77 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -67,7 +67,7 @@ def __iter__(self): def up_sample(self): new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) - n_normal = np.sum(~self.flip_mask) + n_normal = 4 * np.sum(~self.flip_mask) new_coordinates[:n_normal] = np.vstack( ( From 0e6d4b4f55ba40ae5f11d09233644a62fb2de28a Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 15:51:23 +0100 Subject: [PATCH 15/70] uh --- .../structures/triangles/coordinate_array.py | 11 ++++++----- .../triangles/test_coordinate_implementation.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 79c12f77..2e72b4e4 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -51,14 +51,15 @@ def centres(self) -> np.ndarray: @cached_property def flip_mask(self): - return (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0 + mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0 + if self.flipped: + mask = ~mask + return mask @cached_property def flip_array(self): array = np.ones(self.coordinates.shape[0]) array[self.flip_mask] = -1 - if self.flipped: - array *= -1 return array[:, np.newaxis] @@ -89,6 +90,6 @@ def up_sample(self): return CoordinateArrayTriangles( coordinates=new_coordinates, side_length=self.side_length / 2, - flipped=not self.flipped, - offset=-0.25 * HEIGHT_FACTOR * self.side_length, + flipped=True, + offset=self.offset + -0.25 * HEIGHT_FACTOR * self.side_length, ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 2e1e4a15..08083fdd 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -70,6 +70,15 @@ def test_trivial_triangles(one_triangle): ) +def test_above(plot): + triangles = CoordinateArrayTriangles( + coordinates=np.array([[0, 1]]), + side_length=1.0, + ) + plot(triangles) + plot(triangles.up_sample(), color="red") + + @pytest.fixture def upside_down(): return CoordinateArrayTriangles( @@ -118,3 +127,11 @@ def test_up_sample_upside_down(upside_down): [[0.5, 0.4330127018922193], [0.75, 0.0], [0.25, 0.0]], ] ) + + +def test_up_sample_twice(one_triangle, plot): + plot(one_triangle) + up_sampled = one_triangle.up_sample() + plot(up_sampled, color="red") + up_sampled = up_sampled.up_sample() + plot(up_sampled, color="green") From c4db236c8de7f2f65980a48dfb6e662eb71e16a9 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 15:52:34 +0100 Subject: [PATCH 16/70] pretty plot --- .../triangles/test_coordinate_implementation.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 08083fdd..5953aa88 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -131,7 +131,9 @@ def test_up_sample_upside_down(upside_down): def test_up_sample_twice(one_triangle, plot): plot(one_triangle) - up_sampled = one_triangle.up_sample() - plot(up_sampled, color="red") - up_sampled = up_sampled.up_sample() - plot(up_sampled, color="green") + one = one_triangle.up_sample() + two = one.up_sample() + three = two.up_sample() + plot(three, color="blue") + plot(two, color="green") + plot(one, color="red") From 40b1b097b8e17ccebdb8638c92058841627f4dca Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 16:19:33 +0100 Subject: [PATCH 17/70] neighborhood --- .../structures/triangles/coordinate_array.py | 27 ++++++++++++++++++ .../test_coordinate_implementation.py | 28 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 2e72b4e4..abcb64f1 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -93,3 +93,30 @@ def up_sample(self): flipped=True, offset=self.offset + -0.25 * HEIGHT_FACTOR * self.side_length, ) + + def neighborhood(self): + new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) + n_normal = 4 * np.sum(~self.flip_mask) + + new_coordinates[:n_normal] = np.vstack( + ( + self.coordinates[~self.flip_mask], + self.coordinates[~self.flip_mask] + np.array([1, 0]), + self.coordinates[~self.flip_mask] + np.array([-1, 0]), + self.coordinates[~self.flip_mask] + np.array([0, -1]), + ) + ) + new_coordinates[n_normal:] = np.vstack( + ( + self.coordinates[self.flip_mask], + self.coordinates[self.flip_mask] + np.array([1, 1]), + self.coordinates[self.flip_mask] + np.array([-1, 1]), + self.coordinates[self.flip_mask] + np.array([0, 1]), + ) + ) + return CoordinateArrayTriangles( + coordinates=new_coordinates, + side_length=self.side_length, + flipped=self.flipped, + offset=self.offset, + ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 5953aa88..3fe35ab2 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -137,3 +137,31 @@ def test_up_sample_twice(one_triangle, plot): plot(three, color="blue") plot(two, color="green") plot(one, color="red") + + +def test_neighborhood(one_triangle): + assert np.all( + one_triangle.neighborhood().triangles + == [ + [ + [0.0, 0.4330127018922193], + [0.5, -0.4330127018922193], + [-0.5, -0.4330127018922193], + ], + [ + [0.5, -0.4330127018922193], + [0.0, 0.4330127018922193], + [1.0, 0.4330127018922193], + ], + [ + [-0.5, -0.4330127018922193], + [-1.0, 0.4330127018922193], + [0.0, 0.4330127018922193], + ], + [ + [0.0, -1.299038105676658], + [-0.5, -0.4330127018922193], + [0.5, -0.4330127018922193], + ], + ] + ) From 42c90db9c9d1b670900724d8f8d1f0e628f6f3bf Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 16:26:50 +0100 Subject: [PATCH 18/70] test against upside down neighborhood --- .../test_coordinate_implementation.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 3fe35ab2..c6b4b1b2 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -165,3 +165,31 @@ def test_neighborhood(one_triangle): ], ] ) + + +def test_upside_down_neighborhood(upside_down): + assert np.all( + upside_down.neighborhood().triangles.tolist() + == [ + [ + [0.5, -0.4330127018922193], + [0.0, 0.4330127018922193], + [1.0, 0.4330127018922193], + ], + [ + [1.0, 0.4330127018922193], + [0.5, 1.299038105676658], + [1.5, 1.299038105676658], + ], + [ + [0.0, 0.4330127018922193], + [-0.5, 1.299038105676658], + [0.5, 1.299038105676658], + ], + [ + [0.5, 1.299038105676658], + [1.0, 0.4330127018922193], + [0.0, 0.4330127018922193], + ], + ] + ) From 2a483a77ba6275c277526f3f4659d3d6e7333bd6 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 16:35:58 +0100 Subject: [PATCH 19/70] fix upside down neigborhood --- autoarray/structures/triangles/coordinate_array.py | 4 ++-- .../triangles/test_coordinate_implementation.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index abcb64f1..48304e95 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -109,8 +109,8 @@ def neighborhood(self): new_coordinates[n_normal:] = np.vstack( ( self.coordinates[self.flip_mask], - self.coordinates[self.flip_mask] + np.array([1, 1]), - self.coordinates[self.flip_mask] + np.array([-1, 1]), + self.coordinates[self.flip_mask] + np.array([1, 0]), + self.coordinates[self.flip_mask] + np.array([-1, 0]), self.coordinates[self.flip_mask] + np.array([0, 1]), ) ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index c6b4b1b2..1e77d4dc 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -178,13 +178,13 @@ def test_upside_down_neighborhood(upside_down): ], [ [1.0, 0.4330127018922193], - [0.5, 1.299038105676658], - [1.5, 1.299038105676658], + [1.5, -0.4330127018922193], + [0.5, -0.4330127018922193], ], [ [0.0, 0.4330127018922193], - [-0.5, 1.299038105676658], - [0.5, 1.299038105676658], + [0.5, -0.4330127018922193], + [-0.5, -0.4330127018922193], ], [ [0.5, 1.299038105676658], @@ -193,3 +193,7 @@ def test_upside_down_neighborhood(upside_down): ], ] ) + + +def test_both_neighborhood(two_triangles, plot): + plot(two_triangles.neighborhood()) From 582dc6f8b81efd2bdb488bb1119e207078398c53 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 16:42:46 +0100 Subject: [PATCH 20/70] update tests --- .../structures/triangles/coordinate_array.py | 2 +- .../test_coordinate_implementation.py | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 48304e95..09e4e7af 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -115,7 +115,7 @@ def neighborhood(self): ) ) return CoordinateArrayTriangles( - coordinates=new_coordinates, + coordinates=np.unique(new_coordinates, axis=0), side_length=self.side_length, flipped=self.flipped, offset=self.offset, diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 1e77d4dc..69628e52 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -144,24 +144,24 @@ def test_neighborhood(one_triangle): one_triangle.neighborhood().triangles == [ [ - [0.0, 0.4330127018922193], - [0.5, -0.4330127018922193], [-0.5, -0.4330127018922193], + [-1.0, 0.4330127018922193], + [0.0, 0.4330127018922193], ], [ + [0.0, -1.299038105676658], + [-0.5, -0.4330127018922193], [0.5, -0.4330127018922193], - [0.0, 0.4330127018922193], - [1.0, 0.4330127018922193], ], [ - [-0.5, -0.4330127018922193], - [-1.0, 0.4330127018922193], [0.0, 0.4330127018922193], + [0.5, -0.4330127018922193], + [-0.5, -0.4330127018922193], ], [ - [0.0, -1.299038105676658], - [-0.5, -0.4330127018922193], [0.5, -0.4330127018922193], + [0.0, 0.4330127018922193], + [1.0, 0.4330127018922193], ], ] ) @@ -169,28 +169,28 @@ def test_neighborhood(one_triangle): def test_upside_down_neighborhood(upside_down): assert np.all( - upside_down.neighborhood().triangles.tolist() + upside_down.neighborhood().triangles == [ [ - [0.5, -0.4330127018922193], [0.0, 0.4330127018922193], - [1.0, 0.4330127018922193], - ], - [ - [1.0, 0.4330127018922193], - [1.5, -0.4330127018922193], [0.5, -0.4330127018922193], + [-0.5, -0.4330127018922193], ], [ - [0.0, 0.4330127018922193], [0.5, -0.4330127018922193], - [-0.5, -0.4330127018922193], + [0.0, 0.4330127018922193], + [1.0, 0.4330127018922193], ], [ [0.5, 1.299038105676658], [1.0, 0.4330127018922193], [0.0, 0.4330127018922193], ], + [ + [1.0, 0.4330127018922193], + [1.5, -0.4330127018922193], + [0.5, -0.4330127018922193], + ], ] ) From 9bba55248cee6ac0de9402979f284d0a2d055a01 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 16:43:21 +0100 Subject: [PATCH 21/70] remove plotting test --- .../structures/triangles/test_coordinate_implementation.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 69628e52..a2022838 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -193,7 +193,3 @@ def test_upside_down_neighborhood(upside_down): ], ] ) - - -def test_both_neighborhood(two_triangles, plot): - plot(two_triangles.neighborhood()) From 3fcd83a31e556aca9fa8f2aa32d48b258d459818 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 25 Oct 2024 16:45:53 +0100 Subject: [PATCH 22/70] docs --- .../structures/triangles/coordinate_array.py | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 09e4e7af..b6518874 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -12,6 +12,20 @@ def __init__( flipped: bool = False, offset: float = 0.0, ): + """ + 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. + offset + An offset to apply to the y coordinates so that up-sampled triangles align. + """ self.coordinates = coordinates self.side_length = side_length self.flipped = flipped @@ -23,6 +37,9 @@ def __init__( @property def triangles(self) -> np.ndarray: + """ + The vertices of the triangles as an Nx3x2 array. + """ centres = self.centres return np.stack( ( @@ -47,17 +64,28 @@ def triangles(self) -> np.ndarray: @property def centres(self) -> np.ndarray: + """ + The centres of the triangles. + """ return self.scaling_factors * self.coordinates + np.array([0.0, self.offset]) @cached_property - def flip_mask(self): + 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 - def flip_array(self): + def flip_array(self) -> np.ndarray: + """ + An array of 1s and -1s to flip the triangles. + """ array = np.ones(self.coordinates.shape[0]) array[self.flip_mask] = -1 @@ -66,7 +94,10 @@ def flip_array(self): def __iter__(self): return iter(self.triangles) - def up_sample(self): + def up_sample(self) -> "CoordinateArrayTriangles": + """ + Up-sample the triangles by adding a new vertex at the midpoint of each edge. + """ new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) n_normal = 4 * np.sum(~self.flip_mask) @@ -94,7 +125,12 @@ def up_sample(self): offset=self.offset + -0.25 * HEIGHT_FACTOR * self.side_length, ) - def neighborhood(self): + def neighborhood(self) -> "CoordinateArrayTriangles": + """ + Create a new set of triangles that are the neighborhood of the current triangles. + + Ensures that the new triangles are unique. + """ new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) n_normal = 4 * np.sum(~self.flip_mask) From f482e4e0736fc9b301243a7e96ca11dcb4f8c384 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 28 Oct 2024 20:42:30 +0000 Subject: [PATCH 23/70] black --- autoarray/inversion/plot/inversion_plotters.py | 11 +++++++---- autoarray/mask/abstract_mask.py | 7 ++++--- autoarray/structures/grids/irregular_2d.py | 7 +++++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/autoarray/inversion/plot/inversion_plotters.py b/autoarray/inversion/plot/inversion_plotters.py index 4ed29a0b..14d8f612 100644 --- a/autoarray/inversion/plot/inversion_plotters.py +++ b/autoarray/inversion/plot/inversion_plotters.py @@ -247,14 +247,17 @@ def figures_2d_of_pixelization( 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] + 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"), + auto_labels=AutoLabels( + title="Signal To Noise Map", filename="signal_to_noise_map" + ), ) except TypeError: diff --git a/autoarray/mask/abstract_mask.py b/autoarray/mask/abstract_mask.py index c1e60bc6..e2867c2c 100644 --- a/autoarray/mask/abstract_mask.py +++ b/autoarray/mask/abstract_mask.py @@ -75,14 +75,15 @@ def pixel_scale(self) -> float: """ for pixel_scale in self.pixel_scales: if abs(pixel_scale - self.pixel_scales[0]) > 1.0e-8: - - logger.warning(f""" + logger.warning( + f""" The Mask has different pixel scales in each dimensions, which are {self.pixel_scales}. This is not expected, and will lead to unexpected behaviour in the grid and mask classes. The code will continue to run, but you should check that the pixel scales are as you expect and that associated data structures (e.g. grids) are behaving as you expect. - """) + """ + ) return self.pixel_scales[0] diff --git a/autoarray/structures/grids/irregular_2d.py b/autoarray/structures/grids/irregular_2d.py index fba81187..52003115 100644 --- a/autoarray/structures/grids/irregular_2d.py +++ b/autoarray/structures/grids/irregular_2d.py @@ -13,6 +13,7 @@ logger = logging.getLogger(__name__) + class Grid2DIrregular(AbstractNDArray): def __init__(self, values: Union[np.ndarray, List]): """ @@ -353,11 +354,13 @@ def pixel_scale(self) -> float: if self.pixel_scales[0] == self.pixel_scales[1]: return self.pixel_scales[0] else: - logger.warning(f""" + 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( From 6ab7d3e480cc35b647726478e93ec2731ec92c91 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 28 Oct 2024 20:44:09 +0000 Subject: [PATCH 24/70] check noise --- autoarray/dataset/imaging/dataset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 3762a607..99deeb39 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -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": """ @@ -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 @@ -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, ) From 0cd96e2fb7d53083862e689afbedaca5f7a37ad4 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 30 Oct 2024 10:33:26 +0000 Subject: [PATCH 25/70] fix unitt est --- test_autoarray/inversion/inversion/test_mapper_valued.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test_autoarray/inversion/inversion/test_mapper_valued.py b/test_autoarray/inversion/inversion/test_mapper_valued.py index 235e0d56..ec4e916d 100644 --- a/test_autoarray/inversion/inversion/test_mapper_valued.py +++ b/test_autoarray/inversion/inversion/test_mapper_valued.py @@ -196,20 +196,20 @@ def test__magnification_via_interpolation_from(): ) mapper = aa.m.MockMapper( - parameters=3, + parameters=4, mask=mask, interpolated_array=magnification, - mapping_matrix=np.ones((4, 3)), + mapping_matrix=np.ones((4, 4)), ) mapper_valued = aa.MapperValued(values=np.array(magnification), mapper=mapper) magnification = mapper_valued.magnification_via_interpolation_from() - assert magnification == pytest.approx(3.6666666666666665, 1.0e-4) + assert magnification == pytest.approx(4.0, 1.0e-4) magnification = mapper_valued.magnification_via_interpolation_from( shape_native=(3, 3), extent=(-1.0, 1.0, -1.0, 1.0) ) - assert magnification == pytest.approx(3.6666666666666665, 1.0e-4) + assert magnification == pytest.approx(4.0, 1.0e-4) From 5e9e2638884cbfb7c97c642e488041163874d8f5 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Thu, 31 Oct 2024 20:53:35 +0000 Subject: [PATCH 26/70] check noise mao --- autoarray/dataset/imaging/dataset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autoarray/dataset/imaging/dataset.py b/autoarray/dataset/imaging/dataset.py index 99deeb39..85bc3faa 100644 --- a/autoarray/dataset/imaging/dataset.py +++ b/autoarray/dataset/imaging/dataset.py @@ -381,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( @@ -433,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( From 84ce1c1a2ccbd9e49592345ab5b874eddb46d525 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 07:24:03 +0000 Subject: [PATCH 27/70] replaced plot with assert --- .../test_coordinate_implementation.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index a2022838..eb3574be 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -77,6 +77,31 @@ def test_above(plot): ) plot(triangles) plot(triangles.up_sample(), color="red") + assert np.all( + triangles.up_sample().triangles + == [ + [ + [0.0, 0.43301270189221935], + [-0.25, 0.8660254037844386], + [0.25, 0.8660254037844386], + ], + [ + [0.25, 0.8660254037844388], + [0.0, 1.299038105676658], + [0.5, 1.299038105676658], + ], + [ + [-0.25, 0.8660254037844388], + [-0.5, 1.299038105676658], + [0.0, 1.299038105676658], + ], + [ + [0.0, 1.299038105676658], + [0.25, 0.8660254037844388], + [-0.25, 0.8660254037844388], + ], + ] + ) @pytest.fixture From c2fd7be188d1cdd0a982313bb1e95ef7beb788b1 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 09:12:07 +0000 Subject: [PATCH 28/70] simple vertices --- .../structures/triangles/coordinate_array.py | 4 ++++ .../test_coordinate_implementation.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index b6518874..dd947abd 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -156,3 +156,7 @@ def neighborhood(self) -> "CoordinateArrayTriangles": flipped=self.flipped, offset=self.offset, ) + + @property + def vertices(self): + return self.triangles.reshape((-1, 2)) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index eb3574be..739da7ec 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -218,3 +218,21 @@ def test_upside_down_neighborhood(upside_down): ], ] ) + + +def test_complicated(plot, one_triangle): + triangles = one_triangle.neighborhood().neighborhood() + up_sampled = triangles.up_sample() + plot(up_sampled, color="red") + plot(triangles) + + +def test_vertices(one_triangle): + assert np.all( + one_triangle.vertices + == [ + [0.0, HEIGHT_FACTOR / 2], + [0.5, -HEIGHT_FACTOR / 2], + [-0.5, -HEIGHT_FACTOR / 2], + ] + ) From 2c0bcbb84c5ab52062bf6471924ee69ca472065c Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 09:14:37 +0000 Subject: [PATCH 29/70] unique vertices --- autoarray/structures/triangles/coordinate_array.py | 2 +- .../triangles/test_coordinate_implementation.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index dd947abd..d7490c93 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -159,4 +159,4 @@ def neighborhood(self) -> "CoordinateArrayTriangles": @property def vertices(self): - return self.triangles.reshape((-1, 2)) + return np.unique(self.triangles.reshape((-1, 2)), axis=0) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 739da7ec..b7903f62 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -236,3 +236,17 @@ def test_vertices(one_triangle): [-0.5, -HEIGHT_FACTOR / 2], ] ) + + +def test_upsampled_vertices(one_triangle): + assert np.all( + one_triangle.up_sample().vertices + == [ + [-0.5, -0.4330127018922193], + [-0.25, 0.0], + [0.0, -0.4330127018922193], + [0.0, 0.4330127018922193], + [0.25, 0.0], + [0.5, -0.4330127018922193], + ] + ) From 957b313a6f1751793d5bb790685bf425b9eb4a87 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 09:18:29 +0000 Subject: [PATCH 30/70] small changes --- autoarray/structures/triangles/array.py | 2 -- .../structures/triangles/test_coordinate_implementation.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/autoarray/structures/triangles/array.py b/autoarray/structures/triangles/array.py index be3b2e1e..4bf03f9e 100644 --- a/autoarray/structures/triangles/array.py +++ b/autoarray/structures/triangles/array.py @@ -1,5 +1,3 @@ -from typing import Tuple - import numpy as np from autoarray.structures.triangles.abstract import AbstractTriangles diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index b7903f62..6bf7bf18 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -238,7 +238,7 @@ def test_vertices(one_triangle): ) -def test_upsampled_vertices(one_triangle): +def test_up_sampled_vertices(one_triangle): assert np.all( one_triangle.up_sample().vertices == [ From ff7028bff91a980ee667a693e0666db0920df2e0 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 09:52:46 +0000 Subject: [PATCH 31/70] with_vertices --- .../structures/triangles/coordinate_array.py | 18 +++++++++++++++++- .../test_coordinate_implementation.py | 5 +++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index d7490c93..3d9ce820 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -1,6 +1,7 @@ import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR +from autoarray.structures.triangles.array import ArrayTriangles from autoconf import cached_property @@ -35,7 +36,7 @@ def __init__( ) self.offset = offset - @property + @cached_property def triangles(self) -> np.ndarray: """ The vertices of the triangles as an Nx3x2 array. @@ -160,3 +161,18 @@ def neighborhood(self) -> "CoordinateArrayTriangles": @property def vertices(self): return np.unique(self.triangles.reshape((-1, 2)), axis=0) + + @property + def indices(self): + flat_triangles = self.triangles.reshape(-1, 2) + vertices, inverse_indices = np.unique( + flat_triangles, axis=0, return_inverse=True + ) + indices = inverse_indices.reshape(-1, 3) + return indices + + def with_vertices(self, vertices: np.ndarray) -> ArrayTriangles: + return ArrayTriangles( + indices=self.indices, + vertices=vertices, + ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 6bf7bf18..e009c83f 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -250,3 +250,8 @@ def test_up_sampled_vertices(one_triangle): [0.5, -0.4330127018922193], ] ) + + +def test_with_vertices(one_triangle): + triangle = one_triangle.with_vertices(np.array([[0, 0], [1, 0], [0.5, 1]])) + assert np.all(triangle.triangles == [[[1.0, 0.0], [0.5, 1.0], [0.0, 0.0]]]) From a323a369e55e0597fe62f3d4c8bf0f5ed597d0c2 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 10:06:57 +0000 Subject: [PATCH 32/70] for indexes --- autoarray/structures/triangles/coordinate_array.py | 8 ++++++++ .../triangles/test_coordinate_implementation.py | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 3d9ce820..9be6af29 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -176,3 +176,11 @@ def with_vertices(self, vertices: np.ndarray) -> ArrayTriangles: indices=self.indices, vertices=vertices, ) + + def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": + return CoordinateArrayTriangles( + coordinates=self.coordinates[indexes], + side_length=self.side_length, + flipped=self.flipped, + offset=self.offset, + ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index e009c83f..c48c9690 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -255,3 +255,16 @@ def test_up_sampled_vertices(one_triangle): def test_with_vertices(one_triangle): triangle = one_triangle.with_vertices(np.array([[0, 0], [1, 0], [0.5, 1]])) assert np.all(triangle.triangles == [[[1.0, 0.0], [0.5, 1.0], [0.0, 0.0]]]) + + +def test_for_indexes(two_triangles): + assert np.all( + two_triangles.for_indexes(np.array([0])).triangles + == [ + [ + [0.0, 0.4330127018922193], + [0.5, -0.4330127018922193], + [-0.5, -0.4330127018922193], + ] + ] + ) From f4d9d1649735e33bc542d19235b2fa3e0aeff1a9 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 10:09:08 +0000 Subject: [PATCH 33/70] docs --- .../structures/triangles/coordinate_array.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 9be6af29..ebe3cb44 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -159,11 +159,17 @@ def neighborhood(self) -> "CoordinateArrayTriangles": ) @property - def vertices(self): + def vertices(self) -> np.ndarray: + """ + The unique vertices of the triangles. + """ return np.unique(self.triangles.reshape((-1, 2)), axis=0) @property - def indices(self): + def indices(self) -> np.ndarray: + """ + The indices of the vertices of the triangles. + """ flat_triangles = self.triangles.reshape(-1, 2) vertices, inverse_indices = np.unique( flat_triangles, axis=0, return_inverse=True @@ -172,12 +178,36 @@ def indices(self): return indices def with_vertices(self, vertices: np.ndarray) -> ArrayTriangles: + """ + 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. + """ return ArrayTriangles( indices=self.indices, vertices=vertices, ) def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": + """ + Create a new CoordinateArrayTriangles containing triangles corresponding to the given indexes + + Parameters + ---------- + indexes + The indexes of the triangles to include in the new CoordinateArrayTriangles. + + Returns + ------- + The new CoordinateArrayTriangles instance. + """ return CoordinateArrayTriangles( coordinates=self.coordinates[indexes], side_length=self.side_length, From 9081464ecaf019131c47cd8905faf8d02df3b1b4 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 11:32:15 +0000 Subject: [PATCH 34/70] for limits and scales -> 3 covering triangles --- .../structures/triangles/coordinate_array.py | 46 +++++++++++++++---- .../test_coordinate_implementation.py | 10 ++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index ebe3cb44..50686588 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -11,7 +11,8 @@ def __init__( coordinates: np.ndarray, side_length: float, flipped: bool = False, - offset: float = 0.0, + x_offset: float = 0.0, + y_offset: float = 0.0, ): """ Represents a set of triangles by integer coordinates. @@ -24,8 +25,8 @@ def __init__( The side length of the triangles. flipped Whether the triangles are flipped upside down. - offset - An offset to apply to the y coordinates so that up-sampled triangles align. + 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 @@ -34,7 +35,8 @@ def __init__( self.scaling_factors = np.array( [0.5 * side_length, HEIGHT_FACTOR * side_length] ) - self.offset = offset + self.x_offset = x_offset + self.y_offset = y_offset @cached_property def triangles(self) -> np.ndarray: @@ -68,7 +70,7 @@ def centres(self) -> np.ndarray: """ The centres of the triangles. """ - return self.scaling_factors * self.coordinates + np.array([0.0, self.offset]) + return self.scaling_factors * self.coordinates + np.array([0.0, self.y_offset]) @cached_property def flip_mask(self) -> np.ndarray: @@ -123,7 +125,7 @@ def up_sample(self) -> "CoordinateArrayTriangles": coordinates=new_coordinates, side_length=self.side_length / 2, flipped=True, - offset=self.offset + -0.25 * HEIGHT_FACTOR * self.side_length, + y_offset=self.y_offset + -0.25 * HEIGHT_FACTOR * self.side_length, ) def neighborhood(self) -> "CoordinateArrayTriangles": @@ -155,7 +157,7 @@ def neighborhood(self) -> "CoordinateArrayTriangles": coordinates=np.unique(new_coordinates, axis=0), side_length=self.side_length, flipped=self.flipped, - offset=self.offset, + y_offset=self.y_offset, ) @property @@ -212,5 +214,33 @@ def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": coordinates=self.coordinates[indexes], side_length=self.side_length, flipped=self.flipped, - offset=self.offset, + y_offset=self.y_offset, + ) + + @classmethod + def for_limits_and_scale( + cls, + x_min: float, + x_max: float, + y_min: float, + y_max: float, + **kwargs, + ): + x_mean = (x_min + x_max) / 2 + y_mean = (y_min + y_max) / 2 + + max_side_length = max(x_max - x_min, y_max - y_min) + + return cls( + coordinates=np.array( + [ + [-1, 0], + [0, 0], + [1, 0], + ] + ), + side_length=max_side_length / HEIGHT_FACTOR, + x_offset=x_mean, + y_offset=y_mean, + **kwargs, ) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index c48c9690..d2858fe0 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -268,3 +268,13 @@ def test_for_indexes(two_triangles): ] ] ) + + +def test_for_limits_and_scale(plot): + triangles = CoordinateArrayTriangles.for_limits_and_scale( + x_min=-1.0, + x_max=1.0, + y_min=-1.0, + y_max=1.0, + ) + plot(triangles) From 1693af74bcb82d311d07bc4c471237b4b2e39c1f Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 11:33:04 +0000 Subject: [PATCH 35/70] fix --- autoarray/structures/triangles/coordinate_array.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 50686588..452b6ed3 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -224,7 +224,7 @@ def for_limits_and_scale( x_max: float, y_min: float, y_max: float, - **kwargs, + **_, ): x_mean = (x_min + x_max) / 2 y_mean = (y_min + y_max) / 2 @@ -242,5 +242,4 @@ def for_limits_and_scale( side_length=max_side_length / HEIGHT_FACTOR, x_offset=x_mean, y_offset=y_mean, - **kwargs, ) From e5717a9b371037fee68cd9971c6f5fd1399fd903 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 11:38:33 +0000 Subject: [PATCH 36/70] assertion --- .../test_coordinate_implementation.py | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index d2858fe0..056564d4 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -270,11 +270,30 @@ def test_for_indexes(two_triangles): ) -def test_for_limits_and_scale(plot): +def test_for_limits_and_scale(): triangles = CoordinateArrayTriangles.for_limits_and_scale( x_min=-1.0, x_max=1.0, y_min=-1.0, y_max=1.0, ) - plot(triangles) + assert np.all( + triangles.triangles + == [ + [ + [-1.1547005383792517, -1.0], + [-2.3094010767585034, 1.0], + [0.0, 1.0], + ], + [ + [0.0, 1.0], + [1.1547005383792517, -1.0], + [-1.1547005383792517, -1.0], + ], + [ + [1.1547005383792517, -1.0], + [0.0, 1.0], + [2.3094010767585034, 1.0], + ], + ] + ) From c2e1c5adf9140d86a2fbdce31fb2084d69f9e308 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 11:58:45 +0000 Subject: [PATCH 37/70] means --- autoarray/structures/triangles/coordinate_array.py | 4 ++++ .../structures/triangles/test_coordinate_implementation.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 452b6ed3..e51703f6 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -243,3 +243,7 @@ def for_limits_and_scale( x_offset=x_mean, y_offset=y_mean, ) + + @property + def means(self): + return np.mean(self.triangles, axis=1) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 056564d4..49d6255c 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -297,3 +297,7 @@ def test_for_limits_and_scale(): ], ] ) + + +def test_means(one_triangle): + assert np.all(one_triangle.means == [[0.0, -0.14433756729740643]]) From 3d7c14b362c5a36d9860a5555ba7a0e32a1ebb19 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 14:11:13 +0000 Subject: [PATCH 38/70] plotting test for negative triangles --- .../triangles/test_coordinate_implementation.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 49d6255c..01d18c0b 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -301,3 +301,16 @@ def test_for_limits_and_scale(): def test_means(one_triangle): assert np.all(one_triangle.means == [[0.0, -0.14433756729740643]]) + + +def test_negative_triangles(plot): + triangles = CoordinateArrayTriangles( + coordinates=np.array([[-1, -1]]), + side_length=1.0, + ) + up_sampled = triangles.up_sample() + plot(triangles) + plot(up_sampled, color="red") + + neighborhood = triangles.neighborhood() + plot(neighborhood, color="green") From d4f5d3cf5fe26d2db5852bfc39c10f799f5effc9 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 14:24:54 +0000 Subject: [PATCH 39/70] area method --- autoarray/structures/triangles/coordinate_array.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index e51703f6..7e246986 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -247,3 +247,7 @@ def for_limits_and_scale( @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.coordinates) From 2cb8eb1257ff80d0f0d32e12163c5a0b6ee9f665 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 14:36:22 +0000 Subject: [PATCH 40/70] len --- autoarray/structures/triangles/coordinate_array.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 7e246986..dda9a14d 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -251,3 +251,6 @@ def means(self): @property def area(self): return (3**0.5 / 4 * self.side_length**2) * len(self.coordinates) + + def __len__(self): + return len(self.coordinates) From fb6b27eddeef6448fbd7dc280bfee5517115a9fe Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 15:50:57 +0000 Subject: [PATCH 41/70] respect offset --- autoarray/structures/triangles/coordinate_array.py | 7 ++++++- .../triangles/test_coordinate_implementation.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index dda9a14d..4b770441 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -70,7 +70,9 @@ def centres(self) -> np.ndarray: """ The centres of the triangles. """ - return self.scaling_factors * self.coordinates + np.array([0.0, self.y_offset]) + return self.scaling_factors * self.coordinates + np.array( + [self.x_offset, self.y_offset] + ) @cached_property def flip_mask(self) -> np.ndarray: @@ -126,6 +128,7 @@ def up_sample(self) -> "CoordinateArrayTriangles": side_length=self.side_length / 2, flipped=True, y_offset=self.y_offset + -0.25 * HEIGHT_FACTOR * self.side_length, + x_offset=self.x_offset, ) def neighborhood(self) -> "CoordinateArrayTriangles": @@ -158,6 +161,7 @@ def neighborhood(self) -> "CoordinateArrayTriangles": side_length=self.side_length, flipped=self.flipped, y_offset=self.y_offset, + x_offset=self.x_offset, ) @property @@ -215,6 +219,7 @@ def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": side_length=self.side_length, flipped=self.flipped, y_offset=self.y_offset, + x_offset=self.x_offset, ) @classmethod diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 01d18c0b..3e6b2e7b 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -314,3 +314,15 @@ def test_negative_triangles(plot): neighborhood = triangles.neighborhood() plot(neighborhood, color="green") + + +def test_offset(plot): + for offset in range(3): + triangles = CoordinateArrayTriangles( + coordinates=np.array([[0, 0]]), + side_length=1.0, + x_offset=offset, + y_offset=offset, + ) + + plot(triangles) From 58800da411360f841052bc24136710f23a7ad65b Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 15:51:42 +0000 Subject: [PATCH 42/70] compute indices and vertices together --- .../structures/triangles/coordinate_array.py | 18 +++++++++++------- .../test_coordinate_implementation.py | 5 +++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 4b770441..48514927 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -164,24 +164,28 @@ def neighborhood(self) -> "CoordinateArrayTriangles": x_offset=self.x_offset, ) + @cached_property + def _vertices_and_indices(self): + flat_triangles = self.triangles.reshape(-1, 2) + vertices, inverse_indices = np.unique( + flat_triangles, axis=0, return_inverse=True + ) + indices = inverse_indices.reshape(-1, 3) + return vertices, indices + @property def vertices(self) -> np.ndarray: """ The unique vertices of the triangles. """ - return np.unique(self.triangles.reshape((-1, 2)), axis=0) + return self._vertices_and_indices[0] @property def indices(self) -> np.ndarray: """ The indices of the vertices of the triangles. """ - flat_triangles = self.triangles.reshape(-1, 2) - vertices, inverse_indices = np.unique( - flat_triangles, axis=0, return_inverse=True - ) - indices = inverse_indices.reshape(-1, 3) - return indices + return self._vertices_and_indices[1] def with_vertices(self, vertices: np.ndarray) -> ArrayTriangles: """ diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 3e6b2e7b..7ec8b187 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -257,6 +257,11 @@ def test_with_vertices(one_triangle): assert np.all(triangle.triangles == [[[1.0, 0.0], [0.5, 1.0], [0.0, 0.0]]]) +def test_multiple_with_vertices(one_triangle, plot): + up_sampled = one_triangle.up_sample() + plot(up_sampled.with_vertices(2 * up_sampled.vertices).triangles.tolist()) + + def test_for_indexes(two_triangles): assert np.all( two_triangles.for_indexes(np.array([0])).triangles From 21c6974d0f702a65c3f037e6cf88f5ab6cdc51d9 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 16:08:49 +0000 Subject: [PATCH 43/70] bigger grid fixed the issue --- .../structures/triangles/coordinate_array.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index 48514927..bf6aef16 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -233,22 +233,24 @@ def for_limits_and_scale( 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 - max_side_length = max(x_max - x_min, y_max - y_min) - return cls( - coordinates=np.array( - [ - [-1, 0], - [0, 0], - [1, 0], - ] - ), - side_length=max_side_length / HEIGHT_FACTOR, + coordinates=np.array(coordinates), + side_length=scale, x_offset=x_mean, y_offset=y_mean, ) From 2f1a19786a2d836bf1d143c924972f8a15215b07 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 16:11:10 +0000 Subject: [PATCH 44/70] updated tests --- .../test_coordinate_implementation.py | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 7ec8b187..2e90714f 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -231,9 +231,9 @@ def test_vertices(one_triangle): assert np.all( one_triangle.vertices == [ - [0.0, HEIGHT_FACTOR / 2], - [0.5, -HEIGHT_FACTOR / 2], - [-0.5, -HEIGHT_FACTOR / 2], + [-0.5, -0.4330127018922193], + [0.0, 0.4330127018922193], + [0.5, -0.4330127018922193], ] ) @@ -282,26 +282,7 @@ def test_for_limits_and_scale(): y_min=-1.0, y_max=1.0, ) - assert np.all( - triangles.triangles - == [ - [ - [-1.1547005383792517, -1.0], - [-2.3094010767585034, 1.0], - [0.0, 1.0], - ], - [ - [0.0, 1.0], - [1.1547005383792517, -1.0], - [-1.1547005383792517, -1.0], - ], - [ - [1.1547005383792517, -1.0], - [0.0, 1.0], - [2.3094010767585034, 1.0], - ], - ] - ) + assert triangles.triangles.shape == (4, 3, 2) def test_means(one_triangle): From a9a0d4843485a6fc5e004d96915b47cc86ed440f Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Nov 2024 16:11:25 +0000 Subject: [PATCH 45/70] removed plotting tests --- .../test_coordinate_implementation.py | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 2e90714f..bd7c68ba 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -287,28 +287,3 @@ def test_for_limits_and_scale(): def test_means(one_triangle): assert np.all(one_triangle.means == [[0.0, -0.14433756729740643]]) - - -def test_negative_triangles(plot): - triangles = CoordinateArrayTriangles( - coordinates=np.array([[-1, -1]]), - side_length=1.0, - ) - up_sampled = triangles.up_sample() - plot(triangles) - plot(up_sampled, color="red") - - neighborhood = triangles.neighborhood() - plot(neighborhood, color="green") - - -def test_offset(plot): - for offset in range(3): - triangles = CoordinateArrayTriangles( - coordinates=np.array([[0, 0]]), - side_length=1.0, - x_offset=offset, - y_offset=offset, - ) - - plot(triangles) From 8cfed6640ca5a5510ee4054292a35e6de24bbdda Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Mon, 4 Nov 2024 18:28:33 +0000 Subject: [PATCH 46/70] fix plot --- autoarray/plot/abstract_plotters.py | 1 + autoarray/plot/mat_plot/two_d.py | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/autoarray/plot/abstract_plotters.py b/autoarray/plot/abstract_plotters.py index 07db0729..d179533d 100644 --- a/autoarray/plot/abstract_plotters.py +++ b/autoarray/plot/abstract_plotters.py @@ -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 diff --git a/autoarray/plot/mat_plot/two_d.py b/autoarray/plot/mat_plot/two_d.py index 66b7121d..b527fd7a 100644 --- a/autoarray/plot/mat_plot/two_d.py +++ b/autoarray/plot/mat_plot/two_d.py @@ -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 From 617adc75c7c7b8fc70460408f25a2decefb7a215 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Wed, 6 Nov 2024 09:16:36 +0000 Subject: [PATCH 47/70] version --- autoarray/__init__.py | 2 +- autoarray/inversion/pixelization/image_mesh/hilbert.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/autoarray/__init__.py b/autoarray/__init__.py index 0fef39dd..2eade46a 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -99,4 +99,4 @@ conf.instance.register(__file__) -__version__ = "2024.9.21.2" +__version__ = "2024.11.6.1" diff --git a/autoarray/inversion/pixelization/image_mesh/hilbert.py b/autoarray/inversion/pixelization/image_mesh/hilbert.py index f28cf2b3..0b7cc1bb 100644 --- a/autoarray/inversion/pixelization/image_mesh/hilbert.py +++ b/autoarray/inversion/pixelization/image_mesh/hilbert.py @@ -277,7 +277,6 @@ def image_plane_mesh_grid_from( ------- """ - if not mask.is_circular: raise exc.PixelizationException( """ From 99e908b29c084b2669d3a0fcb14c5350adf576fa Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 11:02:37 +0000 Subject: [PATCH 48/70] simple test of all functionality including plotting --- .../triangles/jax_coordinate_array.py | 267 ++++++++++++++++++ .../structures/triangles/conftest.py | 16 ++ .../test_coordinate_implementation.py | 16 +- .../triangles/test_coordinate_jax.py | 27 ++ 4 files changed, 311 insertions(+), 15 deletions(-) create mode 100644 autoarray/structures/triangles/jax_coordinate_array.py create mode 100644 test_autoarray/structures/triangles/test_coordinate_jax.py diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py new file mode 100644 index 00000000..bf6aef16 --- /dev/null +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -0,0 +1,267 @@ +import numpy as np + +from autoarray.structures.triangles.abstract import HEIGHT_FACTOR +from autoarray.structures.triangles.array import ArrayTriangles +from autoconf import cached_property + + +class CoordinateArrayTriangles: + def __init__( + self, + coordinates: np.ndarray, + side_length: float, + flipped: bool = False, + x_offset: float = 0.0, + y_offset: float = 0.0, + ): + """ + 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 + def flip_array(self) -> np.ndarray: + """ + An array of 1s and -1s to flip the triangles. + """ + array = np.ones(self.coordinates.shape[0]) + array[self.flip_mask] = -1 + + return array[:, np.newaxis] + + def __iter__(self): + return iter(self.triangles) + + def up_sample(self) -> "CoordinateArrayTriangles": + """ + Up-sample the triangles by adding a new vertex at the midpoint of each edge. + """ + new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) + n_normal = 4 * np.sum(~self.flip_mask) + + new_coordinates[:n_normal] = np.vstack( + ( + 2 * self.coordinates[~self.flip_mask], + 2 * self.coordinates[~self.flip_mask] + np.array([1, 0]), + 2 * self.coordinates[~self.flip_mask] + np.array([-1, 0]), + 2 * self.coordinates[~self.flip_mask] + np.array([0, 1]), + ) + ) + new_coordinates[n_normal:] = np.vstack( + ( + 2 * self.coordinates[self.flip_mask], + 2 * self.coordinates[self.flip_mask] + np.array([1, 1]), + 2 * self.coordinates[self.flip_mask] + np.array([-1, 1]), + 2 * self.coordinates[self.flip_mask] + np.array([0, 1]), + ) + ) + + return CoordinateArrayTriangles( + coordinates=new_coordinates, + side_length=self.side_length / 2, + flipped=True, + y_offset=self.y_offset + -0.25 * HEIGHT_FACTOR * self.side_length, + x_offset=self.x_offset, + ) + + def neighborhood(self) -> "CoordinateArrayTriangles": + """ + Create a new set of triangles that are the neighborhood of the current triangles. + + Ensures that the new triangles are unique. + """ + new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) + n_normal = 4 * np.sum(~self.flip_mask) + + new_coordinates[:n_normal] = np.vstack( + ( + self.coordinates[~self.flip_mask], + self.coordinates[~self.flip_mask] + np.array([1, 0]), + self.coordinates[~self.flip_mask] + np.array([-1, 0]), + self.coordinates[~self.flip_mask] + np.array([0, -1]), + ) + ) + new_coordinates[n_normal:] = np.vstack( + ( + self.coordinates[self.flip_mask], + self.coordinates[self.flip_mask] + np.array([1, 0]), + self.coordinates[self.flip_mask] + np.array([-1, 0]), + self.coordinates[self.flip_mask] + np.array([0, 1]), + ) + ) + return CoordinateArrayTriangles( + coordinates=np.unique(new_coordinates, axis=0), + side_length=self.side_length, + flipped=self.flipped, + y_offset=self.y_offset, + x_offset=self.x_offset, + ) + + @cached_property + def _vertices_and_indices(self): + flat_triangles = self.triangles.reshape(-1, 2) + vertices, inverse_indices = np.unique( + flat_triangles, axis=0, return_inverse=True + ) + indices = inverse_indices.reshape(-1, 3) + return vertices, indices + + @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) -> ArrayTriangles: + """ + 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. + """ + return ArrayTriangles( + indices=self.indices, + vertices=vertices, + ) + + def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": + """ + Create a new CoordinateArrayTriangles containing triangles corresponding to the given indexes + + Parameters + ---------- + indexes + The indexes of the triangles to include in the new CoordinateArrayTriangles. + + Returns + ------- + The new CoordinateArrayTriangles instance. + """ + return CoordinateArrayTriangles( + coordinates=self.coordinates[indexes], + side_length=self.side_length, + flipped=self.flipped, + y_offset=self.y_offset, + x_offset=self.x_offset, + ) + + @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.coordinates) + + def __len__(self): + return len(self.coordinates) diff --git a/test_autoarray/structures/triangles/conftest.py b/test_autoarray/structures/triangles/conftest.py index d227111b..ccf9923b 100644 --- a/test_autoarray/structures/triangles/conftest.py +++ b/test_autoarray/structures/triangles/conftest.py @@ -1,10 +1,26 @@ from autoarray.numpy_wrapper import numpy as np from autoarray.structures.triangles.array import ArrayTriangles +from matplotlib import pyplot as plt + import pytest +@pytest.fixture +def plot(): + plt.figure(figsize=(8, 8)) + + def plot(triangles, color="black"): + for triangle in triangles: + triangle = np.append(triangle, [triangle[0]], axis=0) + plt.plot(triangle[:, 0], triangle[:, 1], "o-", color=color) + + yield plot + plt.gca().set_aspect("equal", adjustable="box") + plt.show() + + @pytest.fixture def compare_with_nans(): def compare_with_nans_(arr1, arr2): diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index bd7c68ba..9732edd1 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -1,25 +1,11 @@ import pytest -from matplotlib import pyplot as plt + import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles -@pytest.fixture -def plot(): - plt.figure(figsize=(8, 8)) - - def plot(triangles, color="black"): - for triangle in triangles: - triangle = np.append(triangle, [triangle[0]], axis=0) - plt.plot(triangle[:, 0], triangle[:, 1], "o-", color=color) - - yield plot - plt.gca().set_aspect("equal", adjustable="box") - plt.show() - - @pytest.fixture def two_triangles(): return CoordinateArrayTriangles( diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py new file mode 100644 index 00000000..e75b36c3 --- /dev/null +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -0,0 +1,27 @@ +import pytest +import numpy as np + +from autoarray.structures.triangles.jax_coordinate_array import CoordinateArrayTriangles +from autoarray.structures.triangles.shape import Point + + +@pytest.fixture +def one_triangle(): + return CoordinateArrayTriangles( + coordinates=np.array([[0, 0]]), + side_length=1.0, + ) + + +def test_full_routine(one_triangle, plot): + neighborhood = one_triangle.neighborhood() + up_sampled = one_triangle.up_sample() + with_vertices = up_sampled.with_vertices(up_sampled.vertices) + indexes = with_vertices.containing_indices(Point(0.1, 0.1)) + selected = up_sampled.for_indexes(indexes) + + plot(one_triangle.triangles, color="black") + plot(neighborhood.triangles, color="red") + plot(up_sampled.triangles, color="blue") + plot(with_vertices.triangles, color="green") + plot(selected.triangles, color="yellow") From 05ebf254c6ade7405b90ea066202ecbbefbe8ab4 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 11:04:48 +0000 Subject: [PATCH 49/70] extracted full routine --- .../triangles/test_coordinate_jax.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index e75b36c3..c2ac8686 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -13,15 +13,25 @@ def one_triangle(): ) +def full_routine(triangles): + neighborhood = triangles.neighborhood() + up_sampled = neighborhood.up_sample() + with_vertices = up_sampled.with_vertices(up_sampled.vertices) + indexes = with_vertices.containing_indices(Point(0.1, 0.1)) + return up_sampled.for_indexes(indexes) + + def test_full_routine(one_triangle, plot): + full_routine(one_triangle) + neighborhood = one_triangle.neighborhood() - up_sampled = one_triangle.up_sample() + up_sampled = neighborhood.up_sample() with_vertices = up_sampled.with_vertices(up_sampled.vertices) - indexes = with_vertices.containing_indices(Point(0.1, 0.1)) - selected = up_sampled.for_indexes(indexes) - plot(one_triangle.triangles, color="black") - plot(neighborhood.triangles, color="red") + selected = full_routine(one_triangle) + plot(up_sampled.triangles, color="blue") plot(with_vertices.triangles, color="green") + plot(neighborhood.triangles, color="red") + plot(one_triangle.triangles, color="black") plot(selected.triangles, color="yellow") From 5c1ab614e3cae6d0378724718f80caf5ee9c898b Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 11:05:15 +0000 Subject: [PATCH 50/70] jit --- test_autoarray/structures/triangles/test_coordinate_jax.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index c2ac8686..6fb9e25c 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -1,3 +1,4 @@ +import jax import pytest import numpy as np @@ -13,6 +14,7 @@ def one_triangle(): ) +@jax.jit def full_routine(triangles): neighborhood = triangles.neighborhood() up_sampled = neighborhood.up_sample() From ff373a5020ed76502968cf6e12654b79b471f1d4 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 11:43:54 +0000 Subject: [PATCH 51/70] book keeping to start jax working... --- .../triangles/jax_coordinate_array.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index bf6aef16..662c4e3d 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -1,10 +1,12 @@ -import numpy as np +from autoarray.numpy_wrapper import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR from autoarray.structures.triangles.array import ArrayTriangles +from autoarray.numpy_wrapper import register_pytree_node_class from autoconf import cached_property +@register_pytree_node_class class CoordinateArrayTriangles: def __init__( self, @@ -38,6 +40,34 @@ def __init__( self.x_offset = x_offset self.y_offset = y_offset + def tree_flatten(self): + return ( + self.coordinates, + self.side_length, + self.flipped, + self.x_offset, + self.y_offset, + ), () + + @classmethod + def tree_unflatten(cls, aux_data, children): + """ + Create a prior from a flattened PyTree + + Parameters + ---------- + aux_data + Auxiliary information that remains unchanged including + the keys of the dict + children + Child objects subject to change + + Returns + ------- + An instance of this class + """ + return cls(*children) + @cached_property def triangles(self) -> np.ndarray: """ From d82ec73a3ae42cff74ecce61bf527e1438b63599 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 11:58:06 +0000 Subject: [PATCH 52/70] flipped as static --- autoarray/structures/triangles/coordinate_array.py | 8 ++++---- autoarray/structures/triangles/jax_coordinate_array.py | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index bf6aef16..c0fc15b5 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -10,9 +10,9 @@ def __init__( self, coordinates: np.ndarray, side_length: float, - flipped: bool = False, x_offset: float = 0.0, y_offset: float = 0.0, + flipped: bool = False, ): """ Represents a set of triangles by integer coordinates. @@ -126,9 +126,9 @@ def up_sample(self) -> "CoordinateArrayTriangles": return CoordinateArrayTriangles( coordinates=new_coordinates, side_length=self.side_length / 2, - flipped=True, y_offset=self.y_offset + -0.25 * HEIGHT_FACTOR * self.side_length, x_offset=self.x_offset, + flipped=True, ) def neighborhood(self) -> "CoordinateArrayTriangles": @@ -159,9 +159,9 @@ def neighborhood(self) -> "CoordinateArrayTriangles": return CoordinateArrayTriangles( coordinates=np.unique(new_coordinates, axis=0), side_length=self.side_length, - flipped=self.flipped, y_offset=self.y_offset, x_offset=self.x_offset, + flipped=self.flipped, ) @cached_property @@ -221,9 +221,9 @@ def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": return CoordinateArrayTriangles( coordinates=self.coordinates[indexes], side_length=self.side_length, - flipped=self.flipped, y_offset=self.y_offset, x_offset=self.x_offset, + flipped=self.flipped, ) @classmethod diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index 662c4e3d..b329d650 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -12,9 +12,9 @@ def __init__( self, coordinates: np.ndarray, side_length: float, - flipped: bool = False, x_offset: float = 0.0, y_offset: float = 0.0, + flipped: bool = False, ): """ Represents a set of triangles by integer coordinates. @@ -44,10 +44,9 @@ def tree_flatten(self): return ( self.coordinates, self.side_length, - self.flipped, self.x_offset, self.y_offset, - ), () + ), (self.flipped,) @classmethod def tree_unflatten(cls, aux_data, children): @@ -66,7 +65,7 @@ def tree_unflatten(cls, aux_data, children): ------- An instance of this class """ - return cls(*children) + return cls(*children, flipped=aux_data[0]) @cached_property def triangles(self) -> np.ndarray: From 56067fead8f9a5ee2fde9fe0820634b3d2f5754c Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 12:25:12 +0000 Subject: [PATCH 53/70] neigborhood --- .../triangles/jax_coordinate_array.py | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index b329d650..b4f23dd3 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -166,25 +166,34 @@ def neighborhood(self) -> "CoordinateArrayTriangles": Ensures that the new triangles are unique. """ - new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) - n_normal = 4 * np.sum(~self.flip_mask) + normal_indices = np.where(~self.flip_mask)[0] + flip_indices = np.where(self.flip_mask)[0] - new_coordinates[:n_normal] = np.vstack( + normal_coordinates = np.vstack( ( - self.coordinates[~self.flip_mask], - self.coordinates[~self.flip_mask] + np.array([1, 0]), - self.coordinates[~self.flip_mask] + np.array([-1, 0]), - self.coordinates[~self.flip_mask] + np.array([0, -1]), + self.coordinates[normal_indices], + self.coordinates[normal_indices] + np.array([1, 0]), + self.coordinates[normal_indices] + np.array([-1, 0]), + self.coordinates[normal_indices] + np.array([0, -1]), ) ) - new_coordinates[n_normal:] = np.vstack( + flip_coordinates = np.vstack( ( - self.coordinates[self.flip_mask], - self.coordinates[self.flip_mask] + np.array([1, 0]), - self.coordinates[self.flip_mask] + np.array([-1, 0]), - self.coordinates[self.flip_mask] + np.array([0, 1]), + self.coordinates[flip_indices], + self.coordinates[flip_indices] + np.array([1, 0]), + self.coordinates[flip_indices] + np.array([-1, 0]), + self.coordinates[flip_indices] + np.array([0, 1]), ) ) + + new_coordinates = np.concatenate( + ( + normal_coordinates, + flip_coordinates, + ), + axis=0, + ) + return CoordinateArrayTriangles( coordinates=np.unique(new_coordinates, axis=0), side_length=self.side_length, From 2d512f0bc178394a3051a034f190f2813dbb0e1d Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 12:29:24 +0000 Subject: [PATCH 54/70] neighborhood seems to work --- autoarray/structures/triangles/jax_coordinate_array.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index b4f23dd3..e9eb404c 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -166,8 +166,8 @@ def neighborhood(self) -> "CoordinateArrayTriangles": Ensures that the new triangles are unique. """ - normal_indices = np.where(~self.flip_mask)[0] - flip_indices = np.where(self.flip_mask)[0] + normal_indices = np.where(~self.flip_mask, size=self.coordinates.shape[0])[0] + flip_indices = np.where(self.flip_mask, size=self.coordinates.shape[0])[0] normal_coordinates = np.vstack( ( @@ -195,7 +195,11 @@ def neighborhood(self) -> "CoordinateArrayTriangles": ) return CoordinateArrayTriangles( - coordinates=np.unique(new_coordinates, axis=0), + coordinates=np.unique( + new_coordinates, + axis=0, + size=4 * self.coordinates.shape[0], + ), side_length=self.side_length, flipped=self.flipped, y_offset=self.y_offset, From 1ce4a6f6e6b3d3308f2dd45f1c15f6eed68ee355 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 12:50:43 +0000 Subject: [PATCH 55/70] try different implementation --- .../triangles/jax_coordinate_array.py | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index e9eb404c..4b0f8066 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -166,33 +166,30 @@ def neighborhood(self) -> "CoordinateArrayTriangles": Ensures that the new triangles are unique. """ - normal_indices = np.where(~self.flip_mask, size=self.coordinates.shape[0])[0] - flip_indices = np.where(self.flip_mask, size=self.coordinates.shape[0])[0] - - normal_coordinates = np.vstack( - ( - self.coordinates[normal_indices], - self.coordinates[normal_indices] + np.array([1, 0]), - self.coordinates[normal_indices] + np.array([-1, 0]), - self.coordinates[normal_indices] + np.array([0, -1]), - ) - ) - flip_coordinates = np.vstack( - ( - self.coordinates[flip_indices], - self.coordinates[flip_indices] + np.array([1, 0]), - self.coordinates[flip_indices] + np.array([-1, 0]), - self.coordinates[flip_indices] + np.array([0, 1]), - ) + coordinates = self.coordinates # shape (N, 2) + flip_mask = self.flip_mask # shape (N,) + + # Create shifts for all coordinates + shift0 = np.zeros((coordinates.shape[0], 2)) + shift1 = np.tile(np.array([1, 0]), (coordinates.shape[0], 1)) + shift2 = np.tile(np.array([-1, 0]), (coordinates.shape[0], 1)) + + # Create the fourth shift based on the flip_mask + shift3 = np.where( + flip_mask[:, None], + np.tile(np.array([0, 1]), (coordinates.shape[0], 1)), + np.tile(np.array([0, -1]), (coordinates.shape[0], 1)), ) - new_coordinates = np.concatenate( - ( - normal_coordinates, - flip_coordinates, - ), - axis=0, - ) + # Stack all shifts together + shifts = np.stack([shift0, shift1, shift2, shift3], axis=1) # shape (N, 4, 2) + + # Expand coordinates and add shifts + coordinates_expanded = coordinates[:, None, :] # shape (N, 1, 2) + new_coordinates = coordinates_expanded + shifts # shape (N, 4, 2) + + # Reshape to (4N, 2) + new_coordinates = new_coordinates.reshape(-1, 2) return CoordinateArrayTriangles( coordinates=np.unique( From 9b25796a27c126c4d8472a6316af47210caf14d4 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 12:56:45 +0000 Subject: [PATCH 56/70] working version of jax neighborhood[ --- .../triangles/jax_coordinate_array.py | 23 +++++-------- .../triangles/test_coordinate_jax.py | 34 ++++++++++++++++++- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index 4b0f8066..5ec4675a 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -1,4 +1,4 @@ -from autoarray.numpy_wrapper import numpy as np +from jax import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR from autoarray.structures.triangles.array import ArrayTriangles @@ -120,10 +120,8 @@ def flip_array(self) -> np.ndarray: """ An array of 1s and -1s to flip the triangles. """ - array = np.ones(self.coordinates.shape[0]) - array[self.flip_mask] = -1 - - return array[:, np.newaxis] + array = np.where(self.flip_mask, -1.0, 1.0) + return array[:, None] def __iter__(self): return iter(self.triangles) @@ -166,29 +164,24 @@ def neighborhood(self) -> "CoordinateArrayTriangles": Ensures that the new triangles are unique. """ - coordinates = self.coordinates # shape (N, 2) - flip_mask = self.flip_mask # shape (N,) + coordinates = self.coordinates + flip_mask = self.flip_mask - # Create shifts for all coordinates shift0 = np.zeros((coordinates.shape[0], 2)) shift1 = np.tile(np.array([1, 0]), (coordinates.shape[0], 1)) shift2 = np.tile(np.array([-1, 0]), (coordinates.shape[0], 1)) - # Create the fourth shift based on the flip_mask shift3 = np.where( flip_mask[:, None], np.tile(np.array([0, 1]), (coordinates.shape[0], 1)), np.tile(np.array([0, -1]), (coordinates.shape[0], 1)), ) - # Stack all shifts together - shifts = np.stack([shift0, shift1, shift2, shift3], axis=1) # shape (N, 4, 2) + shifts = np.stack([shift0, shift1, shift2, shift3], axis=1) - # Expand coordinates and add shifts - coordinates_expanded = coordinates[:, None, :] # shape (N, 1, 2) - new_coordinates = coordinates_expanded + shifts # shape (N, 4, 2) + coordinates_expanded = coordinates[:, None, :] + new_coordinates = coordinates_expanded + shifts - # Reshape to (4N, 2) new_coordinates = new_coordinates.reshape(-1, 2) return CoordinateArrayTriangles( diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 6fb9e25c..22f34777 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -17,6 +17,7 @@ def one_triangle(): @jax.jit def full_routine(triangles): neighborhood = triangles.neighborhood() + return neighborhood up_sampled = neighborhood.up_sample() with_vertices = up_sampled.with_vertices(up_sampled.vertices) indexes = with_vertices.containing_indices(Point(0.1, 0.1)) @@ -24,9 +25,12 @@ def full_routine(triangles): def test_full_routine(one_triangle, plot): - full_routine(one_triangle) + # full_routine(one_triangle) neighborhood = one_triangle.neighborhood() + plot(np.array(neighborhood), color="red") + return + up_sampled = neighborhood.up_sample() with_vertices = up_sampled.with_vertices(up_sampled.vertices) @@ -37,3 +41,31 @@ def test_full_routine(one_triangle, plot): plot(neighborhood.triangles, color="red") plot(one_triangle.triangles, color="black") plot(selected.triangles, color="yellow") + + +def test_neighborhood(one_triangle): + assert np.all( + np.array(jax.jit(one_triangle.neighborhood)().triangles) + == [ + [ + [-0.5, -0.4330126941204071], + [-1.0, 0.4330126941204071], + [0.0, 0.4330126941204071], + ], + [ + [0.0, -1.299038052558899], + [-0.5, -0.4330126941204071], + [0.5, -0.4330126941204071], + ], + [ + [0.0, 0.4330126941204071], + [0.5, -0.4330126941204071], + [-0.5, -0.4330126941204071], + ], + [ + [0.5, -0.4330126941204071], + [0.0, 0.4330126941204071], + [1.0, 0.4330126941204071], + ], + ] + ) From 881c0cf6a498a68fa6f7f1cdbbe0f286c115801f Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 15:16:51 +0000 Subject: [PATCH 57/70] successfully plotting triangles upsampled by jax --- test_autoarray/structures/triangles/conftest.py | 3 ++- .../structures/triangles/test_coordinate_jax.py | 17 ++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/test_autoarray/structures/triangles/conftest.py b/test_autoarray/structures/triangles/conftest.py index ccf9923b..4a183de8 100644 --- a/test_autoarray/structures/triangles/conftest.py +++ b/test_autoarray/structures/triangles/conftest.py @@ -13,7 +13,8 @@ def plot(): def plot(triangles, color="black"): for triangle in triangles: - triangle = np.append(triangle, [triangle[0]], axis=0) + triangle = np.array(triangle) + triangle = np.append(triangle, np.array([triangle[0]]), axis=0) plt.plot(triangle[:, 0], triangle[:, 1], "o-", color=color) yield plot diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 22f34777..77f9c510 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -25,22 +25,9 @@ def full_routine(triangles): def test_full_routine(one_triangle, plot): - # full_routine(one_triangle) + result = full_routine(one_triangle) - neighborhood = one_triangle.neighborhood() - plot(np.array(neighborhood), color="red") - return - - up_sampled = neighborhood.up_sample() - with_vertices = up_sampled.with_vertices(up_sampled.vertices) - - selected = full_routine(one_triangle) - - plot(up_sampled.triangles, color="blue") - plot(with_vertices.triangles, color="green") - plot(neighborhood.triangles, color="red") - plot(one_triangle.triangles, color="black") - plot(selected.triangles, color="yellow") + plot(result) def test_neighborhood(one_triangle): From b264be6662dc43fc350fdcdab18fac674b4f39a5 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 15:28:33 +0000 Subject: [PATCH 58/70] upsample implementation --- .../triangles/jax_coordinate_array.py | 33 +++++++++---------- .../triangles/test_coordinate_jax.py | 15 +++++++++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index 5ec4675a..8406bf4c 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -130,25 +130,22 @@ def up_sample(self) -> "CoordinateArrayTriangles": """ Up-sample the triangles by adding a new vertex at the midpoint of each edge. """ - new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2)) - n_normal = 4 * np.sum(~self.flip_mask) + coordinates = self.coordinates + flip_mask = self.flip_mask - new_coordinates[:n_normal] = np.vstack( - ( - 2 * self.coordinates[~self.flip_mask], - 2 * self.coordinates[~self.flip_mask] + np.array([1, 0]), - 2 * self.coordinates[~self.flip_mask] + np.array([-1, 0]), - 2 * self.coordinates[~self.flip_mask] + np.array([0, 1]), - ) - ) - new_coordinates[n_normal:] = np.vstack( - ( - 2 * self.coordinates[self.flip_mask], - 2 * self.coordinates[self.flip_mask] + np.array([1, 1]), - 2 * self.coordinates[self.flip_mask] + np.array([-1, 1]), - 2 * self.coordinates[self.flip_mask] + np.array([0, 1]), - ) - ) + coordinates = 2 * coordinates + + n = coordinates.shape[0] + + shift0 = np.zeros((n, 2)) + shift3 = np.tile(np.array([0, 1]), (n, 1)) + shift1 = np.stack([np.ones(n), np.where(flip_mask, 1.0, 0.0)], axis=1) + shift2 = np.stack([-np.ones(n), np.where(flip_mask, 1.0, 0.0)], axis=1) + shifts = np.stack([shift0, shift1, shift2, shift3], axis=1) + + coordinates_expanded = coordinates[:, None, :] + new_coordinates = coordinates_expanded + shifts + new_coordinates = new_coordinates.reshape(-1, 2) return CoordinateArrayTriangles( coordinates=new_coordinates, diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 77f9c510..e6d0e064 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -56,3 +56,18 @@ def test_neighborhood(one_triangle): ], ] ) + + +def test_up_sample(one_triangle): + up_sampled = jax.jit(one_triangle.up_sample)() + assert np.all( + np.array(up_sampled.triangles) + == [ + [ + [[0.0, -0.4330126941204071], [-0.25, 0.0], [0.25, 0.0]], + [[0.25, 0.0], [0.5, -0.4330126941204071], [0.0, -0.4330126941204071]], + [[-0.25, 0.0], [0.0, -0.4330126941204071], [-0.5, -0.4330126941204071]], + [[0.0, 0.4330126941204071], [0.25, 0.0], [-0.25, 0.0]], + ] + ] + ) From bc61efb60485332a01270b267f072cc13aaf50cd Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 15:45:17 +0000 Subject: [PATCH 59/70] upsampling works as part of full routine --- test_autoarray/structures/triangles/test_coordinate_jax.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index e6d0e064..82ecd456 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -17,8 +17,8 @@ def one_triangle(): @jax.jit def full_routine(triangles): neighborhood = triangles.neighborhood() - return neighborhood up_sampled = neighborhood.up_sample() + return up_sampled with_vertices = up_sampled.with_vertices(up_sampled.vertices) indexes = with_vertices.containing_indices(Point(0.1, 0.1)) return up_sampled.for_indexes(indexes) From a172f1c9cff365e7b8241417a436ca9016cfc234 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 15:48:33 +0000 Subject: [PATCH 60/70] with_vertices --- autoarray/structures/triangles/jax_coordinate_array.py | 7 +++++-- test_autoarray/structures/triangles/test_coordinate_jax.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index 8406bf4c..9627ba55 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -1,7 +1,7 @@ from jax import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR -from autoarray.structures.triangles.array import ArrayTriangles +from autoarray.structures.triangles.jax_array import ArrayTriangles from autoarray.numpy_wrapper import register_pytree_node_class from autoconf import cached_property @@ -197,7 +197,10 @@ def neighborhood(self) -> "CoordinateArrayTriangles": def _vertices_and_indices(self): flat_triangles = self.triangles.reshape(-1, 2) vertices, inverse_indices = np.unique( - flat_triangles, axis=0, return_inverse=True + flat_triangles, + axis=0, + return_inverse=True, + size=3 * self.coordinates.shape[0], ) indices = inverse_indices.reshape(-1, 3) return vertices, indices diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 82ecd456..36af507e 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -18,8 +18,8 @@ def one_triangle(): def full_routine(triangles): neighborhood = triangles.neighborhood() up_sampled = neighborhood.up_sample() - return up_sampled with_vertices = up_sampled.with_vertices(up_sampled.vertices) + return with_vertices indexes = with_vertices.containing_indices(Point(0.1, 0.1)) return up_sampled.for_indexes(indexes) From 21ac0ef5ebe59223bb49cb5dc23259daaeb16f9f Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 16:16:37 +0000 Subject: [PATCH 61/70] for_indexes giving triangles as expected --- autoarray/structures/triangles/jax_coordinate_array.py | 9 +++++++-- .../structures/triangles/test_coordinate_jax.py | 3 +-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index 9627ba55..d9274ab7 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -250,12 +250,17 @@ def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": ------- The new CoordinateArrayTriangles instance. """ + mask = indexes == -1 + safe_indexes = np.where(mask, 0, indexes) + coordinates = self.coordinates[safe_indexes] + coordinates = np.where(mask[:, None], np.nan, coordinates) + return CoordinateArrayTriangles( - coordinates=self.coordinates[indexes], + coordinates=coordinates, side_length=self.side_length, - flipped=self.flipped, y_offset=self.y_offset, x_offset=self.x_offset, + flipped=self.flipped, ) @classmethod diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 36af507e..a456c8d7 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -14,12 +14,11 @@ def one_triangle(): ) -@jax.jit +# @jax.jit def full_routine(triangles): neighborhood = triangles.neighborhood() up_sampled = neighborhood.up_sample() with_vertices = up_sampled.with_vertices(up_sampled.vertices) - return with_vertices indexes = with_vertices.containing_indices(Point(0.1, 0.1)) return up_sampled.for_indexes(indexes) From 2684069010558f2cc943ea8051ff267372f152ba Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 16:20:17 +0000 Subject: [PATCH 62/70] use numpy wrapper in tests --- .../structures/triangles/test_coordinate_jax.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index a456c8d7..ff717482 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -1,4 +1,4 @@ -import jax +from autoarray.numpy_wrapper import jit import pytest import numpy as np @@ -14,7 +14,7 @@ def one_triangle(): ) -# @jax.jit +@jit def full_routine(triangles): neighborhood = triangles.neighborhood() up_sampled = neighborhood.up_sample() @@ -31,7 +31,7 @@ def test_full_routine(one_triangle, plot): def test_neighborhood(one_triangle): assert np.all( - np.array(jax.jit(one_triangle.neighborhood)().triangles) + np.array(jit(one_triangle.neighborhood)().triangles) == [ [ [-0.5, -0.4330126941204071], @@ -58,7 +58,7 @@ def test_neighborhood(one_triangle): def test_up_sample(one_triangle): - up_sampled = jax.jit(one_triangle.up_sample)() + up_sampled = jit(one_triangle.up_sample)() assert np.all( np.array(up_sampled.triangles) == [ From 9a29132c289fe1baf36d260e82c19c8214a9bb29 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 16:21:49 +0000 Subject: [PATCH 63/70] try to test with jax --- .../triangles/test_coordinate_jax.py | 80 ++++++++++++------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index ff717482..5ff53267 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -1,10 +1,20 @@ from autoarray.numpy_wrapper import jit import pytest -import numpy as np -from autoarray.structures.triangles.jax_coordinate_array import CoordinateArrayTriangles from autoarray.structures.triangles.shape import Point +try: + from jax import numpy as np + import jax + + jax.config.update("jax_log_compiles", True) + from autoarray.structures.triangles.jax_coordinate_array import ( + CoordinateArrayTriangles, + ) +except ImportError: + import numpy as np + from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles + @pytest.fixture def one_triangle(): @@ -32,28 +42,30 @@ def test_full_routine(one_triangle, plot): def test_neighborhood(one_triangle): assert np.all( np.array(jit(one_triangle.neighborhood)().triangles) - == [ - [ - [-0.5, -0.4330126941204071], - [-1.0, 0.4330126941204071], - [0.0, 0.4330126941204071], - ], + == np.array( [ - [0.0, -1.299038052558899], - [-0.5, -0.4330126941204071], - [0.5, -0.4330126941204071], - ], - [ - [0.0, 0.4330126941204071], - [0.5, -0.4330126941204071], - [-0.5, -0.4330126941204071], - ], - [ - [0.5, -0.4330126941204071], - [0.0, 0.4330126941204071], - [1.0, 0.4330126941204071], - ], - ] + [ + [-0.5, -0.4330126941204071], + [-1.0, 0.4330126941204071], + [0.0, 0.4330126941204071], + ], + [ + [0.0, -1.299038052558899], + [-0.5, -0.4330126941204071], + [0.5, -0.4330126941204071], + ], + [ + [0.0, 0.4330126941204071], + [0.5, -0.4330126941204071], + [-0.5, -0.4330126941204071], + ], + [ + [0.5, -0.4330126941204071], + [0.0, 0.4330126941204071], + [1.0, 0.4330126941204071], + ], + ] + ) ) @@ -61,12 +73,22 @@ def test_up_sample(one_triangle): up_sampled = jit(one_triangle.up_sample)() assert np.all( np.array(up_sampled.triangles) - == [ + == np.array( [ - [[0.0, -0.4330126941204071], [-0.25, 0.0], [0.25, 0.0]], - [[0.25, 0.0], [0.5, -0.4330126941204071], [0.0, -0.4330126941204071]], - [[-0.25, 0.0], [0.0, -0.4330126941204071], [-0.5, -0.4330126941204071]], - [[0.0, 0.4330126941204071], [0.25, 0.0], [-0.25, 0.0]], + [ + [[0.0, -0.4330126941204071], [-0.25, 0.0], [0.25, 0.0]], + [ + [0.25, 0.0], + [0.5, -0.4330126941204071], + [0.0, -0.4330126941204071], + ], + [ + [-0.25, 0.0], + [0.0, -0.4330126941204071], + [-0.5, -0.4330126941204071], + ], + [[0.0, 0.4330126941204071], [0.25, 0.0], [-0.25, 0.0]], + ] ] - ] + ) ) From 2560e559040c49f1aa6b43e2d918b12130128578 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 16:41:03 +0000 Subject: [PATCH 64/70] use allclose --- .../structures/triangles/test_coordinate_jax.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 5ff53267..ea249b8c 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -40,9 +40,9 @@ def test_full_routine(one_triangle, plot): def test_neighborhood(one_triangle): - assert np.all( - np.array(jit(one_triangle.neighborhood)().triangles) - == np.array( + assert np.allclose( + np.array(jit(one_triangle.neighborhood)().triangles), + np.array( [ [ [-0.5, -0.4330126941204071], @@ -65,15 +65,15 @@ def test_neighborhood(one_triangle): [1.0, 0.4330126941204071], ], ] - ) + ), ) def test_up_sample(one_triangle): up_sampled = jit(one_triangle.up_sample)() - assert np.all( - np.array(up_sampled.triangles) - == np.array( + assert np.allclose( + np.array(up_sampled.triangles), + np.array( [ [ [[0.0, -0.4330126941204071], [-0.25, 0.0], [0.25, 0.0]], @@ -90,5 +90,5 @@ def test_up_sample(one_triangle): [[0.0, 0.4330126941204071], [0.25, 0.0], [-0.25, 0.0]], ] ] - ) + ), ) From 179066fb2b50d28116f0548109e7d5987a6bee80 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 16:43:26 +0000 Subject: [PATCH 65/70] convert plot based test to assertion --- .../structures/triangles/test_coordinate_jax.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index ea249b8c..1414e224 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -33,10 +33,21 @@ def full_routine(triangles): return up_sampled.for_indexes(indexes) -def test_full_routine(one_triangle, plot): +def test_full_routine(one_triangle, compare_with_nans): result = full_routine(one_triangle) - plot(result) + assert compare_with_nans( + result.triangles, + np.array( + [ + [ + [0.0, 0.4330126941204071], + [0.25, 0.0], + [-0.25, 0.0], + ] + ] + ), + ) def test_neighborhood(one_triangle): From a7837ee8bd8ede282888dfd81a645cfacd974e82 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 16:57:12 +0000 Subject: [PATCH 66/70] move common functionality to abstract class --- .../triangles/abstract_coordinate_array.py | 171 ++++++++++++++++++ .../structures/triangles/coordinate_array.py | 142 +-------------- .../triangles/jax_coordinate_array.py | 118 +----------- 3 files changed, 179 insertions(+), 252 deletions(-) create mode 100644 autoarray/structures/triangles/abstract_coordinate_array.py diff --git a/autoarray/structures/triangles/abstract_coordinate_array.py b/autoarray/structures/triangles/abstract_coordinate_array.py new file mode 100644 index 00000000..d0cead30 --- /dev/null +++ b/autoarray/structures/triangles/abstract_coordinate_array.py @@ -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.coordinates) + + def __len__(self): + return len(self.coordinates) diff --git a/autoarray/structures/triangles/coordinate_array.py b/autoarray/structures/triangles/coordinate_array.py index c0fc15b5..e45f3eed 100644 --- a/autoarray/structures/triangles/coordinate_array.py +++ b/autoarray/structures/triangles/coordinate_array.py @@ -1,91 +1,14 @@ import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR +from autoarray.structures.triangles.abstract_coordinate_array import ( + AbstractCoordinateArray, +) from autoarray.structures.triangles.array import ArrayTriangles from autoconf import cached_property -class CoordinateArrayTriangles: - 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 - +class CoordinateArrayTriangles(AbstractCoordinateArray): @cached_property def flip_array(self) -> np.ndarray: """ @@ -96,9 +19,6 @@ def flip_array(self) -> np.ndarray: return array[:, np.newaxis] - def __iter__(self): - return iter(self.triangles) - def up_sample(self) -> "CoordinateArrayTriangles": """ Up-sample the triangles by adding a new vertex at the midpoint of each edge. @@ -173,20 +93,6 @@ def _vertices_and_indices(self): indices = inverse_indices.reshape(-1, 3) return vertices, indices - @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) -> ArrayTriangles: """ Create a new set of triangles with the vertices replaced. @@ -225,43 +131,3 @@ def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": x_offset=self.x_offset, flipped=self.flipped, ) - - @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.coordinates) - - def __len__(self): - return len(self.coordinates) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index d9274ab7..525c5b62 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -1,45 +1,16 @@ from jax import numpy as np from autoarray.structures.triangles.abstract import HEIGHT_FACTOR +from autoarray.structures.triangles.abstract_coordinate_array import ( + AbstractCoordinateArray, +) from autoarray.structures.triangles.jax_array import ArrayTriangles from autoarray.numpy_wrapper import register_pytree_node_class from autoconf import cached_property @register_pytree_node_class -class CoordinateArrayTriangles: - 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 - +class CoordinateArrayTriangles(AbstractCoordinateArray): def tree_flatten(self): return ( self.coordinates, @@ -67,33 +38,6 @@ def tree_unflatten(cls, aux_data, children): """ return cls(*children, flipped=aux_data[0]) - @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: """ @@ -205,20 +149,6 @@ def _vertices_and_indices(self): indices = inverse_indices.reshape(-1, 3) return vertices, indices - @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) -> ArrayTriangles: """ Create a new set of triangles with the vertices replaced. @@ -262,43 +192,3 @@ def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles": x_offset=self.x_offset, flipped=self.flipped, ) - - @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.coordinates) - - def __len__(self): - return len(self.coordinates) From b7a11dbf83f867b8178c4cb0981c4952850364c8 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 17:00:51 +0000 Subject: [PATCH 67/70] remove plotting tests --- .../triangles/test_coordinate_implementation.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 9732edd1..97ced67a 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -56,13 +56,11 @@ def test_trivial_triangles(one_triangle): ) -def test_above(plot): +def test_above(): triangles = CoordinateArrayTriangles( coordinates=np.array([[0, 1]]), side_length=1.0, ) - plot(triangles) - plot(triangles.up_sample(), color="red") assert np.all( triangles.up_sample().triangles == [ @@ -140,7 +138,7 @@ def test_up_sample_upside_down(upside_down): ) -def test_up_sample_twice(one_triangle, plot): +def _test_up_sample_twice(one_triangle, plot): plot(one_triangle) one = one_triangle.up_sample() two = one.up_sample() @@ -206,11 +204,9 @@ def test_upside_down_neighborhood(upside_down): ) -def test_complicated(plot, one_triangle): +def _test_complicated(plot, one_triangle): triangles = one_triangle.neighborhood().neighborhood() up_sampled = triangles.up_sample() - plot(up_sampled, color="red") - plot(triangles) def test_vertices(one_triangle): From b831b90b796a19d3f6a054cf2cfdd4d746271355 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 8 Nov 2024 17:01:08 +0000 Subject: [PATCH 68/70] remove plotting tests --- .../structures/triangles/test_coordinate_implementation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_autoarray/structures/triangles/test_coordinate_implementation.py b/test_autoarray/structures/triangles/test_coordinate_implementation.py index 97ced67a..d7c7a338 100644 --- a/test_autoarray/structures/triangles/test_coordinate_implementation.py +++ b/test_autoarray/structures/triangles/test_coordinate_implementation.py @@ -239,7 +239,7 @@ def test_with_vertices(one_triangle): assert np.all(triangle.triangles == [[[1.0, 0.0], [0.5, 1.0], [0.0, 0.0]]]) -def test_multiple_with_vertices(one_triangle, plot): +def _test_multiple_with_vertices(one_triangle, plot): up_sampled = one_triangle.up_sample() plot(up_sampled.with_vertices(2 * up_sampled.vertices).triangles.tolist()) From 4ed1a13d4d68498d9083ebb8a5f390943a945db5 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 11 Nov 2024 09:24:24 +0000 Subject: [PATCH 69/70] fix uniqueness --- autoarray/structures/triangles/jax_coordinate_array.py | 1 + .../structures/triangles/test_coordinate_jax.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/autoarray/structures/triangles/jax_coordinate_array.py b/autoarray/structures/triangles/jax_coordinate_array.py index 525c5b62..30aec4eb 100644 --- a/autoarray/structures/triangles/jax_coordinate_array.py +++ b/autoarray/structures/triangles/jax_coordinate_array.py @@ -130,6 +130,7 @@ def neighborhood(self) -> "CoordinateArrayTriangles": new_coordinates, axis=0, size=4 * self.coordinates.shape[0], + fill_value=np.nan, ), side_length=self.side_length, flipped=self.flipped, diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 1414e224..7dde53a7 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -103,3 +103,11 @@ def test_up_sample(one_triangle): ] ), ) + + +def test_means(one_triangle): + assert len(one_triangle.means) == 1 + + up_sampled = one_triangle.up_sample() + neighborhood = up_sampled.neighborhood() + assert np.count_nonzero(~np.isnan(neighborhood.means)) == 20 From 9704153b39ceb7b81815f6160b568715c0324c55 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 11 Nov 2024 09:36:53 +0000 Subject: [PATCH 70/70] fix area calculation --- .../triangles/abstract_coordinate_array.py | 4 ++-- .../structures/triangles/test_coordinate_jax.py | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/autoarray/structures/triangles/abstract_coordinate_array.py b/autoarray/structures/triangles/abstract_coordinate_array.py index d0cead30..0a5a36b2 100644 --- a/autoarray/structures/triangles/abstract_coordinate_array.py +++ b/autoarray/structures/triangles/abstract_coordinate_array.py @@ -165,7 +165,7 @@ def means(self): @property def area(self): - return (3**0.5 / 4 * self.side_length**2) * len(self.coordinates) + return (3**0.5 / 4 * self.side_length**2) * len(self) def __len__(self): - return len(self.coordinates) + return np.count_nonzero(~np.isnan(self.coordinates).any(axis=1)) diff --git a/test_autoarray/structures/triangles/test_coordinate_jax.py b/test_autoarray/structures/triangles/test_coordinate_jax.py index 7dde53a7..b2270137 100644 --- a/test_autoarray/structures/triangles/test_coordinate_jax.py +++ b/test_autoarray/structures/triangles/test_coordinate_jax.py @@ -1,6 +1,7 @@ from autoarray.numpy_wrapper import jit import pytest +from autoarray.structures.triangles.abstract import HEIGHT_FACTOR from autoarray.structures.triangles.shape import Point try: @@ -110,4 +111,17 @@ def test_means(one_triangle): up_sampled = one_triangle.up_sample() neighborhood = up_sampled.neighborhood() - assert np.count_nonzero(~np.isnan(neighborhood.means)) == 20 + assert np.count_nonzero(~np.isnan(neighborhood.means).any(axis=1)) == 10 + + +ONE_TRIANGLE_AREA = HEIGHT_FACTOR * 0.5 + + +def test_area(one_triangle): + assert one_triangle.area == ONE_TRIANGLE_AREA + assert one_triangle.up_sample().area == ONE_TRIANGLE_AREA + + neighborhood = one_triangle.neighborhood() + assert neighborhood.area == 4 * ONE_TRIANGLE_AREA + assert neighborhood.up_sample().area == 4 * ONE_TRIANGLE_AREA + assert neighborhood.neighborhood().area == 10 * ONE_TRIANGLE_AREA