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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions autogalaxy/config/priors/light/linear/point_source.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
PointSource:
centre_0:
type: Gaussian
mean: 0.0
sigma: 0.3
width_modifier:
type: Absolute
value: 0.05
limits:
lower: -inf
upper: inf
centre_1:
type: Gaussian
mean: 0.0
sigma: 0.3
width_modifier:
type: Absolute
value: 0.05
limits:
lower: -inf
upper: inf
31 changes: 31 additions & 0 deletions autogalaxy/config/priors/light/standard/point_source.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
PointSource:
centre_0:
type: Gaussian
mean: 0.0
sigma: 0.3
width_modifier:
type: Absolute
value: 0.05
limits:
lower: -inf
upper: inf
centre_1:
type: Gaussian
mean: 0.0
sigma: 0.3
width_modifier:
type: Absolute
value: 0.05
limits:
lower: -inf
upper: inf
intensity:
type: LogUniform
lower_limit: 1.0e-06
upper_limit: 1000000.0
width_modifier:
type: Relative
value: 0.5
limits:
lower: 0.0
upper: inf
34 changes: 33 additions & 1 deletion autogalaxy/galaxy/galaxies.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
In a typical modeling workflow, a list of fitted galaxies is always wrapped in a `Galaxies` object, which is
then passed to a `Fit*` class (e.g. `FitImaging`) for comparison against the observed data.
"""

import numpy as np
from typing import Dict, List, Optional, Tuple, Type, Union

Expand Down Expand Up @@ -109,6 +110,26 @@ def image_2d_list_from(
for galaxy in self
]

def image_2d_list_unbinned_from(
self, grid: aa.Grid2D, xp=np, operated_only: Optional[bool] = None
) -> List[np.ndarray]:
return [
galaxy.image_2d_unbinned_from(grid=grid, xp=xp, operated_only=operated_only)
for galaxy in self
]

def image_2d_unbinned_from(
self, grid: aa.Grid2D, xp=np, operated_only: Optional[bool] = None
) -> np.ndarray:
image_2d_list = self.image_2d_list_unbinned_from(
grid=grid, xp=xp, operated_only=operated_only
)

if image_2d_list:
return sum(image_2d_list)

return xp.zeros((grid.over_sampled.shape[0],))

@aa.decorators.to_array
def image_2d_from(
self, grid: aa.type.Grid2DLike, xp=np, operated_only: Optional[bool] = None
Expand Down Expand Up @@ -141,7 +162,7 @@ def galaxy_image_2d_dict_from(
grid: aa.type.Grid2DLike,
xp=np,
operated_only: Optional[bool] = None,
) -> {Galaxy: np.ndarray}:
) -> Dict[Galaxy, np.ndarray]:
"""
Returns a dictionary associating every `Galaxy` object with its corresponding 2D image, using the instance
of each galaxy as the dictionary keys.
Expand Down Expand Up @@ -175,6 +196,17 @@ def galaxy_image_2d_dict_from(

return galaxy_image_2d_dict

def galaxy_image_2d_dict_unbinned_from(
self,
grid: aa.Grid2D,
xp=np,
operated_only: Optional[bool] = None,
) -> Dict[Galaxy, np.ndarray]:
image_2d_list = self.image_2d_list_unbinned_from(
grid=grid, xp=xp, operated_only=operated_only
)
return {galaxy: image_2d_list[index] for index, galaxy in enumerate(self)}

@aa.decorators.to_vector_yx
def deflections_yx_2d_from(
self, grid: aa.type.Grid2DLike, xp=np, **kwargs
Expand Down
26 changes: 26 additions & 0 deletions autogalaxy/galaxy/galaxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
The `Galaxies` class (in `galaxies.py`) wraps a list of `Galaxy` objects and provides the same aggregate
interface over the whole ensemble.
"""

from typing import Dict, List, Optional, Type, Union

import numpy as np
Expand Down Expand Up @@ -210,6 +211,31 @@ def image_2d_list_from(
)
]

def image_2d_list_unbinned_from(
self, grid: aa.Grid2D, xp=np, operated_only: Optional[bool] = None
) -> List[np.ndarray]:
"""Return each non-linear profile image before over-sampling binning."""
return [
light_profile.image_2d_unbinned_from(
grid=grid, xp=xp, operated_only=operated_only
)
for light_profile in self.cls_list_from(
cls=LightProfile, cls_filtered=LightProfileLinear
)
]

def image_2d_unbinned_from(
self, grid: aa.Grid2D, xp=np, operated_only: Optional[bool] = None
) -> np.ndarray:
image_2d_list = self.image_2d_list_unbinned_from(
grid=grid, xp=xp, operated_only=operated_only
)

if image_2d_list:
return sum(image_2d_list)

return xp.zeros((grid.over_sampled.shape[0],))

@aa.decorators.to_array
def image_2d_from(
self,
Expand Down
104 changes: 77 additions & 27 deletions autogalaxy/operate/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
(`LightProfile`, `Galaxy`, `Galaxies`, `Tracer`) to expose a consistent API for blurring and Fourier
transforming images.
"""

from __future__ import annotations
import numpy as np
from typing import TYPE_CHECKING, Dict, List, Optional
Expand Down Expand Up @@ -56,6 +57,18 @@ def image_2d_from(
"""
raise NotImplementedError

def image_2d_unbinned_from(
self, grid: aa.Grid2D, xp=np, operated_only: Optional[bool] = None
):
"""Evaluate on every over-sampled coordinate without binning.

Subclasses that need the parent grid's mask or over-sampling metadata
override this method. The fallback preserves the historical behaviour.
"""
return self.image_2d_from(
grid=grid.over_sampled, xp=xp, operated_only=operated_only
)

def has(self, cls) -> bool:
"""
Returns `True` if any attribute of this object is an instance of `cls`, else `False`.
Expand Down Expand Up @@ -129,14 +142,12 @@ def _psf_evaluation_grids_from(grid, blurring_grid, psf):

For a regular PSF the input grids are returned unchanged (evaluation is
binned to image resolution and the mask travels on the arrays). For an
oversampled PSF (`convolve_over_sample_size > 1`) the over-sampled
coordinates are returned — `grid.over_sampled` is a `Grid2DIrregular` in
per-pixel sub-block order, which the `over_sample` decorator passes through
unbinned and which is the oversampled Convolver's input format — along with
the image mask, which those coordinate arrays cannot carry themselves.
oversampled PSF (`convolve_over_sample_size > 1`) the parent grids are also
returned, along with the image mask. The caller requests unbinned values
separately so discrete profiles retain access to the parent-pixel geometry.
"""
if psf.convolve_over_sample_size > 1:
return grid.over_sampled, blurring_grid.over_sampled, grid.mask
return grid, blurring_grid, grid.mask

return grid, blurring_grid, None

Expand Down Expand Up @@ -208,12 +219,20 @@ def blurred_image_2d_from(
)
)

image_2d_not_operated = self.image_2d_from(
grid=evaluation_grid, xp=xp, operated_only=False
)
blurring_image_2d_not_operated = self.image_2d_from(
grid=evaluation_blurring_grid, xp=xp, operated_only=False
)
if convolution_mask is not None:
image_2d_not_operated = self.image_2d_unbinned_from(
grid=evaluation_grid, xp=xp, operated_only=False
)
blurring_image_2d_not_operated = self.image_2d_unbinned_from(
grid=evaluation_blurring_grid, xp=xp, operated_only=False
)
else:
image_2d_not_operated = self.image_2d_from(
grid=evaluation_grid, xp=xp, operated_only=False
)
blurring_image_2d_not_operated = self.image_2d_from(
grid=evaluation_blurring_grid, xp=xp, operated_only=False
)

blurred_image_2d = self._convolved_from_evaluations(
image_2d=image_2d_not_operated,
Expand Down Expand Up @@ -314,8 +333,8 @@ def convolved_padded_image_2d_from(self, grid, psf: aa.Convolver, xp=np):
mask=padded_mask, over_sample_size=over_sample_size
)

image_over_sampled = self.image_2d_from(
grid=padded_grid.over_sampled, xp=xp, operated_only=False
image_over_sampled = self.image_2d_unbinned_from(
grid=padded_grid, xp=xp, operated_only=False
)

convolved = psf.convolved_image_from(
Expand Down Expand Up @@ -433,6 +452,13 @@ class OperateImageList(OperateImage):
def image_2d_list_from(self, grid: aa.Grid2D, operated_only: Optional[bool] = None):
raise NotImplementedError

def image_2d_list_unbinned_from(
self, grid: aa.Grid2D, operated_only: Optional[bool] = None, xp=np
):
return self.image_2d_list_from(
grid=grid.over_sampled, operated_only=operated_only, xp=xp
)

def blurred_image_2d_list_from(
self,
grid: aa.Grid2D,
Expand Down Expand Up @@ -468,12 +494,20 @@ def blurred_image_2d_list_from(
)
)

image_2d_not_operated_list = self.image_2d_list_from(
grid=evaluation_grid, operated_only=False
)
blurring_image_2d_not_operated_list = self.image_2d_list_from(
grid=evaluation_blurring_grid, operated_only=False
)
if convolution_mask is not None:
image_2d_not_operated_list = self.image_2d_list_unbinned_from(
grid=evaluation_grid, operated_only=False
)
blurring_image_2d_not_operated_list = self.image_2d_list_unbinned_from(
grid=evaluation_blurring_grid, operated_only=False
)
else:
image_2d_not_operated_list = self.image_2d_list_from(
grid=evaluation_grid, operated_only=False
)
blurring_image_2d_not_operated_list = self.image_2d_list_from(
grid=evaluation_blurring_grid, operated_only=False
)

blurred_image_2d_list = []

Expand Down Expand Up @@ -597,6 +631,13 @@ def galaxy_image_2d_dict_from(
) -> Dict[Galaxy, aa.Array2D]:
raise NotImplementedError

def galaxy_image_2d_dict_unbinned_from(
self, grid: aa.Grid2D, xp=np, operated_only: Optional[bool] = None
):
return self.galaxy_image_2d_dict_from(
grid=grid.over_sampled, xp=xp, operated_only=operated_only
)

def galaxy_blurred_image_2d_dict_from(
self, grid, psf, blurring_grid, xp=np
) -> Dict[Galaxy, aa.Array2D]:
Expand Down Expand Up @@ -627,13 +668,22 @@ def galaxy_blurred_image_2d_dict_from(
)
)

galaxy_image_2d_not_operated_dict = self.galaxy_image_2d_dict_from(
grid=evaluation_grid, operated_only=False, xp=xp
)

galaxy_blurring_image_2d_not_operated_dict = self.galaxy_image_2d_dict_from(
grid=evaluation_blurring_grid, operated_only=False, xp=xp
)
if convolution_mask is not None:
galaxy_image_2d_not_operated_dict = self.galaxy_image_2d_dict_unbinned_from(
grid=evaluation_grid, operated_only=False, xp=xp
)
galaxy_blurring_image_2d_not_operated_dict = (
self.galaxy_image_2d_dict_unbinned_from(
grid=evaluation_blurring_grid, operated_only=False, xp=xp
)
)
else:
galaxy_image_2d_not_operated_dict = self.galaxy_image_2d_dict_from(
grid=evaluation_grid, operated_only=False, xp=xp
)
galaxy_blurring_image_2d_not_operated_dict = self.galaxy_image_2d_dict_from(
grid=evaluation_blurring_grid, operated_only=False, xp=xp
)

galaxy_image_2d_operated_dict = self.galaxy_image_2d_dict_from(
grid=grid, operated_only=True, xp=xp
Expand Down
36 changes: 24 additions & 12 deletions autogalaxy/profiles/basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
linear inversion (a single matrix solve), making the inference highly efficient regardless of how many basis
components are included.
"""

import numpy as np
from typing import Dict, List, Optional, Union

Expand Down Expand Up @@ -114,11 +115,17 @@ def image_2d_from(
The image of the light profiles in the basis summed together.
"""
return sum(
self.image_2d_list_from(grid=grid, xp=xp, operated_only=operated_only)
self.image_2d_list_from(
grid=grid, xp=xp, operated_only=operated_only, **kwargs
)
)

def image_2d_list_from(
self, grid: aa.type.Grid2DLike, xp=np, operated_only: Optional[bool] = None
self,
grid: aa.type.Grid2DLike,
xp=np,
operated_only: Optional[bool] = None,
**kwargs,
) -> List[aa.Array2D]:
"""
Returns each image of each light profiles in the basis as a list, from a 2D grid of Cartesian (y,x) coordinates.
Expand All @@ -141,18 +148,23 @@ def image_2d_list_from(
-------
The image of the light profiles in the basis summed together.
"""
return [
(
light_profile.image_2d_from(
grid=grid, xp=xp, operated_only=operated_only
image_2d_list = []

for light_profile in self.light_profile_list:
if not isinstance(light_profile, lp_linear.LightProfileLinear):
image_2d_list.append(
light_profile.image_2d_from(
grid=grid, xp=xp, operated_only=operated_only, **kwargs
)
)
if not isinstance(light_profile, lp_linear.LightProfileLinear)
else aa.Array2D(
values=xp.zeros((grid.shape[0],)), mask=grid.mask
elif kwargs.get("binned", True) is False:
image_2d_list.append(xp.zeros((grid.over_sampled.shape[0],)))
else:
image_2d_list.append(
aa.Array2D(values=xp.zeros((grid.shape[0],)), mask=grid.mask)
)
)
for light_profile in self.light_profile_list
]

return image_2d_list

def convergence_2d_from(
self, grid: aa.type.Grid2DLike, xp=np, **kwargs
Expand Down
Loading
Loading