Skip to content

Commit eda1097

Browse files
committed
merge complete
2 parents f8985ba + d876f51 commit eda1097

33 files changed

+1720
-898
lines changed

autolens/__init__.py

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

124124
conf.instance.register(__file__)
125125

126-
__version__ = "2024.9.21.2"
126+
__version__ = "2024.11.13.2"

autolens/aggregator/tracer.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ def _tracer_from(
4141
galaxies = instance.galaxies
4242

4343
if hasattr(instance, "extra_galaxies"):
44-
galaxies = galaxies + fit.instance.extra_galaxies
44+
if fit.instance.extra_galaxies is not None:
45+
galaxies = galaxies + fit.instance.extra_galaxies
4546

4647
else:
4748
galaxies = fit.instance.galaxies
4849

4950
if hasattr(fit.instance, "extra_galaxies"):
50-
galaxies = galaxies + fit.instance.extra_galaxies
51+
if fit.instance.extra_galaxies is not None:
52+
galaxies = galaxies + fit.instance.extra_galaxies
5153

5254
try:
5355
cosmology = instance.cosmology
@@ -56,17 +58,18 @@ def _tracer_from(
5658

5759
tracer = Tracer(galaxies=galaxies, cosmology=cosmology)
5860

59-
if len(fit.children) > 0:
60-
logger.info(
61-
"""
62-
Using database for a fit with multiple summed Analysis objects.
63-
64-
Tracer objects do not fully support this yet (e.g. model parameters which vary over analyses may be incorrect)
65-
so proceed with caution!
66-
"""
67-
)
68-
69-
return [tracer] * len(fit.children)
61+
if fit.children is not None:
62+
if len(fit.children) > 0:
63+
logger.info(
64+
"""
65+
Using database for a fit with multiple summed Analysis objects.
66+
67+
Tracer objects do not fully support this yet (e.g. model parameters which vary over analyses may be incorrect)
68+
so proceed with caution!
69+
"""
70+
)
71+
72+
return [tracer] * len(fit.children)
7073

7174
return [tracer]
7275

autolens/analysis/result.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
import os
23
import numpy as np
34
from typing import Optional, Union
@@ -15,6 +16,8 @@
1516
from autolens.lens.tracer import Tracer
1617
from autolens.point.solver import PointSolver
1718

19+
logger = logging.getLogger(__name__)
20+
1821

1922
class Result(AgResultDataset):
2023
@property
@@ -101,8 +104,76 @@ def image_plane_multiple_image_positions(self) -> aa.Grid2DIrregular:
101104
source_plane_coordinate=self.source_plane_centre.in_list[0],
102105
)
103106

107+
if multiple_images.shape[0] == 1:
108+
return self.image_plane_multiple_image_positions_for_single_image_from()
109+
104110
return aa.Grid2DIrregular(values=multiple_images)
105111

112+
def image_plane_multiple_image_positions_for_single_image_from(
113+
self, increments: int = 20
114+
) -> aa.Grid2DIrregular:
115+
"""
116+
If the standard point solver only locates one multiple image, finds one or more additional images, which are
117+
not technically multiple image in the point source regime, but are close enough to it they can be used
118+
in a position threshold likelihood.
119+
120+
This is performed by incrementally moving the source-plane centre's coordinates towards the centre of the
121+
source-plane at (0.0", 0.0"). This ensures that the centre will eventually go inside the caustic, where
122+
multiple images are formed.
123+
124+
To move the source-plane centre, the original source-plane centre is multiplied by a factor that decreases
125+
from 1.0 to 0.0 in increments of 1/increments. For example, if the source-plane centre is (1.0", -0.5") and
126+
the `factor` is 0.5, the input source-plane centre is (0.5", -0.25").
127+
128+
The multiple images are always computed for the same mass model, thus they will always be valid multiple images
129+
for the model being fitted, but as the factor decrease the multiple images may move furhter from their observed
130+
positions.
131+
132+
Parameters
133+
----------
134+
increments
135+
The number of increments the source-plane centre is moved to compute multiple images.
136+
"""
137+
138+
logger.info(
139+
"""
140+
Could not find multiple images for maximum likelihood lens model.
141+
142+
Incrementally moving source centre inwards towards centre of source-plane until caustic crossing occurs
143+
and multiple images are formed.
144+
"""
145+
)
146+
147+
grid = self.analysis.dataset.mask.derive_grid.all_false
148+
149+
centre = self.source_plane_centre.in_list[0]
150+
151+
solver = PointSolver.for_grid(
152+
grid=grid,
153+
pixel_scale_precision=0.001,
154+
)
155+
156+
for i in range(1, increments):
157+
factor = 1.0 - (1.0 * (i / increments))
158+
159+
multiple_images = solver.solve(
160+
tracer=self.max_log_likelihood_tracer,
161+
source_plane_coordinate=(centre[0] * factor, centre[1] * factor),
162+
)
163+
164+
if multiple_images.shape[0] > 1:
165+
return aa.Grid2DIrregular(values=multiple_images)
166+
167+
logger.info(
168+
"""
169+
Could not find multiple images for maximum likelihood lens model, even after incrementally moving the source
170+
centre inwards to the centre of the source-plane.
171+
172+
Set the multiple image postiions to two images at (1.0", 1.0") so code continues to run.
173+
"""
174+
)
175+
return aa.Grid2DIrregular(values=[(1.0, 1.0), (1.0, 1.0)])
176+
106177
def positions_threshold_from(
107178
self,
108179
factor=1.0,
@@ -168,7 +239,47 @@ def positions_likelihood_from(
168239
minimum_threshold=None,
169240
use_resample=False,
170241
positions: Optional[aa.Grid2DIrregular] = None,
242+
mass_centre_radial_distance_min: float = None,
171243
) -> Union[PositionsLHPenalty, PositionsLHResample]:
244+
"""
245+
Returns a `PositionsLH` object from the result of a lens model-fit, where the maximum log likelihood mass
246+
and source models are used to determine the multiple image positions in the image-plane and source-plane
247+
and ray-trace them to the source-plane to determine the threshold.
248+
249+
In chained fits, for example the SLaM pipelines, this means that a simple initial fit (e.g. SIE mass model,
250+
parametric source) can be used to determine the multiple image positions and threshold for a more complex
251+
subsequent fit (e.g. power-law mass model, pixelized source).
252+
253+
The mass model central image is removed from the solution, as this is rarely physically observed and therefore
254+
should not be included in the likelihood penalty or resampling. It is removed by setting a positive
255+
magnification threshold in the `PointSolver`. For strange lens models the central image may still be
256+
solved for, in which case the `mass_centre_radial_distance_min` parameter can be used to remove it.
257+
258+
Parameters
259+
----------
260+
factor
261+
The value the computed threshold is multiplied by to make the position threshold larger or smaller than the
262+
maximum log likelihood model's threshold.
263+
minimum_threshold
264+
The output threshold is rounded up to this value if it is below it, to avoid extremely small threshold
265+
values.
266+
use_resample
267+
If `False` the `PositionsLH` object is created using the `PositionsLHPenalty` class, which uses the
268+
threshold to apply a penalty term to the likelihood. If `True` the `PositionsLH` object is created using
269+
the `PositionsLHResample` class, which resamples the positions to the threshold.
270+
positions
271+
If input, these positions are used instead of the computed multiple image positions from the lens mass
272+
model.
273+
mass_centre_radial_distance_min
274+
The minimum radial distance from the mass model centre that a multiple image position must be to be
275+
included in the likelihood penalty or resampling. If `None` all positions are used. This is an additional
276+
method to remove central images that may make it through the point solver's magnification threshold.
277+
278+
Returns
279+
-------
280+
The `PositionsLH` object used to apply a likelihood penalty or resample the positions.
281+
"""
282+
172283
if os.environ.get("PYAUTOFIT_TEST_MODE") == "1":
173284
return None
174285

@@ -177,6 +288,18 @@ def positions_likelihood_from(
177288
if positions is None
178289
else positions
179290
)
291+
292+
if mass_centre_radial_distance_min is not None:
293+
mass_centre = self.max_log_likelihood_tracer.extract_attribute(
294+
cls=ag.mp.MassProfile, attr_name="centre"
295+
)
296+
297+
distances = positions.distances_to_coordinate_from(
298+
coordinate=mass_centre[0]
299+
)
300+
301+
positions = positions[distances > mass_centre_radial_distance_min]
302+
180303
threshold = self.positions_threshold_from(
181304
factor=factor, minimum_threshold=minimum_threshold, positions=positions
182305
)

autolens/config/visualize/plots.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
psf: false
88
positions: # Settings for plots with resampling image-positions on (e.g. the image).
99
image_with_positions: true
10+
point_dataset: # Settings for plots of point source datasets (e.g. PointDatasetPlotter).
11+
subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the data, noise-map, etc.)?
12+
positions: false # Plot the positions of th multiple images of the point source in the image plane?
13+
fluxes: false # Plot the fluxes of the multiple images of the point source in the image plane?
1014
fit: # Settings for plots of all fits (e.g. FitImagingPlotter, FitInterferometerPlotter).
1115
subplot_fit: true # Plot subplot of all fit quantities for any dataset (e.g. the model data, residual-map, etc.)?
1216
all_at_end_png: true # Plot all individual plots listed below as .png (even if False)?
@@ -25,6 +29,9 @@
2529
subtracted_images_of_planes: false # Plot individual plots of each plane's subtracted image?
2630
plane_images_of_planes: false # Plot individual plots of each plane's image (e.g. in the source plane)?
2731
fit_imaging: {} # Settings for plots of fits to imaging datasets (e.g. FitImagingPlotter).
32+
fit_point_dataset: # Settings for plots of fits to point source datasets (e.g. FitPointDatasetPlotter).
33+
positions: false # Plot the positions of th multiple images of the point source in the image plane?
34+
fluxes: false # Plot the fluxes of the multiple images of the point source in the image plane?
2835
tracer: # Settings for plots of tracers (e.g. TracerPlotter).
2936
subplot_tracer: true # Plot subplot of all quantities in each tracer (e.g. images, convergence)?
3037
all_at_end_png: true # Plot all individual plots listed below as .png (even if False)?
@@ -48,7 +55,7 @@
4855
all_at_end_png: true # Plot all individual plots listed below as .png (even if False)?
4956
all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)?
5057
all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)?
51-
errors: false
58+
reconstruction_noise_map: false
5259
reconstructed_image: false
5360
reconstruction: false
5461
regularization_weights: false

autolens/imaging/plot/fit_imaging_plotters.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ def figures_2d_of_planes(
148148
subtracted_image: bool = False,
149149
model_image: bool = False,
150150
plane_image: bool = False,
151+
plane_noise_map: bool = False,
152+
plane_signal_to_noise_map: bool = False,
151153
use_source_vmax: bool = False,
152154
zoom_to_brightest: bool = True,
153155
interpolate_to_uniform: bool = False,
@@ -178,6 +180,14 @@ def figures_2d_of_planes(
178180
Whether to make a 2D plot (via `imshow`) of the image of a plane in its source-plane (e.g. unlensed).
179181
Depending on how the fit is performed, this could either be an image of light profiles of the reconstruction
180182
of an `Inversion`.
183+
plane_noise_map
184+
Whether to make a 2D plot of the noise-map of a plane in its source-plane, where the
185+
noise map can only be computed when a pixelized source reconstruction is performed and they correspond to
186+
the noise map in each reconstructed pixel as given by the inverse curvature matrix.
187+
plane_signal_to_noise_map
188+
Whether to make a 2D plot of the signal-to-noise map of a plane in its source-plane,
189+
where the signal-to-noise map values can only be computed when a pixelized source reconstruction and they
190+
are the ratio of reconstructed flux to error in each pixel.
181191
use_source_vmax
182192
If `True`, the maximum value of the lensed source (e.g. in the image-plane) is used to set the `vmax` of
183193
certain plots (e.g. the `data`) in order to ensure the lensed source is visible compared to the lens.
@@ -283,6 +293,8 @@ def figures_2d_of_planes(
283293

284294
elif self.tracer.planes[plane_index].has(cls=aa.Pixelization):
285295

296+
pix = self.tracer.planes[plane_index].cls_list_from(cls=aa.Pixelization)[0]
297+
286298
inversion_plotter = self.inversion_plotter_of_plane(
287299
plane_index=plane_index
288300
)
@@ -294,8 +306,41 @@ def figures_2d_of_planes(
294306
interpolate_to_uniform=interpolate_to_uniform
295307
)
296308

297-
if use_source_vmax:
298-
self.mat_plot_2d.cmap.kwargs.pop("vmax")
309+
if use_source_vmax:
310+
try:
311+
self.mat_plot_2d.cmap.kwargs.pop("vmax")
312+
except KeyError:
313+
pass
314+
315+
if plane_noise_map:
316+
317+
if self.tracer.planes[plane_index].has(cls=aa.Pixelization):
318+
319+
inversion_plotter = self.inversion_plotter_of_plane(
320+
plane_index=plane_index
321+
)
322+
323+
inversion_plotter.figures_2d_of_pixelization(
324+
pixelization_index=0,
325+
reconstruction_noise_map=True,
326+
zoom_to_brightest=zoom_to_brightest,
327+
interpolate_to_uniform=interpolate_to_uniform
328+
)
329+
330+
if plane_signal_to_noise_map:
331+
332+
if self.tracer.planes[plane_index].has(cls=aa.Pixelization):
333+
334+
inversion_plotter = self.inversion_plotter_of_plane(
335+
plane_index=plane_index
336+
)
337+
338+
inversion_plotter.figures_2d_of_pixelization(
339+
pixelization_index=0,
340+
signal_to_noise_map=True,
341+
zoom_to_brightest=zoom_to_brightest,
342+
interpolate_to_uniform=interpolate_to_uniform
343+
)
299344

300345
def subplot_of_planes(self, plane_index: Optional[int] = None):
301346
"""
@@ -614,7 +659,12 @@ def subplot_mappings_of_plane(self, plane_index: Optional[int] = None, auto_file
614659
"total_mappings_pixels"
615660
]
616661

617-
pix_indexes = inversion_plotter.inversion.brightest_pixel_list_from(
662+
mapper = inversion_plotter.inversion.cls_list_from(cls=aa.AbstractMapper)[0]
663+
mapper_valued = aa.MapperValued(
664+
values=inversion_plotter.inversion.reconstruction_dict[mapper],
665+
mapper=mapper,
666+
)
667+
pix_indexes = mapper_valued.max_pixel_list_from(
618668
total_pixels=total_pixels, filter_neighbors=True
619669
)
620670

0 commit comments

Comments
 (0)