Skip to content

Commit 311f7cd

Browse files
authored
Merge pull request #143 from Jammy2211/feature/coordinate_jax
feature/coordinate jax
2 parents 9331755 + f95c28f commit 311f7cd

File tree

18 files changed

+965
-18
lines changed

18 files changed

+965
-18
lines changed

autoarray/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,4 @@
9898

9999
conf.instance.register(__file__)
100100

101-
__version__ = "2024.9.21.2"
101+
__version__ = "2024.11.6.1"

autoarray/dataset/imaging/dataset.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ def from_fits(
213213
psf_path: Optional[Union[Path, str]] = None,
214214
psf_hdu: int = 0,
215215
noise_covariance_matrix: Optional[np.ndarray] = None,
216+
check_noise_map: bool = True,
216217
over_sampling: Optional[OverSamplingDataset] = OverSamplingDataset(),
217218
) -> "Imaging":
218219
"""
@@ -250,6 +251,8 @@ def from_fits(
250251
The hdu the noise map is contained in the .fits file specified by `noise_map_path`.
251252
noise_covariance_matrix
252253
A noise-map covariance matrix representing the covariance between noise in every `data` value.
254+
check_noise_map
255+
If True, the noise-map is checked to ensure all values are above zero.
253256
over_sampling
254257
The over sampling schemes which divide the grids into sub grids of smaller pixels within their host image
255258
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(
280283
noise_map=noise_map,
281284
psf=psf,
282285
noise_covariance_matrix=noise_covariance_matrix,
286+
check_noise_map=check_noise_map,
283287
over_sampling=over_sampling,
284288
)
285289

@@ -377,6 +381,7 @@ def apply_noise_scaling(self, mask: Mask2D, noise_value: float = 1e8) -> "Imagin
377381
noise_covariance_matrix=self.noise_covariance_matrix,
378382
over_sampling=self.over_sampling,
379383
pad_for_convolver=False,
384+
check_noise_map=False
380385
)
381386

382387
logger.info(
@@ -429,6 +434,7 @@ def apply_over_sampling(
429434
psf=self.psf,
430435
over_sampling=over_sampling,
431436
pad_for_convolver=False,
437+
check_noise_map=False
432438
)
433439

434440
def output_to_fits(

autoarray/inversion/pixelization/image_mesh/hilbert.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,6 @@ def image_plane_mesh_grid_from(
277277
-------
278278
279279
"""
280-
281280
if not mask.is_circular:
282281
raise exc.PixelizationException(
283282
"""

autoarray/inversion/plot/inversion_plotters.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def figures_2d_of_pixelization(
122122
reconstructed_image: bool = False,
123123
reconstruction: bool = False,
124124
errors: bool = False,
125+
signal_to_noise_map: bool = False,
125126
regularization_weights: bool = False,
126127
sub_pixels_per_image_pixels: bool = False,
127128
mesh_pixels_per_image_pixels: bool = False,
@@ -146,6 +147,8 @@ def figures_2d_of_pixelization(
146147
Whether to make a 2D plot (via `imshow` or `fill`) of the mapper's source-plane reconstruction.
147148
errors
148149
Whether to make a 2D plot (via `imshow` or `fill`) of the mapper's source-plane errors.
150+
signal_to_noise_map
151+
Whether to make a 2D plot (via `imshow` or `fill`) of the mapper's source-plane signal-to-noise-map.
149152
sub_pixels_per_image_pixels
150153
Whether to make a 2D plot (via `imshow`) of the number of sub pixels per image pixels in the 2D
151154
data's mask.
@@ -242,6 +245,23 @@ def figures_2d_of_pixelization(
242245
except TypeError:
243246
pass
244247

248+
if signal_to_noise_map:
249+
try:
250+
signal_to_noise_values = (
251+
self.inversion.reconstruction_dict[mapper_plotter.mapper]
252+
/ self.inversion.errors_dict[mapper_plotter.mapper]
253+
)
254+
255+
mapper_plotter.plot_source_from(
256+
pixel_values=signal_to_noise_values,
257+
auto_labels=AutoLabels(
258+
title="Signal To Noise Map", filename="signal_to_noise_map"
259+
),
260+
)
261+
262+
except TypeError:
263+
pass
264+
245265
# TODO : NEed to understand why this raises an error in voronoi_drawer.
246266

247267
if regularization_weights:

autoarray/plot/abstract_plotters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def __init__(
3737

3838
self.subplot_figsize = None
3939

40+
4041
def set_title(self, label):
4142
if self.mat_plot_1d is not None:
4243
self.mat_plot_1d.title.manual_label = label

autoarray/plot/mat_plot/two_d.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,12 +297,6 @@ def plot_array(
297297
"a pixel scales attribute."
298298
)
299299

300-
# Hack being used for BELLSABSORB with Tania, remove later and code up automatic method to make it
301-
# so that if a mask is irregular and zooming in creates white edges, that instead it doesnt have the eddge.
302-
# This could just be a matplotlib settings to change the edge color?
303-
304-
# array = array.resized_from(new_shape=(401, 401))
305-
306300
array, extent = self.zoomed_array_and_extent_from(array=array)
307301

308302
ax = None

autoarray/structures/decorators/to_grid.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def via_grid_2d_irr(self, result) -> Union[Grid2DIrregular, List[Grid2DIrregular
4747
----------
4848
result
4949
The input result (e.g. of a decorated function) that is converted to an Grid2DIrregular or list of
50-
Grid2DIrregular objects.
50+
`Grid2DIrregular` objects.
5151
"""
5252
if not isinstance(result, list):
5353
return Grid2DIrregular(values=result)

autoarray/structures/grids/irregular_2d.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
from autoarray.numpy_wrapper import np
1+
import logging
22
from typing import List, Optional, Tuple, Union
33

4+
from autoarray.numpy_wrapper import np
45
from autoarray.abstract_ndarray import AbstractNDArray
56
from autoarray.geometry.geometry_2d_irregular import Geometry2DIrregular
67
from autoarray.mask.mask_2d import Mask2D
@@ -10,6 +11,8 @@
1011
from autoarray.structures.grids import grid_2d_util
1112
from autoarray.geometry import geometry_util
1213

14+
logger = logging.getLogger(__name__)
15+
1316

1417
class Grid2DIrregular(AbstractNDArray):
1518
def __init__(self, values: Union[np.ndarray, List]):
@@ -351,9 +354,12 @@ def pixel_scale(self) -> float:
351354
if self.pixel_scales[0] == self.pixel_scales[1]:
352355
return self.pixel_scales[0]
353356
else:
354-
raise exc.GridException(
355-
"Cannot return a pixel_scale for a grid where each dimension has a "
356-
"different pixel scale (e.g. pixel_scales[0] != pixel_scales[1])"
357+
logger.warning(
358+
f"""
359+
The `Grid2DIrregular` has pixel scales of {self.pixel_scales}, which are not the same in both
360+
dimensions. This means that the pixel scale of the grid is not a single value and may cause
361+
issues with calculations that assume a uniform pixel scale.
362+
"""
357363
)
358364

359365
@classmethod

autoarray/structures/triangles/abstract.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ def __init__(
2929
self.indices = indices
3030
self.vertices = vertices
3131

32+
def __len__(self):
33+
return len(self.triangles)
34+
3235
@property
3336
def area(self) -> float:
3437
"""
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
from abc import ABC, abstractmethod
2+
3+
import numpy as np
4+
5+
from autoarray.structures.triangles.abstract import HEIGHT_FACTOR, AbstractTriangles
6+
from autoconf import cached_property
7+
8+
9+
class AbstractCoordinateArray(ABC):
10+
def __init__(
11+
self,
12+
coordinates: np.ndarray,
13+
side_length: float,
14+
x_offset: float = 0.0,
15+
y_offset: float = 0.0,
16+
flipped: bool = False,
17+
):
18+
"""
19+
Represents a set of triangles by integer coordinates.
20+
21+
Parameters
22+
----------
23+
coordinates
24+
Integer x y coordinates for each triangle.
25+
side_length
26+
The side length of the triangles.
27+
flipped
28+
Whether the triangles are flipped upside down.
29+
y_offset
30+
An y_offset to apply to the y coordinates so that up-sampled triangles align.
31+
"""
32+
self.coordinates = coordinates
33+
self.side_length = side_length
34+
self.flipped = flipped
35+
36+
self.scaling_factors = np.array(
37+
[0.5 * side_length, HEIGHT_FACTOR * side_length]
38+
)
39+
self.x_offset = x_offset
40+
self.y_offset = y_offset
41+
42+
@cached_property
43+
def triangles(self) -> np.ndarray:
44+
"""
45+
The vertices of the triangles as an Nx3x2 array.
46+
"""
47+
centres = self.centres
48+
return np.stack(
49+
(
50+
centres
51+
+ self.flip_array
52+
* np.array(
53+
[0.0, 0.5 * self.side_length * HEIGHT_FACTOR],
54+
),
55+
centres
56+
+ self.flip_array
57+
* np.array(
58+
[0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR]
59+
),
60+
centres
61+
+ self.flip_array
62+
* np.array(
63+
[-0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR]
64+
),
65+
),
66+
axis=1,
67+
)
68+
69+
@property
70+
def centres(self) -> np.ndarray:
71+
"""
72+
The centres of the triangles.
73+
"""
74+
return self.scaling_factors * self.coordinates + np.array(
75+
[self.x_offset, self.y_offset]
76+
)
77+
78+
@cached_property
79+
def flip_mask(self) -> np.ndarray:
80+
"""
81+
A mask for the triangles that are flipped.
82+
83+
Every other triangle is flipped so that they tessellate.
84+
"""
85+
mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0
86+
if self.flipped:
87+
mask = ~mask
88+
return mask
89+
90+
@cached_property
91+
@abstractmethod
92+
def flip_array(self) -> np.ndarray:
93+
"""
94+
An array of 1s and -1s to flip the triangles.
95+
"""
96+
97+
def __iter__(self):
98+
return iter(self.triangles)
99+
100+
@cached_property
101+
@abstractmethod
102+
def _vertices_and_indices(self):
103+
pass
104+
105+
@property
106+
def vertices(self) -> np.ndarray:
107+
"""
108+
The unique vertices of the triangles.
109+
"""
110+
return self._vertices_and_indices[0]
111+
112+
@property
113+
def indices(self) -> np.ndarray:
114+
"""
115+
The indices of the vertices of the triangles.
116+
"""
117+
return self._vertices_and_indices[1]
118+
119+
def with_vertices(self, vertices: np.ndarray) -> AbstractTriangles:
120+
"""
121+
Create a new set of triangles with the vertices replaced.
122+
123+
Parameters
124+
----------
125+
vertices
126+
The new vertices to use.
127+
128+
Returns
129+
-------
130+
The new set of triangles with the new vertices.
131+
"""
132+
133+
@classmethod
134+
def for_limits_and_scale(
135+
cls,
136+
x_min: float,
137+
x_max: float,
138+
y_min: float,
139+
y_max: float,
140+
scale: float = 1.0,
141+
**_,
142+
):
143+
coordinates = []
144+
x = x_min
145+
while x < x_max:
146+
y = y_min
147+
while y < y_max:
148+
coordinates.append([x, y])
149+
y += scale
150+
x += scale
151+
152+
x_mean = (x_min + x_max) / 2
153+
y_mean = (y_min + y_max) / 2
154+
155+
return cls(
156+
coordinates=np.array(coordinates),
157+
side_length=scale,
158+
x_offset=x_mean,
159+
y_offset=y_mean,
160+
)
161+
162+
@property
163+
def means(self):
164+
return np.mean(self.triangles, axis=1)
165+
166+
@property
167+
def area(self):
168+
return (3**0.5 / 4 * self.side_length**2) * len(self)
169+
170+
def __len__(self):
171+
return np.count_nonzero(~np.isnan(self.coordinates).any(axis=1))

0 commit comments

Comments
 (0)