From 2d231e5b805c6f230635a9335e36fcd11ae38a22 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Sun, 19 Nov 2023 18:31:23 +0000 Subject: [PATCH 1/5] begun adding SubhaloSensitivityResult, nbow need to implement array_2d_from --- autolens/lens/sensitivity.py | 97 ++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 autolens/lens/sensitivity.py diff --git a/autolens/lens/sensitivity.py b/autolens/lens/sensitivity.py new file mode 100644 index 000000000..f90210991 --- /dev/null +++ b/autolens/lens/sensitivity.py @@ -0,0 +1,97 @@ +import numpy as np +from typing import Optional + +import autofit as af +import autoarray as aa +import autogalaxy.plot as aplt + +from autoarray.plot.abstract_plotters import AbstractPlotter + +from autolens.imaging.fit_imaging import FitImaging +from autolens.imaging.plot.fit_imaging_plotters import FitImagingPlotter + + +class SubhaloSensitivityResult(af.SensitivityResult): + def __init__( + self, + result_sensitivity: af.SensitivityResult, + ): + """ + The results of a subhalo sensitivity mapping analysis, where dark matter halos are used to simulate many + strong lens datasets which are fitted to quantify how detectable they are. + + Parameters + ---------- + result_sensitivity + The results of a sensitivity mapping analysis where. + """ + + super().__init__( + results=result_sensitivity.results, + shape=result_sensitivity.shape + ) + + def _array_2d_from(self, values) -> aa.Array2D: + """ + Returns an `Array2D` where the input values are reshaped from list of lists to a 2D array, which is + suitable for plotting. + + For example, this function may return the 2D array of the increases in log evidence for every lens model + fitted with a DM subhalo in the sensitivity mapping compared to the model without a DM subhalo. + + The orientation of the 2D array and its values are chosen to ensure that when this array is plotted, DM + subhalos with positive y and negative x `centre` coordinates appear in the top-left of the image. + + Parameters + ---------- + values_native + The list of list of values which are mapped to the 2D array (e.g. the `log_evidence` difference of every + lens model with a DM subhalo compared to the one without). + + Returns + ------- + The 2D array of values, where the values are mapped from the input list of lists. + """ + values_reshaped = [value for values in values.native for value in values] + + return aa.Array2D.from_yx_and_values( + y=[centre[0] for centre in self.physical_centres_lists], + x=[centre[1] for centre in self.physical_centres_lists], + values=values_reshaped, + pixel_scales=self.physical_step_sizes, + shape_native=self.shape, + ) + + def figure_of_merit_array( + self, + use_log_evidences: bool = True, + remove_zeros: bool = False, + ) -> aa.Array2D: + """ + Returns an `Array2D` where the values are the figure of merit (`log_evidence` or `log_likelihood` difference) + of every lens model on the sensitivity mapping grid. + + Values below zero may be rounded to zero, to prevent the figure of merit map being dominated by low values + + Parameters + ---------- + use_log_evidences + If `True`, the figure of merit values are the log evidences of every lens model on the grid search. + If `False`, they are the log likelihoods. + remove_zeros + If `True`, the figure of merit array is altered so that all values below 0.0 and set to 0.0. For plotting + relative figures of merit for Bayesian model comparison, this is convenient to remove negative values + and produce a clearer visualization of the overlay. + """ + + figures_of_merits = self.figure_of_merits( + use_log_evidences=use_log_evidences, + ) + + if remove_zeros: + figures_of_merits = af.GridList( + values=[fom if fom > 0.0 else 0.0 for fom in figures_of_merits], + shape=figures_of_merits.shape, + ) + + return self._array_2d_from(values=figures_of_merits) From 2582c4f36fe902f9ee02b027278f32813a7e791c Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 24 Nov 2023 10:52:19 +0000 Subject: [PATCH 2/5] pushing for hpc run --- autolens/lens/ray_tracing.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/autolens/lens/ray_tracing.py b/autolens/lens/ray_tracing.py index 0dbf49756..ed5750944 100644 --- a/autolens/lens/ray_tracing.py +++ b/autolens/lens/ray_tracing.py @@ -5,8 +5,6 @@ import autoarray as aa import autogalaxy as ag -from autoconf.dictable import from_dict, to_dict, output_to_json - from autogalaxy.plane.plane import Plane from autogalaxy.profiles.light.snr import LightProfileSNR From e09787d0ebbedf959ced7858ab1d7da273168678 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 1 Dec 2023 12:00:07 +0000 Subject: [PATCH 3/5] Sensitivity module now performs viusalizaiton of tracer --- autolens/interferometer/simulator.py | 1 - autolens/lens/sensitivity.py | 189 +++++++++++++++++++++++++-- autolens/plot/__init__.py | 1 + 3 files changed, 181 insertions(+), 10 deletions(-) diff --git a/autolens/interferometer/simulator.py b/autolens/interferometer/simulator.py index 20f5aac74..f4fc5adb8 100644 --- a/autolens/interferometer/simulator.py +++ b/autolens/interferometer/simulator.py @@ -4,7 +4,6 @@ class SimulatorInterferometer(aa.SimulatorInterferometer): - def via_tracer_from(self, tracer, grid): """ Returns a realistic simulated image by applying effects to a plain simulated image. diff --git a/autolens/lens/sensitivity.py b/autolens/lens/sensitivity.py index f90210991..184fde27a 100644 --- a/autolens/lens/sensitivity.py +++ b/autolens/lens/sensitivity.py @@ -1,20 +1,21 @@ -import numpy as np from typing import Optional +import autofit as af + +from autofit.non_linear.grid.sensitivity.result import SensitivityResult + import autofit as af import autoarray as aa -import autogalaxy.plot as aplt -from autoarray.plot.abstract_plotters import AbstractPlotter +from autolens.lens.ray_tracing import Tracer -from autolens.imaging.fit_imaging import FitImaging -from autolens.imaging.plot.fit_imaging_plotters import FitImagingPlotter +import autolens.plot as aplt -class SubhaloSensitivityResult(af.SensitivityResult): +class SubhaloSensitivityResult(SensitivityResult): def __init__( self, - result_sensitivity: af.SensitivityResult, + result_sensitivity: SensitivityResult, ): """ The results of a subhalo sensitivity mapping analysis, where dark matter halos are used to simulate many @@ -27,8 +28,9 @@ def __init__( """ super().__init__( - results=result_sensitivity.results, - shape=result_sensitivity.shape + samples=result_sensitivity.samples, + perturb_samples=result_sensitivity.perturb_samples, + shape=result_sensitivity.shape, ) def _array_2d_from(self, values) -> aa.Array2D: @@ -95,3 +97,172 @@ def figure_of_merit_array( ) return self._array_2d_from(values=figures_of_merits) + + +class SubhaloSensitivityPlotter: + def __init__( + self, + grid: aa.type.Grid2DLike, + mask: aa.Mask2D, + tracer_perturb: Optional[Tracer] = None, + tracer_no_perturb: Optional[Tracer] = None, + source_image: Optional[aa.Array2D] = None, + result_sensitivity: Optional[SensitivityResult] = None, + mat_plot_2d: aplt.MatPlot2D = aplt.MatPlot2D(), + visuals_2d: aplt.Visuals2D = aplt.Visuals2D(), + include_2d: aplt.Include2D = aplt.Include2D(), + ): + """ + Plots the simulated datasets and results of a sensitivity mapping analysis, where dark matter halos are used + to simulate many strong lens datasets which are fitted to quantify how detectable they are. + + The `mat_plot_1d` and `mat_plot_2d` attributes wrap matplotlib function calls to make the figure. By default, + the settings passed to every matplotlib function called are those specified in + the `config/visualize/mat_wrap/*.ini` files, but a user can manually input values into `MatPlot2D` to + customize the figure's appearance. + + Overlaid on the figure are visuals, contained in the `Visuals1D` and `Visuals2D` objects. Attributes may be + extracted from the `MassProfile` and plotted via the visuals object, if the corresponding entry is `True` in + the `Include1D` or `Include2D` object or the `config/visualize/include.ini` file. + + Parameters + ---------- + tracer + The tracer the plotter plots. + grid + The 2D (y,x) grid of coordinates used to evaluate the tracer's light and mass quantities that are plotted. + mat_plot_1d + Contains objects which wrap the matplotlib function calls that make 1D plots. + visuals_1d + Contains 1D visuals that can be overlaid on 1D plots. + include_1d + Specifies which attributes of the `MassProfile` are extracted and plotted as visuals for 1D plots. + mat_plot_2d + Contains objects which wrap the matplotlib function calls that make 2D plots. + visuals_2d + Contains 2D visuals that can be overlaid on 2D plots. + include_2d + Specifies which attributes of the `MassProfile` are extracted and plotted as visuals for 2D plots. + """ + self.grid = grid + self.mask = mask + self.tracer_perturb = tracer_perturb + self.tracer_no_perturb = tracer_no_perturb + self.source_image = source_image + self.result_sensitivity = result_sensitivity + self.mat_plot_2d = mat_plot_2d + self.visuals_2d = visuals_2d + self.include_2d = include_2d + + def subplot_tracer_images(self): + """ + Output the tracer images of the dataset simulated for sensitivity mapping as a .png subplot. + + This dataset corresponds to a single grid-cell on the sensitivity mapping grid and therefore will be output + many times over the entire sensitivity mapping grid. + + The subplot includes the overall image, the lens galaxy image, the lensed source galaxy image and the source + galaxy image interpolated to a uniform grid. + + Images are masked before visualization, so that they zoom in on the region of interest which is actually + fitted. + """ + + grid = aa.Grid2D.from_mask(mask=self.mask) + + image = self.tracer_perturb.image_2d_from(grid=grid).binned + lens_image = self.tracer_perturb.image_2d_list_from(grid=grid)[0].binned + lensed_source_image = self.tracer_perturb.image_2d_via_input_plane_image_from( + grid=grid, plane_image=self.source_image + ).binned + lensed_source_image_no_perturb = ( + self.tracer_no_perturb.image_2d_via_input_plane_image_from( + grid=grid, plane_image=self.source_image + ).binned + ) + + plotter = aplt.Array2DPlotter( + array=image, + mat_plot_2d=self.mat_plot_2d, + ) + plotter.open_subplot_figure(number_subplots=6) + plotter.set_title("Image") + plotter.figure_2d() + + plotter = aplt.Array2DPlotter( + array=lens_image, + mat_plot_2d=self.mat_plot_2d, + ) + plotter.set_title("Lens Image") + plotter.figure_2d() + + grid = self.mask.derive_grid.unmasked_sub_1 + + visuals_2d = aplt.Visuals2D( + mask=self.mask, + tangential_critical_curves=self.tracer_perturb.tangential_critical_curve_list_from( + grid=grid + ), + radial_critical_curves=self.tracer_perturb.radial_critical_curve_list_from( + grid=grid + ), + ) + + plotter = aplt.Array2DPlotter( + array=lensed_source_image, + mat_plot_2d=self.mat_plot_2d, + visuals_2d=visuals_2d, + ) + plotter.set_title("Lensed Source Image") + plotter.figure_2d() + + visuals_2d = aplt.Visuals2D( + mask=self.mask, + tangential_caustics=self.tracer_perturb.tangential_caustic_list_from( + grid=grid + ), + radial_caustics=self.tracer_perturb.radial_caustic_list_from(grid=grid), + ) + + plotter = aplt.Array2DPlotter( + array=self.source_image.binned, + mat_plot_2d=self.mat_plot_2d, + visuals_2d=visuals_2d, + ) + plotter.set_title("Source Image") + plotter.figure_2d() + + visuals_2d = aplt.Visuals2D( + mask=self.mask, + tangential_critical_curves=self.tracer_no_perturb.tangential_critical_curve_list_from( + grid=grid + ), + radial_critical_curves=self.tracer_no_perturb.radial_critical_curve_list_from( + grid=grid + ), + ) + + plotter = aplt.Array2DPlotter( + array=lensed_source_image, + mat_plot_2d=self.mat_plot_2d, + visuals_2d=visuals_2d, + ) + plotter.set_title("Lensed Source Image (No Subhalo)") + plotter.figure_2d() + + residual_map = ( + lensed_source_image.binned - lensed_source_image_no_perturb.binned + ) + + plotter = aplt.Array2DPlotter( + array=residual_map, + mat_plot_2d=self.mat_plot_2d, + visuals_2d=visuals_2d, + ) + plotter.set_title("Residual Map (Subhalo - No Subhalo)") + plotter.figure_2d() + + plotter.mat_plot_2d.output.subplot_to_figure( + auto_filename=f"subplot_lensed_images" + ) + plotter.close_subplot_figure() diff --git a/autolens/plot/__init__.py b/autolens/plot/__init__.py index f2de7353f..6b0ca6597 100644 --- a/autolens/plot/__init__.py +++ b/autolens/plot/__init__.py @@ -98,3 +98,4 @@ from autolens.point.plot.fit_point_plotters import FitPointDatasetPlotter from autolens.lens.plot.ray_tracing_plotters import TracerPlotter from autolens.lens.subhalo import SubhaloPlotter +from autolens.lens.sensitivity import SubhaloSensitivityPlotter From 80b8f0cb182c14a2aa2e024b04263b68c8d8a514 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 1 Dec 2023 12:03:26 +0000 Subject: [PATCH 4/5] visuals now include convergence --- autolens/lens/sensitivity.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/autolens/lens/sensitivity.py b/autolens/lens/sensitivity.py index 184fde27a..b5b41c5e9 100644 --- a/autolens/lens/sensitivity.py +++ b/autolens/lens/sensitivity.py @@ -171,7 +171,6 @@ def subplot_tracer_images(self): grid = aa.Grid2D.from_mask(mask=self.mask) image = self.tracer_perturb.image_2d_from(grid=grid).binned - lens_image = self.tracer_perturb.image_2d_list_from(grid=grid)[0].binned lensed_source_image = self.tracer_perturb.image_2d_via_input_plane_image_from( grid=grid, plane_image=self.source_image ).binned @@ -189,13 +188,6 @@ def subplot_tracer_images(self): plotter.set_title("Image") plotter.figure_2d() - plotter = aplt.Array2DPlotter( - array=lens_image, - mat_plot_2d=self.mat_plot_2d, - ) - plotter.set_title("Lens Image") - plotter.figure_2d() - grid = self.mask.derive_grid.unmasked_sub_1 visuals_2d = aplt.Visuals2D( @@ -216,6 +208,13 @@ def subplot_tracer_images(self): plotter.set_title("Lensed Source Image") plotter.figure_2d() + plotter = aplt.Array2DPlotter( + array=self.tracer_perturb.convergence_2d_from(grid=grid).binned, + mat_plot_2d=self.mat_plot_2d, + ) + plotter.set_title("Convergence") + plotter.figure_2d() + visuals_2d = aplt.Visuals2D( mask=self.mask, tangential_caustics=self.tracer_perturb.tangential_caustic_list_from( From 58481c4290863f066ad4038dbf146beff6eda78b Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Fri, 1 Dec 2023 12:05:04 +0000 Subject: [PATCH 5/5] swap source and conergene plot locations --- autolens/lens/sensitivity.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/autolens/lens/sensitivity.py b/autolens/lens/sensitivity.py index b5b41c5e9..9f7e90ec5 100644 --- a/autolens/lens/sensitivity.py +++ b/autolens/lens/sensitivity.py @@ -208,13 +208,6 @@ def subplot_tracer_images(self): plotter.set_title("Lensed Source Image") plotter.figure_2d() - plotter = aplt.Array2DPlotter( - array=self.tracer_perturb.convergence_2d_from(grid=grid).binned, - mat_plot_2d=self.mat_plot_2d, - ) - plotter.set_title("Convergence") - plotter.figure_2d() - visuals_2d = aplt.Visuals2D( mask=self.mask, tangential_caustics=self.tracer_perturb.tangential_caustic_list_from( @@ -231,6 +224,13 @@ def subplot_tracer_images(self): plotter.set_title("Source Image") plotter.figure_2d() + plotter = aplt.Array2DPlotter( + array=self.tracer_perturb.convergence_2d_from(grid=grid).binned, + mat_plot_2d=self.mat_plot_2d, + ) + plotter.set_title("Convergence") + plotter.figure_2d() + visuals_2d = aplt.Visuals2D( mask=self.mask, tangential_critical_curves=self.tracer_no_perturb.tangential_critical_curve_list_from(