Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
45a35f4
mask_1d_util
Jammy2211 Apr 2, 2025
a715118
Merge branch 'main' of github.com:Jammy2211/PyAutoArray into main
Jammy2211 Apr 2, 2025
5de1760
Merge pull request #167 from Jammy2211/feature/unit_tests
Jammy2211 Apr 9, 2025
869a8e1
Revert "Feature/unit tests"
Jammy2211 Apr 9, 2025
d68d5a0
Merge pull request #171 from Jammy2211/revert-167-feature/unit_tests
Jammy2211 Apr 9, 2025
214e940
Merge branch 'main' of github.com:Jammy2211/PyAutoArray into main
Jammy2211 Apr 9, 2025
4e75879
black
Jammy2211 Apr 12, 2025
61bbee2
readd numba utils
Jammy2211 Apr 12, 2025
23fc632
update cahe
Jammy2211 Apr 12, 2025
6ba9566
added zoom_2d methods to standalone module
Apr 21, 2025
54b94ef
all Zoom unit tests pass
Apr 21, 2025
50e4506
refactor plot in zoom
Apr 21, 2025
a0e4d0e
clean up config
Apr 21, 2025
8dd8755
aatempted docstrings but wait until internet for AI
Apr 21, 2025
093a7aa
Array2D uses zoomed_around_mask
Apr 21, 2025
8d0a157
clean up zoom in Array2D
Apr 21, 2025
a6d9d50
clean up use of extent in zoom tests
Apr 21, 2025
73fcaca
fix unit tes
Apr 21, 2025
ddd8b74
docstrings and remove mask_unmasked
Apr 21, 2025
6b363da
added fits_are_Zoomed to config
Apr 21, 2025
ff27742
move zoom array to Zoom2D
Apr 21, 2025
c4bca0e
add new functions to Zoom2D
Apr 29, 2025
b93f511
Merge pull request #172 from Jammy2211/feature/zoom_array
Jammy2211 Apr 29, 2025
0f913ac
added Array2DRGB
Apr 30, 2025
c488f38
rgb test
Apr 30, 2025
473dc10
black
Apr 30, 2025
3e6127d
Merge pull request #173 from Jammy2211/feature/rgb
Jammy2211 Apr 30, 2025
83b6ad4
missing init
rhayes777 May 2, 2025
56cc2f7
refactor tests to use parametrize
rhayes777 May 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v2
- uses: actions/cache@v3
id: cache-pip
with:
path: ~/.cache/pip
Expand Down
5 changes: 5 additions & 0 deletions autoarray/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from . import fixtures
from . import mock as m
from .numba_util import profile_func
from .preloads import Preloads
from .dataset import preprocess
from .dataset.abstract.dataset import AbstractDataset
from .dataset.abstract.w_tilde import AbstractWTilde
Expand Down Expand Up @@ -52,8 +53,11 @@
from .mask.derive.mask_2d import DeriveMask2D
from .mask.derive.grid_1d import DeriveGrid1D
from .mask.derive.grid_2d import DeriveGrid2D
from .mask.derive.zoom_2d import Zoom2D
from .mask.mask_1d import Mask1D
from .mask.mask_2d import Mask2D
from .operators.convolver import Convolver
from .operators.convolver import Convolver
from .operators.transformer import TransformerDFT
from .operators.transformer import TransformerNUFFT
from .operators.over_sampling.decorator import over_sample
Expand All @@ -62,6 +66,7 @@
from .layout.layout import Layout2D
from .structures.arrays.uniform_1d import Array1D
from .structures.arrays.uniform_2d import Array2D
from .structures.arrays.rgb import Array2DRGB
from .structures.arrays.irregular import ArrayIrregular
from .structures.grids.uniform_1d import Grid1D
from .structures.grids.uniform_2d import Grid2D
Expand Down
20 changes: 10 additions & 10 deletions autoarray/abstract_ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

from abc import ABC
from abc import abstractmethod
import jax.numpy as jnp
import numpy as np

from autoconf.fitsable import output_to_fits

from autoarray.numpy_wrapper import register_pytree_node, Array
from autoarray.numpy_wrapper import numpy as npw, register_pytree_node, Array

from typing import TYPE_CHECKING

Expand Down Expand Up @@ -83,7 +83,7 @@ def __init__(self, array):

def invert(self):
new = self.copy()
new._array = jnp.invert(new._array)
new._array = np.invert(new._array)
return new

@classmethod
Expand All @@ -105,7 +105,7 @@ def instance_flatten(cls, instance):
@staticmethod
def flip_hdu_for_ds9(values):
if conf.instance["general"]["fits"]["flip_for_ds9"]:
return jnp.flipud(values)
return np.flipud(values)
return values

@classmethod
Expand All @@ -114,11 +114,11 @@ def instance_unflatten(cls, aux_data, children):
Unflatten a tuple of attributes (i.e. a pytree) into an instance of an autoarray class
"""
instance = cls.__new__(cls)
for key, value in zip(aux_data, children):
for key, value in zip(aux_data, children[1:]):
setattr(instance, key, value)
return instance

def with_new_array(self, array: jnp.ndarray) -> "AbstractNDArray":
def with_new_array(self, array: np.ndarray) -> "AbstractNDArray":
"""
Copy this object but give it a new array.

Expand Down Expand Up @@ -165,7 +165,7 @@ def __iter__(self):

@to_new_array
def sqrt(self):
return jnp.sqrt(self._array)
return np.sqrt(self._array)

@property
def array(self):
Expand Down Expand Up @@ -331,13 +331,13 @@ def __getitem__(self, item):
result = self._array[item]
if isinstance(item, slice):
result = self.with_new_array(result)
if isinstance(result, jnp.ndarray):
if isinstance(result, np.ndarray):
result = self.with_new_array(result)
return result

def __setitem__(self, key, value):
if isinstance(key, (jnp.ndarray, AbstractNDArray, Array)):
self._array = jnp.where(key, value, self._array)
if isinstance(key, (np.ndarray, AbstractNDArray, Array)):
self._array = npw.where(key, value, self._array)
else:
self._array[key] = value

Expand Down
3 changes: 3 additions & 0 deletions autoarray/config/general.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ pixelization:
voronoi_nn_max_interpolation_neighbors: 300
structures:
native_binned_only: false # If True, data structures are only stored in their native and binned format. This is used to reduce memory usage in autocti.
test:
preloads_check_threshold: 1.0 # If the figure of merit of a fit with and without preloads is greater than this threshold, the check preload test fails and an exception raised for a model-fit.

3 changes: 3 additions & 0 deletions autoarray/config/grids.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
radial_minimum:
function_name:
class_name: 1.0e-08
1 change: 0 additions & 1 deletion autoarray/config/visualize/general.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ general:
log10_min_value: 1.0e-4 # If negative values are being plotted on a log10 scale, values below this value are rounded up to it (e.g. to remove negative values).
log10_max_value: 1.0e99 # If positive values are being plotted on a log10 scale, values above this value are rounded down to it (e.g. to prevent white blobs).
zoom_around_mask: true # If True, plots of data structures with a mask automatically zoom in the masked region.
disable_zoom_for_fits: true # If True, the zoom-in around the masked region is disabled when outputting .fits files, which is useful to retain the same dimensions as the input data.
inversion:
reconstruction_vmax_factor: 0.5
total_mappings_pixels : 8 # The number of source pixels used when plotting the subplot_mappings of a pixelization.
Expand Down
3 changes: 3 additions & 0 deletions autoarray/config/visualize/plots.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# For example, if `plots: fit: subplot_fit=True``, the ``fit_dataset.png`` subplot file will
# be plotted every time visualization is performed.

subplot_format: [png] # Output format of all subplots, can be png, pdf or both (e.g. [png, pdf])
fits_are_zoomed: true # If true, output .fits files are zoomed in on the center of the unmasked region image, saving hard-disk space.

dataset: # Settings for plots of all datasets (e.g. ImagingPlotter, InterferometerPlotter).
subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the data, noise-map, etc.)?
imaging: # Settings for plots of imaging datasets (e.g. ImagingPlotter)
Expand Down
58 changes: 37 additions & 21 deletions autoarray/dataset/imaging/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from autoarray.dataset.grids import GridsDataset
from autoarray.dataset.imaging.w_tilde import WTildeImaging
from autoarray.structures.arrays.uniform_2d import Array2D
from autoarray.operators.convolver import Convolver
from autoarray.structures.arrays.kernel_2d import Kernel2D
from autoarray.mask.mask_2d import Mask2D
from autoarray import type as ty
Expand All @@ -29,7 +30,7 @@ def __init__(
noise_covariance_matrix: Optional[np.ndarray] = None,
over_sample_size_lp: Union[int, Array2D] = 4,
over_sample_size_pixelization: Union[int, Array2D] = 4,
pad_for_psf: bool = False,
pad_for_convolver: bool = False,
use_normalized_psf: Optional[bool] = True,
check_noise_map: bool = True,
):
Expand Down Expand Up @@ -76,7 +77,7 @@ def __init__(
over_sample_size_pixelization
How over sampling is performed for the grid which is associated with a pixelization, which is therefore
passed into the calculations performed in the `inversion` module.
pad_for_psf
pad_for_convolver
The PSF convolution may extend beyond the edges of the image mask, which can lead to edge effects in the
convolved image. If `True`, the image and noise-map are padded to ensure the PSF convolution does not
extend beyond the edge of the image.
Expand All @@ -89,9 +90,9 @@ def __init__(

self.unmasked = None

self.pad_for_psf = pad_for_psf
self.pad_for_convolver = pad_for_convolver

if pad_for_psf and psf is not None:
if pad_for_convolver and psf is not None:
try:
data.mask.derive_mask.blurring_from(
kernel_shape_native=psf.shape_native
Expand Down Expand Up @@ -161,15 +162,11 @@ def __init__(

if psf is not None and use_normalized_psf:
psf = Kernel2D.no_mask(
values=psf.native._array, pixel_scales=psf.pixel_scales, normalize=True
values=psf.native, pixel_scales=psf.pixel_scales, normalize=True
)

self.psf = psf

if psf is not None:
if psf.mask.shape[0] % 2 == 0 or psf.mask.shape[1] % 2 == 0:
raise exc.KernelException("Kernel2D Kernel2D must be odd")

@cached_property
def grids(self):
return GridsDataset(
Expand All @@ -179,6 +176,25 @@ def grids(self):
psf=self.psf,
)

@cached_property
def convolver(self):
"""
Returns a `Convolver` from a mask and 2D PSF kernel.

The `Convolver` stores in memory the array indexing between the mask and PSF, enabling efficient 2D PSF
convolution of images and matrices used for linear algebra calculations (see `operators.convolver`).

This uses lazy allocation such that the calculation is only performed when the convolver is used, ensuring
efficient set up of the `Imaging` class.

Returns
-------
Convolver
The convolver given the masked imaging data's mask and PSF.
"""

return Convolver(mask=self.mask, kernel=self.psf)

@cached_property
def w_tilde(self):
"""
Expand All @@ -204,9 +220,9 @@ def w_tilde(self):
indexes,
lengths,
) = inversion_imaging_util.w_tilde_curvature_preload_imaging_from(
noise_map_native=np.array(self.noise_map.native.array).astype("float64"),
kernel_native=np.array(self.psf.native.array).astype("float64"),
native_index_for_slim_index=np.array(self.mask.derive_indexes.native_for_slim).astype("int"),
noise_map_native=np.array(self.noise_map.native),
kernel_native=np.array(self.psf.native),
native_index_for_slim_index=self.mask.derive_indexes.native_for_slim,
)

return WTildeImaging(
Expand Down Expand Up @@ -354,7 +370,7 @@ def apply_mask(self, mask: Mask2D) -> "Imaging":
noise_covariance_matrix=noise_covariance_matrix,
over_sample_size_lp=over_sample_size_lp,
over_sample_size_pixelization=over_sample_size_pixelization,
pad_for_psf=True,
pad_for_convolver=True,
)

dataset.unmasked = unmasked_dataset
Expand Down Expand Up @@ -409,20 +425,20 @@ def apply_noise_scaling(
"""

if signal_to_noise_value is None:
noise_map = np.array(self.noise_map.native.array)
noise_map[mask.array == False] = noise_value
noise_map = self.noise_map.native
noise_map[mask == False] = noise_value
else:
noise_map = np.where(
mask == False,
np.median(self.data.native.array[mask.derive_mask.edge == False])
np.median(self.data.native[mask.derive_mask.edge == False])
/ signal_to_noise_value,
self.noise_map.native.array,
self.noise_map.native,
)

if should_zero_data:
data = np.where(np.invert(mask.array), 0.0, self.data.native.array)
data = np.where(np.invert(mask), 0.0, self.data.native)
else:
data = self.data.native.array
data = self.data.native

data_unmasked = Array2D.no_mask(
values=data,
Expand All @@ -447,7 +463,7 @@ def apply_noise_scaling(
noise_covariance_matrix=self.noise_covariance_matrix,
over_sample_size_lp=self.over_sample_size_lp,
over_sample_size_pixelization=self.over_sample_size_pixelization,
pad_for_psf=False,
pad_for_convolver=False,
check_noise_map=False,
)

Expand Down Expand Up @@ -495,7 +511,7 @@ def apply_over_sampling(
over_sample_size_lp=over_sample_size_lp or self.over_sample_size_lp,
over_sample_size_pixelization=over_sample_size_pixelization
or self.over_sample_size_pixelization,
pad_for_psf=False,
pad_for_convolver=False,
check_noise_map=False,
)

Expand Down
2 changes: 1 addition & 1 deletion autoarray/dataset/interferometer/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,5 +276,5 @@ def output_to_fits(
)

@property
def psf(self):
def convolver(self):
return None
14 changes: 7 additions & 7 deletions autoarray/dataset/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,15 @@ def edges_from(image, no_edges):
edges = []

for edge_no in range(no_edges):
top_edge = image.native.array[edge_no, edge_no : image.shape_native[1] - edge_no]
bottom_edge = image.native.array[
top_edge = image.native[edge_no, edge_no : image.shape_native[1] - edge_no]
bottom_edge = image.native[
image.shape_native[0] - 1 - edge_no,
edge_no : image.shape_native[1] - edge_no,
]
left_edge = image.native.array[
left_edge = image.native[
edge_no + 1 : image.shape_native[0] - 1 - edge_no, edge_no
]
right_edge = image.native.array[
right_edge = image.native[
edge_no + 1 : image.shape_native[0] - 1 - edge_no,
image.shape_native[1] - 1 - edge_no,
]
Expand Down Expand Up @@ -328,7 +328,7 @@ def background_noise_map_via_edges_from(image, no_edges):
def psf_with_odd_dimensions_from(psf):
"""
If the PSF kernel has one or two even-sized dimensions, return a PSF object where the kernel has odd-sized
dimensions (odd-sized dimensions are required for 2D convolution).
dimensions (odd-sized dimensions are required by a *Convolver*).

Kernels are rescaled using the scikit-image routine rescale, which performs rescaling via an interpolation
routine. This may lead to loss of accuracy in the PSF kernel and it is advised that users, where possible,
Expand Down Expand Up @@ -517,8 +517,8 @@ def noise_map_with_signal_to_noise_limit_from(
noise_map_limit = np.where(
(signal_to_noise_map.native > signal_to_noise_limit)
& (noise_limit_mask == False),
np.abs(data.native.array) / signal_to_noise_limit,
noise_map.native.array,
np.abs(data.native) / signal_to_noise_limit,
noise_map.native,
)

mask = Mask2D.all_false(
Expand Down
11 changes: 11 additions & 0 deletions autoarray/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ class PlottingException(Exception):
pass


class PreloadsException(Exception):
"""
Raises exceptions associated with the `preloads.py` module and `Preloads` class.

For example if the preloaded quantities lead to a change in figure of merit of a fit compared to a fit without
preloading.
"""

pass


class ProfilingException(Exception):
"""
Raises exceptions associated with in-built profiling tools (e.g. the `profile_func` decorator).
Expand Down
Loading
Loading