Skip to content

Commit 82f48a6

Browse files
authored
Merge pull request #580 from PyAutoLabs/feature/weak-modeling
feat: AnalysisWeak — weak lensing modeling (weak series step 4)
2 parents cdcd2c7 + 8a0733d commit 82f48a6

8 files changed

Lines changed: 448 additions & 0 deletions

File tree

autolens/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
from .point.solver.shape_solver import ShapeSolver
128128
from .weak.dataset import WeakDataset
129129
from .weak.fit import FitWeak
130+
from .weak.model.analysis import AnalysisWeak
130131
from .weak.simulator import SimulatorShearYX
131132

132133
from . import exc

autolens/config/visualize/plots.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ point_dataset: # Settings for plots of point source
5757

5858
fit_point_dataset: {} # Settings for plots of fits to point source datasets (e.g. FitPointDatasetPlotter).
5959

60+
weak_dataset: # Settings for plots of weak lensing shear catalogues (e.g. PlotterWeak).
61+
subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the shear field, noise-map, etc.)?
62+
63+
fit_weak: {} # Settings for plots of fits to weak lensing shear catalogues (e.g. PlotterWeak).
64+
6065
fit_ellipse: # Settings for plots of ellipse fitting fits (e.g. FitEllipse)
6166
data : true # Plot the data of the ellipse fit?
6267
data_no_ellipse: true # Plot the data without the black data ellipses, which obscure noisy data?

autolens/weak/model/__init__.py

Whitespace-only changes.

autolens/weak/model/analysis.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
"""
2+
Analysis class for fitting a ``Tracer`` model to a weak-lensing shear catalogue.
3+
4+
``AnalysisWeak`` implements the ``log_likelihood_function`` called by a ``PyAutoFit``
5+
non-linear search at each iteration. It:
6+
7+
1. Constructs a ``Tracer`` from the current model instance.
8+
2. Calls ``FitWeak`` to compare the tracer's model shear field (evaluated at the
9+
catalogue's galaxy positions via ``LensCalc.shear_yx_2d_via_hessian_from``) against
10+
the observed ``WeakDataset``.
11+
3. Returns the fit's log likelihood as the figure of merit.
12+
13+
It also manages result output (``ResultWeak``) and on-the-fly visualisation
14+
(``VisualizerWeak``).
15+
"""
16+
import autofit as af
17+
import autogalaxy as ag
18+
19+
from autogalaxy.analysis.analysis.analysis import Analysis as AgAnalysis
20+
21+
from autolens.analysis.analysis.lens import AnalysisLens
22+
from autolens.weak.dataset import WeakDataset
23+
from autolens.weak.fit import FitWeak
24+
from autolens.weak.model.result import ResultWeak
25+
from autolens.weak.model.visualizer import VisualizerWeak
26+
27+
28+
class AnalysisWeak(AgAnalysis, AnalysisLens):
29+
Visualizer = VisualizerWeak
30+
Result = ResultWeak
31+
32+
def __init__(
33+
self,
34+
dataset: WeakDataset,
35+
cosmology: ag.cosmo.LensingCosmology = None,
36+
title_prefix: str = None,
37+
use_jax: bool = False,
38+
**kwargs,
39+
):
40+
"""
41+
Fits a lens model to a weak-lensing shear catalogue via a non-linear search.
42+
43+
The `Analysis` class defines the `log_likelihood_function` which fits the model to the dataset and returns the
44+
log likelihood value defining how well the model fitted the data.
45+
46+
It handles many other tasks, such as visualization, outputting results to hard-disk and storing results in
47+
a format that can be loaded after the model-fit is complete.
48+
49+
This class is used for model-fits which fit lens mass models to `WeakDataset` shear catalogues — the
50+
weak-lensing analogue of `AnalysisImaging` / `AnalysisPoint`. Each background galaxy in the catalogue
51+
contributes two independent shear measurements (gamma_1 and gamma_2), which `FitWeak` compares against
52+
the model shear field of the `Tracer`.
53+
54+
`use_jax` defaults to `False` because `FitWeak` is a NumPy-only fit (its `model_shear` is cached via
55+
`functools.cached_property` and its statistics use `np.asarray`); JAX support requires pytree
56+
registration of `FitWeak` and an `xp`-threaded fit path, which is deliberate future work.
57+
58+
Parameters
59+
----------
60+
dataset
61+
The `WeakDataset` that is fitted by the model, containing the observed per-galaxy shear
62+
measurements, their positions and the per-galaxy noise.
63+
cosmology
64+
The Cosmology assumed for this analysis.
65+
title_prefix
66+
A string that is added before the title of all figures output by visualization, for example to
67+
put the name of the dataset and galaxy in the title.
68+
"""
69+
super().__init__(cosmology=cosmology, use_jax=use_jax, **kwargs)
70+
71+
AnalysisLens.__init__(self=self, cosmology=cosmology, use_jax=use_jax)
72+
73+
self.dataset = dataset
74+
75+
self.title_prefix = title_prefix
76+
77+
def log_likelihood_function(self, instance):
78+
"""
79+
Given an instance of the model, where the model parameters are set via a non-linear search, fit the model
80+
instance to the weak-lensing shear catalogue.
81+
82+
This function returns a log likelihood which is used by the non-linear search to guide the model-fit.
83+
84+
For this analysis class, this function performs the following steps:
85+
86+
1) Extracts all galaxies from the model instance and sets up a `Tracer`, which includes ordering the galaxies
87+
by redshift to set up each `Plane`.
88+
89+
2) Uses the `Tracer` to create a `FitWeak` object, which evaluates the tracer's shear field at the
90+
catalogue's galaxy positions (via the same `LensCalc.shear_yx_2d_via_hessian_from` primitive the
91+
`SimulatorShearYX` uses) and compares it to the observed shears.
92+
93+
3) Returns the fit's log likelihood — a Gaussian likelihood over the N x 2 independent shear components.
94+
95+
Parameters
96+
----------
97+
instance
98+
An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
99+
via a non-linear search).
100+
101+
Returns
102+
-------
103+
float
104+
The log likelihood indicating how well this model instance fitted the weak-lensing data.
105+
"""
106+
return self.fit_from(instance=instance).log_likelihood
107+
108+
def fit_from(self, instance) -> FitWeak:
109+
"""
110+
Given a model instance create a `FitWeak` object.
111+
112+
This function is used in the `log_likelihood_function` to fit the model to the weak-lensing data and
113+
compute the log likelihood.
114+
115+
Parameters
116+
----------
117+
instance
118+
An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
119+
via a non-linear search).
120+
121+
Returns
122+
-------
123+
The fit of the lens model to the weak-lensing shear catalogue.
124+
"""
125+
tracer = self.tracer_via_instance_from(
126+
instance=instance,
127+
)
128+
129+
return FitWeak(
130+
dataset=self.dataset,
131+
tracer=tracer,
132+
)
133+
134+
def save_attributes(self, paths: af.DirectoryPaths):
135+
"""
136+
Before the non-linear search begins, this routine saves attributes of the `Analysis` object to the `files`
137+
folder such that they can be loaded after the analysis using PyAutoFit's database and aggregator tools.
138+
139+
For this analysis, it outputs the following:
140+
141+
- The weak-lensing shear catalogue as a readable .json file.
142+
143+
It is common for these attributes to be loaded by many of the template aggregator functions given in the
144+
`aggregator` modules. For example, when using the database tools to perform a fit, the default behaviour is for
145+
the dataset, settings and other attributes necessary to perform the fit to be loaded via the pickle files
146+
output by this function.
147+
148+
Parameters
149+
----------
150+
paths
151+
The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
152+
visualization, and the pickled objects used by the aggregator output by this function.
153+
"""
154+
ag.output_to_json(
155+
obj=self.dataset,
156+
file_path=paths._files_path / "dataset.json",
157+
)

autolens/weak/model/plotter.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from autolens.analysis.plotter import Plotter
2+
from autolens.analysis.plotter import plot_setting
3+
4+
from autolens.weak.dataset import WeakDataset
5+
from autolens.weak.fit import FitWeak
6+
from autolens.weak.plot.weak_dataset_plots import subplot_weak_dataset
7+
from autolens.weak.plot.fit_weak_plots import subplot_fit_weak
8+
from autolens.weak.plot.fit_weak_plots import subplot_fit_quick as subplot_fit_quick_weak
9+
10+
11+
class PlotterWeak(Plotter):
12+
def dataset_weak(self, dataset: WeakDataset):
13+
"""
14+
Output visualization of a `WeakDataset` shear catalogue.
15+
16+
Parameters
17+
----------
18+
dataset
19+
The weak-lensing dataset which is visualized.
20+
"""
21+
22+
def should_plot(name):
23+
return plot_setting(section=["weak_dataset"], name=name)
24+
25+
output_path = str(self.image_path)
26+
fmt = self.fmt
27+
28+
if should_plot("subplot_dataset"):
29+
subplot_weak_dataset(
30+
dataset,
31+
output_path=output_path,
32+
output_format=fmt,
33+
title_prefix=self.title_prefix,
34+
)
35+
36+
def fit_weak(self, fit: FitWeak, quick_update: bool = False):
37+
"""
38+
Visualizes a `FitWeak` object.
39+
40+
Parameters
41+
----------
42+
fit
43+
The maximum log likelihood `FitWeak` of the non-linear search.
44+
quick_update
45+
If `True`, a lighter-weight quick-update subplot is output instead of the full fit subplot.
46+
"""
47+
48+
def should_plot(name):
49+
return plot_setting(section=["fit", "fit_weak"], name=name)
50+
51+
output_path = str(self.image_path)
52+
fmt = self.fmt
53+
54+
if quick_update:
55+
subplot_fit_quick_weak(
56+
fit,
57+
output_path=output_path,
58+
output_format=fmt,
59+
title_prefix=self.title_prefix,
60+
)
61+
return
62+
63+
if should_plot("subplot_fit"):
64+
subplot_fit_weak(
65+
fit,
66+
output_path=output_path,
67+
output_format=fmt,
68+
title_prefix=self.title_prefix,
69+
)

autolens/weak/model/result.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import autoarray as aa
2+
3+
from autolens.analysis.result import Result
4+
5+
6+
class ResultWeak(Result):
7+
@property
8+
def grid(self):
9+
return aa.Grid2D.uniform(shape_native=(100, 100), pixel_scales=0.1)
10+
11+
@property
12+
def max_log_likelihood_fit(self):
13+
return self.analysis.fit_from(instance=self.instance)

autolens/weak/model/visualizer.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import autofit as af
2+
import autogalaxy as ag
3+
4+
from autolens.weak.model.plotter import PlotterWeak
5+
6+
7+
class VisualizerWeak(af.Visualizer):
8+
@staticmethod
9+
def visualize_before_fit(
10+
analysis,
11+
paths: af.AbstractPaths,
12+
model: af.AbstractPriorModel,
13+
):
14+
"""
15+
PyAutoFit calls this function immediately before the non-linear search begins.
16+
17+
It visualizes objects which do not change throughout the model fit like the dataset.
18+
19+
Parameters
20+
----------
21+
paths
22+
The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
23+
visualization and the pickled objects used by the aggregator output by this function.
24+
model
25+
The model object, which includes model components representing the galaxies that are fitted to
26+
the weak-lensing data.
27+
"""
28+
29+
plotter = PlotterWeak(
30+
image_path=paths.image_path, title_prefix=analysis.title_prefix
31+
)
32+
33+
plotter.dataset_weak(dataset=analysis.dataset)
34+
35+
@staticmethod
36+
def visualize(
37+
analysis,
38+
paths: af.DirectoryPaths,
39+
instance: af.ModelInstance,
40+
during_analysis: bool,
41+
quick_update: bool = False,
42+
):
43+
"""
44+
Output images of the maximum log likelihood model inferred by the model-fit. This function is called throughout
45+
the non-linear search at regular intervals, and therefore provides on-the-fly visualization of how well the
46+
model-fit is going.
47+
48+
The visualization performed by this function includes:
49+
50+
- Images of the best-fit `Tracer`, including the convergence and potential of its mass profiles over
51+
the extent of the shear catalogue.
52+
53+
- Images of the best-fit `FitWeak`, including the data, model and residual shear fields and the
54+
chi-squared map of its fit to the weak-lensing data.
55+
56+
The images output by this function are customized using the file `config/visualize/plots.yaml`.
57+
58+
Parameters
59+
----------
60+
paths
61+
The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
62+
visualization, and the pickled objects used by the aggregator output by this function.
63+
instance
64+
An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
65+
via a non-linear search).
66+
"""
67+
fit = analysis.fit_for_visualization(instance=instance)
68+
69+
plotter = PlotterWeak(
70+
image_path=paths.image_path, title_prefix=analysis.title_prefix
71+
)
72+
73+
plotter.fit_weak(fit=fit, quick_update=quick_update)
74+
75+
if quick_update:
76+
return
77+
78+
tracer = fit.tracer
79+
80+
grid = ag.Grid2D.from_extent(
81+
extent=fit.dataset.extent_from(), shape_native=(100, 100)
82+
)
83+
84+
plotter.tracer(
85+
tracer=tracer,
86+
grid=grid,
87+
)
88+
plotter.galaxies(
89+
galaxies=tracer.galaxies,
90+
grid=grid,
91+
)

0 commit comments

Comments
 (0)