diff --git a/autolens/__init__.py b/autolens/__init__.py index 144e771ff..5b1eaa678 100644 --- a/autolens/__init__.py +++ b/autolens/__init__.py @@ -1,10 +1,9 @@ from autoconf.dictable import from_dict, from_json, output_to_json, to_dict from autoarray import preprocess from autoarray.dataset.imaging.w_tilde import WTildeImaging -from autoarray.dataset.imaging.dataset import Imaging, SettingsImaging +from autoarray.dataset.imaging.dataset import Imaging from autoarray.dataset.interferometer.dataset import ( Interferometer, - SettingsInterferometer, ) from autoarray.mask.mask_1d import Mask1D from autoarray.mask.mask_2d import Mask2D @@ -34,9 +33,9 @@ from autoarray.structures.arrays.irregular import ArrayIrregular from autoarray.structures.grids.uniform_1d import Grid1D from autoarray.structures.grids.uniform_2d import Grid2D -from autoarray.structures.grids.iterate_2d import Grid2DIterate from autoarray.structures.grids.irregular_2d import Grid2DIrregular from autoarray.structures.grids.irregular_2d import Grid2DIrregularUniform +from autoarray.structures.grids.over_sample.iterate import OverSampleIterate from autoarray.structures.mesh.rectangular_2d import Mesh2DRectangular from autoarray.structures.mesh.voronoi_2d import Mesh2DVoronoi from autoarray.structures.mesh.delaunay_2d import Mesh2DDelaunay @@ -47,7 +46,8 @@ from autoarray.structures.visibilities import VisibilitiesNoiseMap from autogalaxy import cosmology as cosmo -from autogalaxy.analysis.adapt_images import AdaptImages +from autogalaxy.analysis.adapt_images.adapt_images import AdaptImages +from autogalaxy.analysis.adapt_images.adapt_image_maker import AdaptImageMaker from autogalaxy.gui.clicker import Clicker from autogalaxy.gui.scribbler import Scribbler from autogalaxy.galaxy.galaxy import Galaxy @@ -57,7 +57,6 @@ from autogalaxy.analysis.clump_model import ClumpModelDisabled from autogalaxy.quantity.dataset_quantity import DatasetQuantity -from autogalaxy.quantity.dataset_quantity import SettingsQuantity from autogalaxy.profiles.geometry_profiles import EllProfile from autogalaxy.profiles import ( point_sources as ps, diff --git a/autolens/aggregator/fit_imaging.py b/autolens/aggregator/fit_imaging.py index 4d8214473..4f0294559 100644 --- a/autolens/aggregator/fit_imaging.py +++ b/autolens/aggregator/fit_imaging.py @@ -15,7 +15,6 @@ def _fit_imaging_from( fit: af.Fit, instance: Optional[af.ModelInstance] = None, - settings_dataset: aa.SettingsImaging = None, settings_inversion: aa.SettingsInversion = None, use_preloaded_grid: bool = True, ) -> List[FitImaging]: @@ -37,7 +36,7 @@ def _fit_imaging_from( is instead used to load lists of the data, noise-map, PSF and mask and combine them into a list of `FitImaging` objects. - The settings of a pixelization of inversion can be overwritten by inputting a `settings_dataset` object, for example + The settings of an inversion can be overwritten by inputting a `settings_inversion` object, for example if you want to use a grid with a different inversion solver. Parameters @@ -47,8 +46,6 @@ def _fit_imaging_from( instance A manual instance that overwrites the max log likelihood instance in fit (e.g. for drawing the instance randomly from the PDF). - settings_dataset - Optionally overwrite the `SettingsImaging` of the `Imaging` object that is created from the fit. settings_inversion Optionally overwrite the `SettingsInversion` of the `Inversion` object that is created from the fit. use_preloaded_grid @@ -57,7 +54,7 @@ def _fit_imaging_from( as the fit. """ - dataset_list = _imaging_from(fit=fit, settings_dataset=settings_dataset) + dataset_list = _imaging_from(fit=fit) tracer_list = _tracer_from(fit=fit, instance=instance) @@ -98,7 +95,6 @@ class FitImagingAgg(af.AggBase): def __init__( self, aggregator: af.Aggregator, - settings_dataset: Optional[aa.SettingsImaging] = None, settings_inversion: Optional[aa.SettingsInversion] = None, use_preloaded_grid: bool = True, ): @@ -132,8 +128,6 @@ def __init__( ---------- aggregator A `PyAutoFit` aggregator object which can load the results of model-fits. - settings_dataset - Optionally overwrite the `SettingsImaging` of the `Imaging` object that is created from the fit. settings_inversion Optionally overwrite the `SettingsInversion` of the `Inversion` object that is created from the fit. use_preloaded_grid @@ -143,7 +137,6 @@ def __init__( """ super().__init__(aggregator=aggregator) - self.settings_dataset = settings_dataset self.settings_inversion = settings_inversion self.use_preloaded_grid = use_preloaded_grid @@ -166,7 +159,6 @@ def object_via_gen_from( return _fit_imaging_from( fit=fit, instance=instance, - settings_dataset=self.settings_dataset, settings_inversion=self.settings_inversion, use_preloaded_grid=self.use_preloaded_grid, ) diff --git a/autolens/aggregator/fit_interferometer.py b/autolens/aggregator/fit_interferometer.py index 2b42ac20e..24e78843f 100644 --- a/autolens/aggregator/fit_interferometer.py +++ b/autolens/aggregator/fit_interferometer.py @@ -16,7 +16,6 @@ def _fit_interferometer_from( fit: af.Fit, instance: Optional[af.ModelInstance] = None, real_space_mask: Optional[aa.Mask2D] = None, - settings_dataset: aa.SettingsInterferometer = None, settings_inversion: aa.SettingsInversion = None, use_preloaded_grid: bool = True, ) -> List[FitInterferometer]: @@ -39,7 +38,7 @@ def _fit_interferometer_from( method is instead used to load lists of the data, noise-map, PSF and mask and combine them into a list of `FitInterferometer` objects. - The settings of a pixelization of inversion can be overwritten by inputting a `settings_dataset` object, for + The settings of an inversion can be overwritten by inputting a `settings_inversion` object, for example if you want to use a grid with a different inversion solver. Parameters @@ -49,8 +48,6 @@ def _fit_interferometer_from( instance A manual instance that overwrites the max log likelihood instance in fit (e.g. for drawing the instance randomly from the PDF). - settings_dataset - Optionally overwrite the `SettingsInterferometer` of the `Interferometer` object that is created from the fit. settings_inversion Optionally overwrite the `SettingsInversion` of the `Inversion` object that is created from the fit. use_preloaded_grid @@ -61,7 +58,6 @@ def _fit_interferometer_from( dataset_list = _interferometer_from( fit=fit, real_space_mask=real_space_mask, - settings_dataset=settings_dataset, ) tracer_list = _tracer_from(fit=fit, instance=instance) @@ -102,7 +98,6 @@ class FitInterferometerAgg(af.AggBase): def __init__( self, aggregator: af.Aggregator, - settings_dataset: Optional[aa.SettingsInterferometer] = None, settings_inversion: Optional[aa.SettingsInversion] = None, use_preloaded_grid: bool = True, real_space_mask: Optional[aa.Mask2D] = None, @@ -133,8 +128,6 @@ def __init__( ---------- aggregator A `PyAutoFit` aggregator object which can load the results of model-fits. - settings_dataset - Optionally overwrite the `SettingsInterferometer` of the `Interferometer` object that is created from the fit. settings_inversion Optionally overwrite the `SettingsInversion` of the `Inversion` object that is created from the fit. use_preloaded_grid @@ -144,7 +137,6 @@ def __init__( """ super().__init__(aggregator=aggregator) - self.settings_dataset = settings_dataset self.settings_inversion = settings_inversion self.use_preloaded_grid = use_preloaded_grid self.real_space_mask = real_space_mask @@ -168,7 +160,6 @@ def object_via_gen_from( return _fit_interferometer_from( fit=fit, instance=instance, - settings_dataset=self.settings_dataset, settings_inversion=self.settings_inversion, use_preloaded_grid=self.use_preloaded_grid, ) diff --git a/autolens/aggregator/subhalo.py b/autolens/aggregator/subhalo.py index f13874b39..62ebe03f2 100644 --- a/autolens/aggregator/subhalo.py +++ b/autolens/aggregator/subhalo.py @@ -10,7 +10,6 @@ class SubhaloAgg: def __init__( self, aggregator_grid_search: af.GridSearchAggregator, - settings_dataset: Optional[aa.SettingsImaging] = None, settings_inversion: Optional[aa.SettingsInversion] = None, use_preloaded_grid: bool = True, ): @@ -20,7 +19,6 @@ def __init__( """ self.aggregator_grid_search = aggregator_grid_search - self.settings_dataset = settings_dataset self.settings_inversion = settings_inversion self.use_preloaded_grid = use_preloaded_grid diff --git a/autolens/analysis/analysis.py b/autolens/analysis/analysis.py index e6cdfc0be..2d77a8208 100644 --- a/autolens/analysis/analysis.py +++ b/autolens/analysis/analysis.py @@ -1,11 +1,7 @@ -import dill import os -import json import logging import numpy as np -from os import path -from scipy.stats import norm -from typing import Dict, Optional, List, Union +from typing import Dict, Optional, Union from autoconf import conf from autoconf.dictable import to_dict, output_to_json @@ -14,14 +10,13 @@ import autoarray as aa import autogalaxy as ag -from autogalaxy.analysis.analysis import AnalysisDataset as AgAnalysisDataset +from autogalaxy.analysis.analysis.dataset import AnalysisDataset as AgAnalysisDataset from autolens.analysis.result import ResultDataset from autolens.analysis.maker import FitMaker from autolens.analysis.preloads import Preloads from autolens.analysis.positions import PositionsLHResample from autolens.analysis.positions import PositionsLHPenalty -from autolens.analysis.visualizer import Visualizer from autolens.lens.tracer import Tracer from autolens.lens import tracer_util @@ -33,7 +28,7 @@ logger.setLevel(level="INFO") -class AnalysisLensing: +class AnalysisLens: def __init__( self, positions_likelihood: Optional[ @@ -139,14 +134,14 @@ def log_likelihood_positions_overwrite_from( raise exc.FitException from e -class AnalysisDataset(AgAnalysisDataset, AnalysisLensing): +class AnalysisDataset(AgAnalysisDataset, AnalysisLens): def __init__( self, dataset, positions_likelihood: Optional[ Union[PositionsLHResample, PositionsLHPenalty] ] = None, - adapt_images: Optional[ag.AdaptImages] = None, + adapt_image_maker: Optional[ag.AdaptImageMaker] = None, cosmology: ag.cosmo.LensingCosmology = ag.cosmo.Planck15(), settings_inversion: aa.SettingsInversion = None, raise_inversion_positions_likelihood_exception: bool = True, @@ -185,12 +180,12 @@ def __init__( super().__init__( dataset=dataset, - adapt_images=adapt_images, + adapt_image_maker=adapt_image_maker, cosmology=cosmology, settings_inversion=settings_inversion, ) - AnalysisLensing.__init__( + AnalysisLens.__init__( self=self, positions_likelihood=positions_likelihood, cosmology=cosmology, diff --git a/autolens/analysis/analysis/__init__.py b/autolens/analysis/analysis/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/autolens/analysis/analysis/dataset.py b/autolens/analysis/analysis/dataset.py new file mode 100644 index 000000000..9d1dc1918 --- /dev/null +++ b/autolens/analysis/analysis/dataset.py @@ -0,0 +1,196 @@ +import os +import logging +from typing import Optional, Union + +from autoconf import conf +from autoconf.dictable import to_dict, output_to_json + +import autofit as af +import autoarray as aa +import autogalaxy as ag + +from autogalaxy.analysis.analysis.dataset import AnalysisDataset as AgAnalysisDataset + +from autolens.analysis.analysis.lens import AnalysisLens +from autolens.analysis.result import ResultDataset +from autolens.analysis.maker import FitMaker +from autolens.analysis.preloads import Preloads +from autolens.analysis.positions import PositionsLHResample +from autolens.analysis.positions import PositionsLHPenalty + +from autolens import exc + +logger = logging.getLogger(__name__) + +logger.setLevel(level="INFO") + + +class AnalysisDataset(AgAnalysisDataset, AnalysisLens): + def __init__( + self, + dataset, + positions_likelihood: Optional[ + Union[PositionsLHResample, PositionsLHPenalty] + ] = None, + adapt_image_maker: Optional[ag.AdaptImageMaker] = None, + cosmology: ag.cosmo.LensingCosmology = ag.cosmo.Planck15(), + settings_inversion: aa.SettingsInversion = None, + raise_inversion_positions_likelihood_exception: bool = True, + ): + """ + Fits a lens model to a dataset via a non-linear search. + + This abstract Analysis class has attributes and methods for all model-fits which fit the model to a dataset + (e.g. imaging or interferometer data). + + This class stores the Cosmology used for the analysis and settings that control aspects of the calculation, + including how pixelizations, inversions and lensing calculations are performed. + + Parameters + ---------- + dataset + The imaging, interferometer or other dataset that the model if fitted too. + positions_likelihood + An object which alters the likelihood function to include a term which accounts for whether + image-pixel coordinates in arc-seconds corresponding to the multiple images of the lensed source galaxy + trace close to one another in the source-plane. + adapt_images + Contains the adapt-images which are used to make a pixelization's mesh and regularization adapt to the + reconstructed galaxy's morphology. + cosmology + The AstroPy Cosmology assumed for this analysis. + settings_inversion + Settings controlling how an inversion is fitted during the model-fit, for example which linear algebra + formalism is used. + raise_inversion_positions_likelihood_exception + If an inversion is used without the `positions_likelihood` it is likely a systematic solution will + be inferred, in which case an Exception is raised before the model-fit begins to inform the user + of this. This exception is not raised if this input is False, allowing the user to perform the model-fit + anyway. + """ + + super().__init__( + dataset=dataset, + adapt_image_maker=adapt_image_maker, + cosmology=cosmology, + settings_inversion=settings_inversion, + ) + + AnalysisLens.__init__( + self=self, + positions_likelihood=positions_likelihood, + cosmology=cosmology, + ) + + self.preloads = self.preloads_cls() + + self.raise_inversion_positions_likelihood_exception = ( + raise_inversion_positions_likelihood_exception + ) + + if os.environ.get("PYAUTOFIT_TEST_MODE") == "1": + self.raise_inversion_positions_likelihood_exception = False + + def modify_before_fit(self, paths: af.DirectoryPaths, model: af.Collection): + """ + This function is called immediately before the non-linear search begins and performs final tasks and checks + before it begins. + + This function: + + - Checks that the adapt-dataset is consistent with previous adapt-datasets if the model-fit is being + resumed from a previous run. + + - Checks the model and raises exceptions if certain critieria are not met. + + Once inherited from it also visualizes objects which do not change throughout the model fit like the dataset. + + Parameters + ---------- + paths + The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored, + visualization and the pickled objects used by the aggregator output by this function. + model + The PyAutoFit model object, which includes model components representing the galaxies that are fitted to + the imaging data. + """ + + super().modify_before_fit(paths=paths, model=model) + + self.raise_exceptions(model=model) + + def raise_exceptions(self, model): + has_pix = model.has_model(cls=(aa.Pixelization,)) or model.has_instance( + cls=(aa.Pixelization,) + ) + + if has_pix: + if ( + self.positions_likelihood is None + and self.raise_inversion_positions_likelihood_exception + and not conf.instance["general"]["test"][ + "disable_positions_lh_inversion_check" + ] + ): + raise exc.AnalysisException( + """ + You have begun a model-fit which reconstructs the source using a pixelization. + However, you have not input a `positions_likelihood` object. + It is likely your model-fit will infer an inaccurate solution. + + Please read the following readthedocs page for a description of why this is, and how to set up + a positions likelihood object: + + https://pyautolens.readthedocs.io/en/latest/general/demagnified_solutions.html + """ + ) + + @property + def preloads_cls(self): + return Preloads + + @property + def fit_maker_cls(self): + return FitMaker + + def save_results(self, paths: af.DirectoryPaths, result: ResultDataset): + """ + At the end of a model-fit, this routine saves attributes of the `Analysis` object to the `files` + folder such that they can be loaded after the analysis using PyAutoFit's database and aggregator tools. + + For this analysis it outputs the following: + + - The maximum log likelihood tracer of the fit. + + Parameters + ---------- + paths + The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored, + visualization and the pickled objects used by the aggregator output by this function. + result + The result of a model fit, including the non-linear search, samples and maximum likelihood tracer. + """ + try: + output_to_json( + obj=result.max_log_likelihood_tracer, + file_path=paths._files_path / "tracer.json", + ) + except AttributeError: + pass + + image_mesh_list = [] + + for galaxy in result.instance.galaxies: + pixelization_list = galaxy.cls_list_from(cls=aa.Pixelization) + + for pixelization in pixelization_list: + if pixelization is not None: + image_mesh_list.append(pixelization.image_mesh) + + if len(image_mesh_list) > 0: + paths.save_json( + name="preload_mesh_grids_of_planes", + object_dict=to_dict( + result.max_log_likelihood_fit.tracer_to_inversion.image_plane_mesh_grid_pg_list + ), + ) diff --git a/autolens/analysis/analysis/lens.py b/autolens/analysis/analysis/lens.py new file mode 100644 index 000000000..b795b8db9 --- /dev/null +++ b/autolens/analysis/analysis/lens.py @@ -0,0 +1,125 @@ +import logging +import numpy as np +from typing import Dict, Optional, Union + +import autofit as af +import autoarray as aa +import autogalaxy as ag + +from autolens.analysis.positions import PositionsLHResample +from autolens.analysis.positions import PositionsLHPenalty +from autolens.lens.tracer import Tracer + +from autolens.lens import tracer_util + +from autolens import exc + +logger = logging.getLogger(__name__) + +logger.setLevel(level="INFO") + + +class AnalysisLens: + def __init__( + self, + positions_likelihood: Optional[ + Union[PositionsLHResample, PositionsLHPenalty] + ] = None, + cosmology: ag.cosmo.LensingCosmology = ag.cosmo.Planck15(), + ): + """ + Analysis classes are used by PyAutoFit to fit a model to a dataset via a non-linear search. + + This abstract Analysis class has attributes and methods for all model-fits which include lensing calculations, + but does not perform a model-fit by itself (and is therefore only inherited from). + + This class stores the Cosmology used for the analysis and settings that control specific aspects of the lensing + calculation, for example how close the brightest pixels in the lensed source have to trace within one another + in the source plane for the model to not be discarded. + + Parameters + ---------- + cosmology + The Cosmology assumed for this analysis. + """ + self.cosmology = cosmology + self.positions_likelihood = positions_likelihood + + def tracer_via_instance_from( + self, + instance: af.ModelInstance, + run_time_dict: Optional[Dict] = None, + ) -> Tracer: + """ + Create a `Tracer` from the galaxies contained in a model instance. + + If PyAutoFit's profiling tools are used with the analysis class, this function may receive a `run_time_dict` + which times how long each set of the model-fit takes to perform. + + Parameters + ---------- + instance + An instance of the model that is fitted to the data by this analysis (whose parameters may have been set + via a non-linear search). + + Returns + ------- + Tracer + An instance of the Tracer class that is used to then fit the dataset. + """ + if hasattr(instance, "perturb"): + instance.galaxies.subhalo = instance.perturb + + # TODO : Need to think about how we do this without building it into the model attribute names. + # TODO : A Subhalo class that extends the Galaxy class maybe? + + if hasattr(instance.galaxies, "subhalo"): + subhalo_centre = tracer_util.grid_2d_at_redshift_from( + galaxies=instance.galaxies, + redshift=instance.galaxies.subhalo.redshift, + grid=aa.Grid2DIrregular(values=[instance.galaxies.subhalo.mass.centre]), + cosmology=self.cosmology, + ) + + instance.galaxies.subhalo.mass.centre = tuple(subhalo_centre.in_list[0]) + + if hasattr(instance, "cosmology"): + cosmology = instance.cosmology + else: + cosmology = self.cosmology + + return Tracer( + galaxies=instance.galaxies, + cosmology=cosmology, + run_time_dict=run_time_dict, + ) + + def log_likelihood_positions_overwrite_from( + self, instance: af.ModelInstance + ) -> Optional[float]: + """ + Call the positions overwrite log likelihood function, which add a penalty term to the likelihood if the + positions of the multiple images of the lensed source do not trace close to one another in the + source plane. + + This function handles a number of exceptions which may occur when calling the overwrite function via the + `PositionsLikelihood` class, so that they do not need to be handled individually for each `Analysis` class. + + Parameters + ---------- + instance + An instance of the model that is being fitted to the data by this analysis (whose parameters have been set + via a non-linear search). + + Returns + ------- + The penalty value of the positions log likelihood, if the positions do not trace close in the source plane, + else a None is returned to indicate there is no penalty. + """ + if self.positions_likelihood is not None: + try: + return self.positions_likelihood.log_likelihood_function_positions_overwrite( + instance=instance, analysis=self + ) + except (ValueError, np.linalg.LinAlgError) as e: + raise exc.FitException from e diff --git a/autolens/analysis/positions.py b/autolens/analysis/positions.py index 2679599f7..873351702 100644 --- a/autolens/analysis/positions.py +++ b/autolens/analysis/positions.py @@ -10,7 +10,7 @@ import autogalaxy as ag -from autogalaxy.analysis.analysis import AnalysisDataset +from autogalaxy.analysis.analysis.dataset import AnalysisDataset from autolens.lens.tracer import Tracer from autolens.point.fit_point.max_separation import FitPositionsSourceMaxSeparation diff --git a/autolens/analysis/result.py b/autolens/analysis/result.py index bccf6098b..d2b187b67 100644 --- a/autolens/analysis/result.py +++ b/autolens/analysis/result.py @@ -143,8 +143,10 @@ def positions_threshold_from( else positions ) + tracer = Tracer(galaxies=self.max_log_likelihood_galaxies) + positions_fits = FitPositionsSourceMaxSeparation( - positions=positions, noise_map=None, tracer=self.max_log_likelihood_tracer + positions=positions, noise_map=None, tracer=tracer ) threshold = factor * np.max( diff --git a/autolens/fixtures.py b/autolens/fixtures.py index bf6b2ee3a..748688a40 100644 --- a/autolens/fixtures.py +++ b/autolens/fixtures.py @@ -150,17 +150,20 @@ def make_adapt_images_7x7(): def make_analysis_imaging_7x7(): - return al.AnalysisImaging( + analysis = al.AnalysisImaging( dataset=make_masked_imaging_7x7(), settings_inversion=aa.SettingsInversion(use_w_tilde=False), - adapt_images=make_adapt_images_7x7(), ) + analysis._adapt_images = make_adapt_images_7x7() + return analysis def make_analysis_interferometer_7(): - return al.AnalysisInterferometer( - dataset=make_interferometer_7(), adapt_images=make_adapt_images_7x7() + analysis = al.AnalysisInterferometer( + dataset=make_interferometer_7(), ) + analysis._adapt_images = make_adapt_images_7x7() + return analysis def make_analysis_point_x2(): diff --git a/autolens/imaging/fit_imaging.py b/autolens/imaging/fit_imaging.py index b42edd13e..88a040169 100644 --- a/autolens/imaging/fit_imaging.py +++ b/autolens/imaging/fit_imaging.py @@ -373,17 +373,9 @@ def refit_with_new_preloads( return FitImaging( dataset=self.dataset, tracer=self.tracer, + sky=self.sky, adapt_images=self.adapt_images, settings_inversion=settings_inversion, preloads=preloads, run_time_dict=run_time_dict, - ) - - @property - def rff(self): - return np.divide( - self.residual_map, - self.data, - # out=np.zeros_like(self.residual_map.native), - # where=np.asarray(self.mask.native) == 0, - ) \ No newline at end of file + ) \ No newline at end of file diff --git a/autolens/imaging/model/analysis.py b/autolens/imaging/model/analysis.py index 631a98871..dc9a67ff8 100644 --- a/autolens/imaging/model/analysis.py +++ b/autolens/imaging/model/analysis.py @@ -9,7 +9,7 @@ from autoarray.exc import PixelizationException -from autolens.analysis.analysis import AnalysisDataset +from autolens.analysis.analysis.dataset import AnalysisDataset from autolens.analysis.preloads import Preloads from autolens.imaging.model.result import ResultImaging from autolens.imaging.model.visualizer import VisualizerImaging @@ -24,6 +24,8 @@ class AnalysisImaging(AnalysisDataset): + Result = ResultImaging + def modify_before_fit(self, paths: af.DirectoryPaths, model: af.Collection): """ This function is called immediately before the non-linear search begins and performs final tasks and checks @@ -280,39 +282,7 @@ def visualize( visualizer.visualize_inversion( inversion=fit.inversion, during_analysis=during_analysis ) - - def make_result( - self, - samples: af.SamplesPDF, - search_internal = None - ) -> ResultImaging: - """ - After the non-linear search is complete create its `Result`, which includes: - - - The samples of the non-linear search (E.g. MCMC chains, nested sampling samples) which are used to compute - the maximum likelihood model, posteriors and other properties. - - - The model used to fit the data, which uses the samples to create specific instances of the model (e.g. - an instance of the maximum log likelihood model). - - - The non-linear search used to perform the model fit. - - The `ResultImaging` object contains a number of methods which use the above objects to create the max - log likelihood `Tracer`, `FitImaging`, adapt-galaxy images,etc. - - Parameters - ---------- - samples - A PyAutoFit object which contains the samples of the non-linear search, for example the chains of an MCMC - run of samples of the nested sampler. - - Returns - ------- - ResultImaging - The result of fitting the model to the imaging dataset, via a non-linear search. - """ - return ResultImaging(samples=samples, analysis=self, search_internal=search_internal) - + def save_attributes(self, paths: af.DirectoryPaths): """ Before the non-linear search begins, this routine saves attributes of the `Analysis` object to the `pickles` @@ -350,17 +320,12 @@ def save_attributes(self, paths: af.DirectoryPaths): """ super().save_attributes(paths=paths) - paths.save_fits( - name="psf", - hdu=self.dataset.psf.hdu_for_output, - prefix="dataset", - ) - paths.save_fits( - name="mask", - hdu=self.dataset.mask.hdu_for_output, - prefix="dataset", + analysis = ag.AnalysisImaging( + dataset=self.dataset, ) + analysis.save_attributes(paths=paths) + if self.positions_likelihood is not None: paths.save_json( diff --git a/autolens/imaging/model/result.py b/autolens/imaging/model/result.py index e1ae52030..7ef1f636f 100644 --- a/autolens/imaging/model/result.py +++ b/autolens/imaging/model/result.py @@ -5,41 +5,9 @@ from autolens.lens.tracer import Tracer from autolens.imaging.fit_imaging import FitImaging from autolens.analysis.result import ResultDataset -from autolens.analysis.preloads import Preloads class ResultImaging(ResultDataset): - """ - After the non-linear search of a fit to an imaging dataset is complete it creates this `ResultImaging`, object - which includes: - - - The samples of the non-linear search (E.g. MCMC chains, nested sampling samples) which are used to compute - the maximum likelihood model, posteriors and other properties. - - - The model used to fit the data, which uses the samples to create specific instances of the model (e.g. - an instance of the maximum log likelihood model). - - - The non-linear search used to perform the model fit. - - This class contains a number of methods which use the above objects to create the max log likelihood `Plane`, - `FitImaging`, adapt-galaxy images,etc. - - Parameters - ---------- - samples - A PyAutoFit object which contains the samples of the non-linear search, for example the chains of an MCMC - run of samples of the nested sampler. - model - The PyAutoFit model object, which includes model components representing the galaxies that are fitted to - the imaging data. - search - The non-linear search used to perform this model-fit. - - Returns - ------- - ResultImaging - The result of fitting the model to the imaging dataset, via a non-linear search. - """ @property def max_log_likelihood_fit(self) -> FitImaging: diff --git a/autolens/interferometer/model/analysis.py b/autolens/interferometer/model/analysis.py index 583382b8a..8093737a4 100644 --- a/autolens/interferometer/model/analysis.py +++ b/autolens/interferometer/model/analysis.py @@ -10,11 +10,10 @@ from autoarray.exc import PixelizationException -from autolens.analysis.analysis import AnalysisDataset +from autolens.analysis.analysis.dataset import AnalysisDataset from autolens.analysis.preloads import Preloads from autolens.analysis.positions import PositionsLHResample from autolens.analysis.positions import PositionsLHPenalty -from autolens.lens.tracer import Tracer from autolens.interferometer.model.result import ResultInterferometer from autolens.interferometer.model.visualizer import VisualizerInterferometer from autolens.interferometer.fit_interferometer import FitInterferometer @@ -27,13 +26,15 @@ class AnalysisInterferometer(AnalysisDataset): + Result = ResultInterferometer + def __init__( self, dataset, positions_likelihood: Optional[ Union[PositionsLHResample, PositionsLHPenalty] ] = None, - adapt_images: Optional[ag.AdaptImages] = None, + adapt_image_maker: Optional[ag.AdaptImageMaker] = None, cosmology: ag.cosmo.LensingCosmology = ag.cosmo.Planck15(), settings_inversion: aa.SettingsInversion = None, raise_inversion_positions_likelihood_exception: bool = True, @@ -78,7 +79,7 @@ def __init__( super().__init__( dataset=dataset, positions_likelihood=positions_likelihood, - adapt_images=adapt_images, + adapt_image_maker=adapt_image_maker, cosmology=cosmology, settings_inversion=settings_inversion, raise_inversion_positions_likelihood_exception=raise_inversion_positions_likelihood_exception, @@ -328,36 +329,6 @@ def visualize(self, paths: af.DirectoryPaths, instance, during_analysis): except IndexError: pass - def make_result(self, samples: af.SamplesPDF, search_internal=None): - """ - After the non-linear search is complete create its `Result`, which includes: - - - The samples of the non-linear search (E.g. MCMC chains, nested sampling samples) which are used to compute - the maximum likelihood model, posteriors and other properties. - - - The model used to fit the data, which uses the samples to create specific instances of the model (e.g. - an instance of the maximum log likelihood model). - - - The non-linear search used to perform the model fit. - - The `ResultInterferometer` object contains a number of methods which use the above objects to create the max - log likelihood `Plane`, `FitInterferometer`, adapt-galaxy images,etc. - - Parameters - ---------- - samples - A PyAutoFit object which contains the samples of the non-linear search, for example the chains of an MCMC - run of samples of the nested sampler. - - Returns - ------- - ResultImaging - The result of fitting the model to the imaging dataset, via a non-linear search. - """ - return ResultInterferometer( - samples=samples, analysis=self, search_internal=search_internal - ) - def save_attributes(self, paths: af.DirectoryPaths): """ Before the non-linear search begins, this routine saves attributes of the `Analysis` object to the `pickles` @@ -395,15 +366,12 @@ def save_attributes(self, paths: af.DirectoryPaths): """ super().save_attributes(paths=paths) - hdu = aa.util.array_2d.hdu_for_output_from( - array_2d=self.dataset.uv_wavelengths, - ) - paths.save_fits(name="uv_wavelengths", hdu=hdu, prefix="dataset") - paths.save_fits( - name="real_space_mask", - hdu=self.dataset.real_space_mask.hdu_for_output, - prefix="dataset", + analysis = ag.AnalysisInterferometer( + dataset=self.dataset, ) + + analysis.save_attributes(paths=paths) + if self.positions_likelihood is not None: paths.save_json( name="positions", diff --git a/autolens/interferometer/model/result.py b/autolens/interferometer/model/result.py index 55426fea6..7da68c89e 100644 --- a/autolens/interferometer/model/result.py +++ b/autolens/interferometer/model/result.py @@ -1,46 +1,12 @@ -import numpy as np - import autoarray as aa -import autogalaxy as ag -from autogalaxy.analysis.adapt_images import AdaptImages +from autogalaxy.analysis.adapt_images.adapt_images import AdaptImages from autolens.lens.tracer import Tracer from autolens.interferometer.fit_interferometer import FitInterferometer from autolens.analysis.result import ResultDataset class ResultInterferometer(ResultDataset): - """ - After the non-linear search of a fit to an interferometer dataset is complete it creates - this `ResultInterferometer` object, which includes: - - - The samples of the non-linear search (E.g. MCMC chains, nested sampling samples) which are used to compute - the maximum likelihood model, posteriors and other properties. - - - The model used to fit the data, which uses the samples to create specific instances of the model (e.g. - an instance of the maximum log likelihood model). - - - The non-linear search used to perform the model fit. - - This class contains a number of methods which use the above objects to create the max log likelihood `Tracer`, - `FitInterferometer`, adapt-galaxy images,etc. - - Parameters - ---------- - samples - A PyAutoFit object which contains the samples of the non-linear search, for example the chains of an MCMC - run of samples of the nested sampler. - model - The PyAutoFit model object, which includes model components representing the galaxies that are fitted to - the interferometer data. - search - The non-linear search used to perform this model-fit. - - Returns - ------- - ResultInterferometer - The result of fitting the model to the interferometer dataset, via a non-linear search. - """ @property def max_log_likelihood_fit(self) -> FitInterferometer: diff --git a/autolens/lens/to_inversion.py b/autolens/lens/to_inversion.py index e7068e538..0f4063836 100644 --- a/autolens/lens/to_inversion.py +++ b/autolens/lens/to_inversion.py @@ -70,7 +70,7 @@ def lp_linear_func_list_galaxy_dict( ) for plane_index, galaxies in enumerate(self.planes): - plane_to_inversion = ag.GalaxiesToInversion( + galaxies_to_inversion = ag.GalaxiesToInversion( galaxies=galaxies, sky=self.sky, dataset=self.dataset, @@ -82,7 +82,7 @@ def lp_linear_func_list_galaxy_dict( ) lp_linear_galaxy_dict_of_plane = ( - plane_to_inversion.lp_linear_func_list_galaxy_dict + galaxies_to_inversion.lp_linear_func_list_galaxy_dict ) lp_linear_galaxy_dict_list = { diff --git a/autolens/lens/tracer_util.py b/autolens/lens/tracer_util.py index db6d03759..15eb57076 100644 --- a/autolens/lens/tracer_util.py +++ b/autolens/lens/tracer_util.py @@ -238,8 +238,8 @@ def grid_2d_at_redshift_from( return traced_grid_list[plane_index_with_redshift[0]] for plane_index, plane_redshift in enumerate(plane_redshifts): - if redshift < plane_redshift: - plane_index_insert = plane_index + if redshift > plane_redshift: + plane_index_insert = plane_index + 1 planes.insert(plane_index_insert, [ag.Galaxy(redshift=redshift)]) diff --git a/autolens/point/model/analysis.py b/autolens/point/model/analysis.py index 6ded76b84..5f97b6424 100644 --- a/autolens/point/model/analysis.py +++ b/autolens/point/model/analysis.py @@ -3,9 +3,9 @@ import autofit as af import autogalaxy as ag -from autogalaxy.analysis.analysis import Analysis as AgAnalysis +from autogalaxy.analysis.analysis.analysis import Analysis as AgAnalysis -from autolens.analysis.analysis import AnalysisLensing +from autolens.analysis.analysis.lens import AnalysisLens from autolens.analysis.visualizer import Visualizer from autolens.point.point_dataset import PointDict from autolens.point.fit_point.point_dict import FitPointDict @@ -23,7 +23,9 @@ NumbaException = ValueError -class AnalysisPoint(AgAnalysis, AnalysisLensing): +class AnalysisPoint(AgAnalysis, AnalysisLens): + Result = ResultPoint + def __init__( self, point_dict: PointDict, @@ -54,7 +56,7 @@ def __init__( super().__init__(cosmology=cosmology) - AnalysisLensing.__init__(self=self, cosmology=cosmology) + AnalysisLens.__init__(self=self, cosmology=cosmology) self.point_dict = point_dict @@ -98,15 +100,6 @@ def visualize(self, paths, instance, during_analysis): visualizer = Visualizer(visualize_path=paths.image_path) - def make_result( - self, - samples: af.SamplesPDF, - search_internal=None, - ): - return ResultPoint( - samples=samples, analysis=self, search_internal=search_internal - ) - def save_attributes(self, paths: af.DirectoryPaths): self.point_dict.output_to_json( file_path=paths._files_path / "point_dict.json", overwrite=True diff --git a/autolens/point/point_solver.py b/autolens/point/point_solver.py index f8b14fda7..11f3ee020 100644 --- a/autolens/point/point_solver.py +++ b/autolens/point/point_solver.py @@ -232,8 +232,8 @@ def grid_within_distance_of_source_plane_centre( ) grid_within_distance_of_centre = grid_within_distance( - distances_1d=source_plane_distances, - grid_slim=grid, + distances_1d=np.asarray(source_plane_distances), + grid_slim=np.asarray(grid), within_distance=distance, ) diff --git a/autolens/quantity/model/analysis.py b/autolens/quantity/model/analysis.py index 42b4a76b6..dedd16d8b 100644 --- a/autolens/quantity/model/analysis.py +++ b/autolens/quantity/model/analysis.py @@ -1,4 +1,5 @@ import os +from typing import Optional import autofit as af import autogalaxy as ag @@ -6,13 +7,13 @@ from autogalaxy.quantity.model.visualizer import VisualizerQuantity from autolens.analysis.visualizer import Visualizer -from autolens.analysis.analysis import AnalysisLensing +from autolens.analysis.analysis.lens import AnalysisLens from autogalaxy.quantity.plot.fit_quantity_plotters import FitQuantityPlotter from autolens.quantity.model.result import ResultQuantity from autolens.quantity.fit_quantity import FitQuantity -class AnalysisQuantity(ag.AnalysisQuantity, AnalysisLensing): +class AnalysisQuantity(ag.AnalysisQuantity, AnalysisLens): def __init__( self, dataset: ag.DatasetQuantity, @@ -51,7 +52,7 @@ def __init__( """ super().__init__(dataset=dataset, func_str=func_str, cosmology=cosmology) - AnalysisLensing.__init__(self=self, cosmology=cosmology) + AnalysisLens.__init__(self=self, cosmology=cosmology) def fit_quantity_for_instance(self, instance: af.ModelInstance) -> FitQuantity: """ @@ -126,8 +127,10 @@ def visualize( def make_result( self, - samples: af.SamplesPDF, - search_internal=None, + samples_summary: af.SamplesSummary, + paths: af.AbstractPaths, + samples: Optional[af.SamplesPDF] = None, + search_internal: Optional[object] = None, ) -> ResultQuantity: """ After the non-linear search is complete create its `ResultQuantity`, which includes: @@ -146,8 +149,7 @@ def make_result( Parameters ---------- samples - A PyAutoFit object which contains the samples of the non-linear search, for example the chains of an MCMC - run of samples of the nested sampler. + The samples of the non-linear search, for example the MCMC chains. search The non-linear search used to perform this model-fit. @@ -157,5 +159,9 @@ def make_result( The result of fitting the model to the imaging dataset, via a non-linear search. """ return ResultQuantity( - samples=samples, analysis=self, search_internal=search_internal + samples_summary=samples_summary, + paths=paths, + samples=samples, + search_internal=search_internal, + analysis=self, ) diff --git a/docs/api/data.rst b/docs/api/data.rst index 543aa3a6a..4e95a17d8 100644 --- a/docs/api/data.rst +++ b/docs/api/data.rst @@ -34,7 +34,6 @@ For datasets taken with a CCD (or similar imaging device), including objects whi :recursive: Imaging - SettingsImaging SimulatorImaging Kernel2D Convolver @@ -50,7 +49,6 @@ a fast Fourier transform to map data to the uv-plane. :toctree: _autosummary Interferometer - SettingsInterferometer SimulatorInterferometer Visibilities TransformerDFT diff --git a/docs/api/pixelization.rst b/docs/api/pixelization.rst index 95e65dfe8..022501f07 100644 --- a/docs/api/pixelization.rst +++ b/docs/api/pixelization.rst @@ -34,7 +34,6 @@ Image Mesh [ag.image_mesh] Overlay Hilbert - HilbertBalanced KMeans Mesh [ag.mesh] diff --git a/docs/api/plot.rst b/docs/api/plot.rst index c4c51417c..8ea9af044 100644 --- a/docs/api/plot.rst +++ b/docs/api/plot.rst @@ -56,11 +56,9 @@ by **PyAutoGalaxy**. :template: custom-class-template.rst :recursive: - DynestyPlotter - UltraNestPlotter - EmceePlotter - ZeusPlotter - PySwarmsPlotter + NestPlotter + MCMCPlotter + OptimizePlotter Plot Customization [aplt] ------------------------- diff --git a/docs/overview/overview_3_modeling.rst b/docs/overview/overview_3_modeling.rst index 766013021..42f627ed3 100644 --- a/docs/overview/overview_3_modeling.rst +++ b/docs/overview/overview_3_modeling.rst @@ -523,8 +523,8 @@ mass its name ``mass`` defined when making the ``Model`` above is used). .. code-block:: python - search_plotter = aplt.DynestyPlotter(samples=result.samples) - search_plotter.corner() + plotter = aplt.NestPlotter(samples=result.samples) + plotter.corner_cornerpy() Here is an example of how a PDF estimated for a lens model appears: diff --git a/test_autolens/aggregator/conftest.py b/test_autolens/aggregator/conftest.py index 8b0c5e794..e4acef15c 100644 --- a/test_autolens/aggregator/conftest.py +++ b/test_autolens/aggregator/conftest.py @@ -66,11 +66,6 @@ def make_model(): @pytest.fixture(name="samples") def make_samples(model): - galaxy_0 = al.Galaxy(redshift=0.5, light=al.lp.Sersic(centre=(0.0, 1.0))) - galaxy_1 = al.Galaxy(redshift=1.0, light=al.lp.Sersic()) - - tracer = al.Tracer(galaxies=[galaxy_0, galaxy_1]) - parameters = [model.prior_count * [1.0], model.prior_count * [10.0]] sample_list = Sample.from_lists( @@ -84,6 +79,5 @@ def make_samples(model): return al.m.MockSamples( model=model, sample_list=sample_list, - max_log_likelihood_instance=tracer, prior_means=[1.0] * model.prior_count, ) diff --git a/test_autolens/analysis/analysis/__init__.py b/test_autolens/analysis/analysis/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_autolens/analysis/analysis/test_analysis_dataset.py b/test_autolens/analysis/analysis/test_analysis_dataset.py new file mode 100644 index 000000000..71198ca21 --- /dev/null +++ b/test_autolens/analysis/analysis/test_analysis_dataset.py @@ -0,0 +1,142 @@ +import numpy as np +from os import path +import os +import pytest + +from autoconf import conf +from autoconf.dictable import from_json + +import autofit as af +import autolens as al +from autolens import exc + +directory = path.dirname(path.realpath(__file__)) + + +def test__relocate_pix_border__determines_if_border_pixel_relocation_is_used( + masked_imaging_7x7, +): + masked_imaging_7x7.sub_size_pixelization = 2 + + pixelization = al.Pixelization( + mesh=al.mesh.Rectangular(shape=(3, 3)), + regularization=al.reg.Constant(coefficient=1.0), + ) + + model = af.Collection( + galaxies=af.Collection( + lens=al.Galaxy( + redshift=0.5, mass=al.mp.IsothermalSph(einstein_radius=100.0) + ), + source=al.Galaxy(redshift=1.0, pixelization=pixelization), + ) + ) + + analysis = al.AnalysisImaging( + dataset=masked_imaging_7x7, + settings_inversion=al.SettingsInversion(relocate_pix_border=True), + ) + + analysis.dataset.grid_pixelization[4] = np.array([[500.0, 0.0]]) + + instance = model.instance_from_unit_vector([]) + fit = analysis.fit_from(instance=instance) + + assert fit.inversion.linear_obj_list[0].source_plane_data_grid[4][ + 0 + ] == pytest.approx(97.19584, 1.0e-2) + assert fit.inversion.linear_obj_list[0].source_plane_data_grid[4][ + 1 + ] == pytest.approx(-3.699999, 1.0e-2) + + analysis = al.AnalysisImaging( + dataset=masked_imaging_7x7, + settings_inversion=al.SettingsInversion(relocate_pix_border=False), + ) + + analysis.dataset.grid_pixelization[4] = np.array([300.0, 0.0]) + + instance = model.instance_from_unit_vector([]) + fit = analysis.fit_from( + instance=instance, + ) + + assert fit.inversion.linear_obj_list[0].source_plane_data_grid[4][ + 0 + ] == pytest.approx(200.0, 1.0e-4) + + +def test__modify_before_fit__inversion_no_positions_likelihood__raises_exception( + masked_imaging_7x7, +): + lens = al.Galaxy(redshift=0.5, mass=al.mp.IsothermalSph()) + + pixelization = al.Pixelization( + mesh=al.mesh.Rectangular(), regularization=al.reg.Constant() + ) + + source = al.Galaxy(redshift=1.0, pixelization=pixelization) + + model = af.Collection(galaxies=af.Collection(lens=lens, source=source)) + + analysis = al.AnalysisImaging(dataset=masked_imaging_7x7) + + with pytest.raises(exc.AnalysisException): + analysis.modify_before_fit(paths=af.DirectoryPaths(), model=model) + + positions_likelihood = al.PositionsLHPenalty( + positions=al.Grid2DIrregular([(1.0, 100.0), (200.0, 2.0)]), threshold=0.01 + ) + + analysis = al.AnalysisImaging( + dataset=masked_imaging_7x7, positions_likelihood=positions_likelihood + ) + analysis.modify_before_fit(paths=af.DirectoryPaths(), model=model) + + +def test__check_preloads(masked_imaging_7x7): + conf.instance["general"]["test"]["check_preloads"] = True + + lens_galaxy = al.Galaxy(redshift=0.5, light=al.lp.Sersic(intensity=0.1)) + + model = af.Collection(galaxies=af.Collection(lens=lens_galaxy)) + + analysis = al.AnalysisImaging(dataset=masked_imaging_7x7) + + instance = model.instance_from_unit_vector([]) + tracer = analysis.tracer_via_instance_from(instance=instance) + fit = al.FitImaging(dataset=masked_imaging_7x7, tracer=tracer) + + analysis.preloads.check_via_fit(fit=fit) + + analysis.preloads.blurred_image = fit.blurred_image + + analysis.preloads.check_via_fit(fit=fit) + + analysis.preloads.blurred_image = fit.blurred_image + 1.0 + + with pytest.raises(exc.PreloadsException): + analysis.preloads.check_via_fit(fit=fit) + + +def test__save_results__tracer_output_to_json(analysis_imaging_7x7): + lens = al.Galaxy(redshift=0.5) + source = al.Galaxy(redshift=1.0) + + model = af.Collection(galaxies=af.Collection(lens=lens, source=source)) + + tracer = al.Tracer(galaxies=[lens, source]) + + paths = af.DirectoryPaths() + + analysis_imaging_7x7.save_results( + paths=paths, + result=al.m.MockResult(max_log_likelihood_tracer=tracer, model=model), + ) + + tracer = from_json(file_path=paths._files_path / "tracer.json") + + assert tracer.galaxies[0].redshift == 0.5 + assert tracer.galaxies[1].redshift == 1.0 + + os.remove(paths._files_path / "tracer.json") diff --git a/test_autolens/analysis/analysis/test_analysis_lens.py b/test_autolens/analysis/analysis/test_analysis_lens.py new file mode 100644 index 000000000..0a3d87d72 --- /dev/null +++ b/test_autolens/analysis/analysis/test_analysis_lens.py @@ -0,0 +1,72 @@ +from os import path +import pytest + +import autofit as af +import autolens as al + +directory = path.dirname(path.realpath(__file__)) + + +def test__tracer_for_instance(analysis_imaging_7x7): + model = af.Collection( + galaxies=af.Collection( + lens=al.Galaxy( + redshift=0.5, + light=al.lp.SersicSph(intensity=2.0), + mass=al.mp.IsothermalSph(centre=(0.0, 0.0), einstein_radius=1.0), + ), + source=al.Galaxy(redshift=1.0), + ) + + af.Collection( + clump=al.Galaxy( + redshift=0.5, + light=al.lp.SersicSph(intensity=0.1), + mass=al.mp.IsothermalSph(einstein_radius=0.2), + ) + ), + ) + + instance = model.instance_from_unit_vector([]) + tracer = analysis_imaging_7x7.tracer_via_instance_from(instance=instance) + + assert tracer.galaxies[0].redshift == 0.5 + assert tracer.galaxies[0].light.intensity == 2.0 + assert tracer.galaxies[0].mass.centre == pytest.approx((0.0, 0.0), 1.0e-4) + assert tracer.galaxies[0].mass.einstein_radius == 1.0 + assert tracer.galaxies[2].redshift == 0.5 + assert tracer.galaxies[2].light.intensity == 0.1 + assert tracer.galaxies[2].mass.einstein_radius == 0.2 + + +def test__tracer_for_instance__subhalo_redshift_rescale_used(analysis_imaging_7x7): + model = af.Collection( + galaxies=af.Collection( + lens=al.Galaxy( + redshift=0.5, + mass=al.mp.IsothermalSph(centre=(0.0, 0.0), einstein_radius=1.0), + ), + subhalo=al.Galaxy(redshift=0.25, mass=al.mp.NFWSph(centre=(0.1, 0.2))), + source=al.Galaxy(redshift=1.0), + ) + ) + + instance = model.instance_from_unit_vector([]) + tracer = analysis_imaging_7x7.tracer_via_instance_from(instance=instance) + + assert tracer.galaxies[1].mass.centre == pytest.approx((0.1, 0.2), 1.0e-4) + + model = af.Collection( + galaxies=af.Collection( + lens=al.Galaxy( + redshift=0.5, + mass=al.mp.IsothermalSph(centre=(0.0, 0.0), einstein_radius=1.0), + ), + subhalo=al.Galaxy(redshift=0.75, mass=al.mp.NFWSph(centre=(0.1, 0.2))), + source=al.Galaxy(redshift=1.0), + ) + ) + + instance = model.instance_from_unit_vector([]) + tracer = analysis_imaging_7x7.tracer_via_instance_from(instance=instance) + + assert tracer.galaxies[1].mass.centre == pytest.approx((-0.19959, -0.39919), 1.0e-4) diff --git a/test_autolens/analysis/test_analysis.py b/test_autolens/analysis/test_analysis.py index 158f06e1e..b28ba52e3 100644 --- a/test_autolens/analysis/test_analysis.py +++ b/test_autolens/analysis/test_analysis.py @@ -81,6 +81,8 @@ def test__tracer_for_instance__subhalo_redshift_rescale_used(analysis_imaging_7x def test__relocate_pix_border__determines_if_border_pixel_relocation_is_used( masked_imaging_7x7, ): + masked_imaging_7x7.sub_size_pixelization = 2 + pixelization = al.Pixelization( mesh=al.mesh.Rectangular(shape=(3, 3)), regularization=al.reg.Constant(coefficient=1.0), @@ -95,10 +97,6 @@ def test__relocate_pix_border__determines_if_border_pixel_relocation_is_used( ) ) - masked_imaging_7x7 = masked_imaging_7x7.apply_settings( - settings=al.SettingsImaging(sub_size_pixelization=2) - ) - analysis = al.AnalysisImaging( dataset=masked_imaging_7x7, settings_inversion=al.SettingsInversion(relocate_pix_border=True), diff --git a/test_autolens/analysis/test_result.py b/test_autolens/analysis/test_result.py index 40e579374..5e5e35852 100644 --- a/test_autolens/analysis/test_result.py +++ b/test_autolens/analysis/test_result.py @@ -23,13 +23,7 @@ def test__max_log_likelihood_tracer( ) ) - samples = al.m.MockSamples( - model=model, - max_log_likelihood_instance=tracer_x2_plane_7x7, - prior_means=[1.0] * model.prior_count, - ) - - search = al.m.MockSearch(name="test_search_2", samples=samples) + search = al.m.MockSearch(name="test_search_2") result = search.fit(model=model, analysis=analysis_imaging_7x7) @@ -58,9 +52,9 @@ def test__max_log_likelihood_positions_threshold(masked_imaging_7x7): ] ) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis) + result = res.Result(samples_summary=samples_summary, analysis=analysis) assert result.max_log_likelihood_positions_threshold == pytest.approx( 0.8309561230, 1.0e-4 @@ -76,9 +70,9 @@ def test__source_plane_light_profile_centre(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis_imaging_7x7) + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) assert result.source_plane_light_profile_centre.in_list == [(1.0, 2.0)] @@ -94,9 +88,9 @@ def test__source_plane_light_profile_centre(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source_0, source_1]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis_imaging_7x7) + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) assert result.source_plane_light_profile_centre.in_list == [(1.0, 2.0)] @@ -110,17 +104,17 @@ def test__source_plane_light_profile_centre(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source_0, source_1]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis_imaging_7x7) + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) assert result.source_plane_light_profile_centre.in_list == [(5.0, 6.0)] tracer = al.Tracer(galaxies=[al.Galaxy(redshift=0.5)]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis_imaging_7x7) + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) assert result.source_plane_light_profile_centre == None @@ -137,9 +131,11 @@ def test__source_plane_inversion_centre(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = ResultImaging(samples=samples, analysis=analysis_imaging_7x7) + result = ResultImaging( + samples_summary=samples_summary, analysis=analysis_imaging_7x7 + ) assert ( result.source_plane_inversion_centre.in_list[0] @@ -155,9 +151,11 @@ def test__source_plane_inversion_centre(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = ResultImaging(samples=samples, analysis=analysis_imaging_7x7) + result = ResultImaging( + samples_summary=samples_summary, analysis=analysis_imaging_7x7 + ) assert result.source_plane_inversion_centre == None @@ -166,9 +164,11 @@ def test__source_plane_inversion_centre(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = ResultImaging(samples=samples, analysis=analysis_imaging_7x7) + result = ResultImaging( + samples_summary=samples_summary, analysis=analysis_imaging_7x7 + ) assert result.source_plane_inversion_centre == None @@ -189,9 +189,11 @@ def test__source_plane_centre(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = ResultImaging(samples=samples, analysis=analysis_imaging_7x7) + result = ResultImaging( + samples_summary=samples_summary, analysis=analysis_imaging_7x7 + ) assert result.source_plane_centre.in_list[0] == pytest.approx( (-0.916666, -0.916666), 1.0e-4 @@ -220,9 +222,11 @@ def test__image_plane_multiple_image_positions(analysis_imaging_7x7): tracer = al.Tracer(galaxies=[lens, source]) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = ResultImaging(samples=samples, analysis=analysis_imaging_7x7) + result = ResultImaging( + samples_summary=samples_summary, analysis=analysis_imaging_7x7 + ) multiple_images = result.image_plane_multiple_image_positions @@ -243,9 +247,9 @@ def test__image_plane_multiple_image_positions(analysis_imaging_7x7): ] ) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis_imaging_7x7) + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) assert result.image_plane_multiple_image_positions.in_list[0][0] == pytest.approx( 1.0004, 1.0e-2 @@ -268,9 +272,9 @@ def test__positions_threshold_from(analysis_imaging_7x7): ] ) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis_imaging_7x7) + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) assert result.positions_threshold_from() == pytest.approx(0.000973519, 1.0e-4) assert result.positions_threshold_from(factor=5.0) == pytest.approx( @@ -297,9 +301,9 @@ def test__positions_likelihood_from(analysis_imaging_7x7): ] ) - samples = al.m.MockSamples(max_log_likelihood_instance=tracer) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) - result = res.Result(samples=samples, analysis=analysis_imaging_7x7) + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) positions_likelihood = result.positions_likelihood_from( factor=0.1, minimum_threshold=0.2 @@ -317,10 +321,10 @@ def test__positions_likelihood_from(analysis_imaging_7x7): def test__results_include_mask__available_as_property( - analysis_imaging_7x7, masked_imaging_7x7, samples_with_result + analysis_imaging_7x7, masked_imaging_7x7, samples_summary_with_result ): result = res.ResultDataset( - samples=samples_with_result, + samples_summary=samples_summary_with_result, analysis=analysis_imaging_7x7, ) @@ -328,10 +332,10 @@ def test__results_include_mask__available_as_property( def test__results_include_positions__available_as_property( - analysis_imaging_7x7, masked_imaging_7x7, samples_with_result + analysis_imaging_7x7, masked_imaging_7x7, samples_summary_with_result ): result = res.ResultDataset( - samples=samples_with_result, + samples_summary=samples_summary_with_result, analysis=analysis_imaging_7x7, ) @@ -346,7 +350,7 @@ def test__results_include_positions__available_as_property( ) result = res.ResultDataset( - samples=samples_with_result, + samples_summary=samples_summary_with_result, analysis=analysis, ) @@ -362,7 +366,7 @@ def test___image_dict(analysis_imaging_7x7): instance.galaxies = galaxies result = ResultImaging( - samples=al.m.MockSamples(max_log_likelihood_instance=instance), + samples_summary=al.m.MockSamplesSummary(max_log_likelihood_instance=instance), analysis=analysis_imaging_7x7, ) diff --git a/test_autolens/config/general.yaml b/test_autolens/config/general.yaml index 824254fa0..e06e1b19d 100644 --- a/test_autolens/config/general.yaml +++ b/test_autolens/config/general.yaml @@ -36,8 +36,7 @@ profiling: perform: true repeats: 1 test: - check_figure_of_merit_sanity: false - bypass_figure_of_merit_sanity: true + check_likelihood_function: false # if True, when a search is resumed the likelihood of a previous sample is recalculated to ensure it is consistent with the previous run. check_preloads: false exception_override: false 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. diff --git a/test_autolens/config/output.yaml b/test_autolens/config/output.yaml new file mode 100644 index 000000000..5cb8812dc --- /dev/null +++ b/test_autolens/config/output.yaml @@ -0,0 +1,63 @@ +# Determines whether files saved by the search are output to the hard-disk. This is true both when saving to the +# directory structure and when saving to database. + +# Files can be listed name: bool where the name is the name of the file without a suffix (e.g. model not model.json) +# and bool is true or false. + +# If a given file is not listed then the default value is used. + +default: true # If true then files which are not explicitly listed here are output anyway. If false then they are not. + +### Samples ### + +# The `samples.csv`file contains every sampled value of every free parameter with its log likelihood and weight. + +# This file is often large, therefore disabling it can significantly reduce hard-disk space use. + +# `samples.csv` is used to perform marginalization, infer model parameter errors and do other analysis of the search +# chains. Even if output of `samples.csv` is disabled, these tasks are still performed by the fit and output to +# the `samples_summary.json` file. However, without a `samples.csv` file these types of tasks cannot be performed +# after the fit is complete, for example via the database. + +samples: true + +# The `samples.csv` file contains every accepted sampled value of every free parameter with its log likelihood and +# weight. For certain searches, the majority of samples have a very low weight, which has no numerical impact on the +# results of the model-fit. However, these samples are still output to the `samples.csv` file, taking up hard-disk space +# and slowing down analysis of the samples (e.g. via the database). + +# The `samples_weight_threshold` below specifies the threshold value of the weight such that samples with a weight +# below this value are not output to the `samples.csv` file. This can be used to reduce the size of the `samples.csv` +# file and speed up analysis of the samples. + +# Note that for many searches (e.g. MCMC) all samples have equal weight, and thus this threshold has no impact and +# there is no simple way to save hard-disk space. However, for nested sampling, the majority of samples have a very +# low weight and this threshold can be used to save hard-disk space. + +# Set value to empty (e.g. delete 1.0e-10 below) to disable this feature. + +samples_weight_threshold: 1.0e-10 + +### Search Internal ### + +# The search internal folder which contains a saved state of the non-linear search, as a .pickle or .dill file. + +# If the entry below is false, the folder is still output during the model-fit, as it is required to resume the fit +# from where it left off. Therefore, settings `false` below does not impact model-fitting checkpointing and resumption. +# Instead, the search internal folder is deleted once the fit is completed. + +# The search internal folder file is often large, therefore deleting it after a fit is complete can significantly +# reduce hard-disk space use. + +# The search internal representation (e.g. what you can load from the output .pickle file) may have additional +# quantities specific to the non-linear search that you are interested in inspecting. Deleting the folder means this +# information is list. + +search_internal: false + +# Other Files: + +covariance: false # `covariance.csv`: The [free parameters x free parameters] covariance matrix. +data: true # `data.json`: The value of every data point in the data. +noise_map: true # `noise_map.json`: The value of every RMS noise map value. + diff --git a/test_autolens/conftest.py b/test_autolens/conftest.py index b4442adec..b5ceb2edf 100644 --- a/test_autolens/conftest.py +++ b/test_autolens/conftest.py @@ -391,6 +391,6 @@ def make_include_all(): return fixtures.make_include_2d_all() -@pytest.fixture(name="samples_with_result") -def make_samples_with_result(): - return fixtures.make_samples_with_result() +@pytest.fixture(name="samples_summary_with_result") +def make_samples_summary_with_result(): + return fixtures.make_samples_summary_with_result() diff --git a/test_autolens/imaging/model/test_analysis_imaging.py b/test_autolens/imaging/model/test_analysis_imaging.py index 7b9459bde..54c4b99ef 100644 --- a/test_autolens/imaging/model/test_analysis_imaging.py +++ b/test_autolens/imaging/model/test_analysis_imaging.py @@ -1,4 +1,3 @@ -import numpy as np from os import path import pytest @@ -15,26 +14,12 @@ def test__make_result__result_imaging_is_returned(masked_imaging_7x7): - model = af.Collection(galaxies=af.Collection(galaxy_0=al.Galaxy(redshift=0.5))) - - instance = model.instance_from_prior_medians() - - samples = al.m.MockSamples( - model=model, - max_log_likelihood_instance=instance, - prior_means=[1.0] * model.prior_count - ) - search = al.m.MockSearch(name="test_search", samples=samples) + model = af.Collection(galaxies=af.Collection(galaxy_0=al.Galaxy(redshift=0.5))) analysis = al.AnalysisImaging(dataset=masked_imaging_7x7) - def modify_after_fit( - paths: af.DirectoryPaths, model: af.AbstractPriorModel, result: af.Result - ): - pass - - analysis.modify_after_fit = modify_after_fit + search = al.m.MockSearch(name="test_search") result = search.fit(model=model, analysis=analysis) diff --git a/test_autolens/imaging/model/test_result_imaging.py b/test_autolens/imaging/model/test_result_imaging.py index 89703e819..9263e2e8b 100644 --- a/test_autolens/imaging/model/test_result_imaging.py +++ b/test_autolens/imaging/model/test_result_imaging.py @@ -1,4 +1,3 @@ -import numpy as np import pytest import autofit as af @@ -15,9 +14,9 @@ def test___linear_light_profiles_in_result(analysis_imaging_7x7): instance = af.ModelInstance() instance.galaxies = galaxies - samples = al.m.MockSamples(max_log_likelihood_instance=instance) + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=instance) - result = ResultImaging(samples=samples, analysis=analysis_imaging_7x7) + result = ResultImaging(samples_summary=samples_summary, analysis=analysis_imaging_7x7) assert not isinstance( result.max_log_likelihood_tracer.galaxies[0].bulge, diff --git a/test_autolens/imaging/test_simulate_and_fit_imaging.py b/test_autolens/imaging/test_simulate_and_fit_imaging.py index 894c2f6fc..e07817407 100644 --- a/test_autolens/imaging/test_simulate_and_fit_imaging.py +++ b/test_autolens/imaging/test_simulate_and_fit_imaging.py @@ -57,6 +57,7 @@ def test__perfect_fit__chi_squared_0(): noise_map_path=path.join(file_path, "noise_map.fits"), psf_path=path.join(file_path, "psf.fits"), pixel_scales=0.2, + sub_size=1 ) mask = al.Mask2D.circular( @@ -64,9 +65,6 @@ def test__perfect_fit__chi_squared_0(): ) masked_dataset = dataset.apply_mask(mask=mask) - masked_dataset = masked_dataset.apply_settings( - settings=al.SettingsImaging(sub_size=1) - ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) @@ -145,6 +143,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_agree_with_standa dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) + dataset.sub_size = 1 dataset.noise_map = al.Array2D.ones( shape_native=dataset.data.shape_native, pixel_scales=0.2 ) @@ -154,9 +153,6 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_agree_with_standa ) masked_dataset = dataset.apply_mask(mask=mask) - masked_dataset = masked_dataset.apply_settings( - settings=al.SettingsImaging(sub_size=1) - ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) @@ -254,6 +250,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization( dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) + dataset.sub_size = 1 dataset.noise_map = al.Array2D.ones( shape_native=dataset.data.shape_native, pixel_scales=0.2 ) @@ -263,9 +260,6 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization( ) masked_dataset = dataset.apply_mask(mask=mask) - masked_dataset = masked_dataset.apply_settings( - settings=al.SettingsImaging(sub_size=1) - ) lens_galaxy_linear = al.Galaxy( redshift=0.5, @@ -393,6 +387,7 @@ def test__simulate_imaging_data_and_fit__complex_fit_compare_mapping_matrix_w_ti dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) + dataset.sub_size = 2 dataset.noise_map = al.Array2D.ones( shape_native=dataset.data.shape_native, pixel_scales=0.2 ) @@ -401,9 +396,6 @@ def test__simulate_imaging_data_and_fit__complex_fit_compare_mapping_matrix_w_ti ) masked_dataset = dataset.apply_mask(mask=mask) - masked_dataset = masked_dataset.apply_settings( - settings=al.SettingsImaging(sub_size=2) - ) lens_0 = al.Galaxy( redshift=0.1, diff --git a/test_autolens/interferometer/model/test_analysis_interferometer.py b/test_autolens/interferometer/model/test_analysis_interferometer.py index 73deb53c5..3f62c28b1 100644 --- a/test_autolens/interferometer/model/test_analysis_interferometer.py +++ b/test_autolens/interferometer/model/test_analysis_interferometer.py @@ -1,5 +1,4 @@ from os import path -import numpy as np import pytest import autofit as af @@ -14,20 +13,9 @@ def test__make_result__result_interferometer_is_returned(interferometer_7): model = af.Collection(galaxies=af.Collection(galaxy_0=al.Galaxy(redshift=0.5))) - instance = model.instance_from_prior_medians() - - samples = al.m.MockSamples(max_log_likelihood_instance=instance) - - search = al.m.MockSearch(name="test_search", samples=samples) - analysis = al.AnalysisInterferometer(dataset=interferometer_7) - def modify_after_fit( - paths: af.DirectoryPaths, model: af.AbstractPriorModel, result: af.Result - ): - pass - - analysis.modify_after_fit = modify_after_fit + search = al.m.MockSearch(name="test_search") result = search.fit(model=model, analysis=analysis) diff --git a/test_autolens/interferometer/test_simulate_and_fit_interferometer.py b/test_autolens/interferometer/test_simulate_and_fit_interferometer.py index 13df2ee0a..7e7bbfa96 100644 --- a/test_autolens/interferometer/test_simulate_and_fit_interferometer.py +++ b/test_autolens/interferometer/test_simulate_and_fit_interferometer.py @@ -61,9 +61,7 @@ def test__perfect_fit__chi_squared_0(): noise_map_path=path.join(file_path, "noise_map.fits"), uv_wavelengths_path=path.join(file_path, "uv_wavelengths.fits"), real_space_mask=real_space_mask, - ) - dataset = dataset.apply_settings( - settings=al.SettingsInterferometer(transformer_class=al.TransformerDFT) + transformer_class=al.TransformerDFT, ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) @@ -136,10 +134,6 @@ def test__simulate_interferometer_data_and_fit__known_likelihood(): dataset = simulator.via_tracer_from(tracer=tracer, grid=grid) - dataset = dataset.apply_settings( - settings=al.SettingsInterferometer(transformer_class=al.TransformerDFT) - ) - fit = al.FitInterferometer( dataset=dataset, tracer=tracer, @@ -181,12 +175,6 @@ def test__simulate_interferometer_data_and_fit__linear_light_profiles_agree_with dataset = simulator.via_tracer_from(tracer=tracer, grid=grid) - dataset = dataset.apply_settings( - settings=al.SettingsInterferometer( - grid_class=al.Grid2D, transformer_class=al.TransformerDFT, sub_size=1 - ) - ) - fit = al.FitInterferometer( dataset=dataset, tracer=tracer, @@ -290,12 +278,6 @@ def test__simulate_interferometer_data_and_fit__linear_light_profiles_and_pixeli dataset = simulator.via_tracer_from(tracer=tracer, grid=grid) - dataset = dataset.apply_settings( - settings=al.SettingsInterferometer( - grid_class=al.Grid2D, transformer_class=al.TransformerDFT, sub_size=1 - ) - ) - lens_galaxy_linear = al.Galaxy( redshift=0.5, light=al.lp_linear.Sersic(centre=(0.1, 0.1)), diff --git a/test_autolens/lens/test_tracer.py b/test_autolens/lens/test_tracer.py index 28b92e4bd..4db53685f 100644 --- a/test_autolens/lens/test_tracer.py +++ b/test_autolens/lens/test_tracer.py @@ -1,7 +1,6 @@ import numpy as np import pytest from os import path -from skimage import measure from autoconf.dictable import from_json, output_to_json import autofit as af @@ -154,13 +153,38 @@ def test__grid_2d_at_redshift_from(sub_grid_2d_7x7): tracer = al.Tracer(galaxies=galaxies, cosmology=al.cosmo.Planck15()) + galaxies_plus_extra = [g0, g1, g2, g3, g4, g5, al.Galaxy(redshift=0.5)] + tracer_plus_extra = al.Tracer( + galaxies=galaxies_plus_extra, cosmology=al.cosmo.Planck15() + ) + grid_2d_list_from = tracer_plus_extra.traced_grid_2d_list_from(grid=grid_simple) + grid_at_redshift = tracer.grid_2d_at_redshift_from(grid=grid_simple, redshift=0.5) - assert grid_at_redshift[0] == pytest.approx((9.73109691, 19.46219382), 1.0e-4) + assert grid_2d_list_from[1] == pytest.approx(grid_at_redshift, 1.0e-4) + assert grid_at_redshift[0] == pytest.approx((0.6273814, 1.2547628), 1.0e-4) + + galaxies_plus_extra = [g0, g1, g2, g3, g4, g5, al.Galaxy(redshift=1.75)] + tracer_plus_extra = al.Tracer( + galaxies=galaxies_plus_extra, cosmology=al.cosmo.Planck15() + ) + grid_2d_list_from = tracer_plus_extra.traced_grid_2d_list_from(grid=grid_simple) grid_at_redshift = tracer.grid_2d_at_redshift_from(grid=grid_simple, redshift=1.75) - assert grid_at_redshift[0] == pytest.approx((0.65903649, 1.31807298), 1.0e-4) + assert grid_2d_list_from[2] == pytest.approx(grid_at_redshift, 1.0e-4) + assert grid_at_redshift[0] == pytest.approx((0.27331481161, 0.5466296232), 1.0e-4) + + galaxies_plus_extra = [g0, g1, g2, g3, g4, g5, al.Galaxy(redshift=2.0)] + tracer_plus_extra = al.Tracer( + galaxies=galaxies_plus_extra, cosmology=al.cosmo.Planck15() + ) + grid_2d_list_from = tracer_plus_extra.traced_grid_2d_list_from(grid=grid_simple) + + grid_at_redshift = tracer.grid_2d_at_redshift_from(grid=grid_simple, redshift=2.0) + + assert grid_2d_list_from[2] == pytest.approx(grid_at_redshift, 1.0e-4) + assert grid_at_redshift[0] == pytest.approx((0.222772465, 0.445544931), 1.0e-4) def test__image_2d_list_from(): @@ -364,7 +388,7 @@ def test__image_2d_via_input_plane_image_from__with_foreground_planes__multi_pla ) -def test__padded_image_2d_from(sub_grid_2d_7x7, grid_2d_iterate_7x7): +def test__padded_image_2d_from(sub_grid_2d_7x7): padded_grid = sub_grid_2d_7x7.padded_grid_from(kernel_shape_native=(3, 3)) g0 = al.Galaxy(redshift=0.1, light_profile=al.lp.Sersic(intensity=0.1)) @@ -804,11 +828,10 @@ def test__regression__centre_of_profile_in_right_place(): assert deflections.native[1, 4, 1] > 0 assert deflections.native[1, 3, 1] < 0 - grid = al.Grid2DIterate.uniform( + grid = al.Grid2D.uniform( shape_native=(7, 7), pixel_scales=1.0, - fractional_accuracy=0.99, - sub_steps=[2, 4], + over_sample=al.OverSampleIterate(fractional_accuracy=0.99, sub_steps=[2, 4]), ) convergence = tracer.convergence_2d_from(grid=grid) @@ -841,7 +864,10 @@ def test__decorators__grid_iterate_in__iterates_array_result_correctly(gal_x1_lp origin=(0.001, 0.001), ) - grid = al.Grid2DIterate.from_mask(mask=mask, fractional_accuracy=1.0, sub_steps=[2]) + grid = al.Grid2D.from_mask( + mask=mask, + over_sample=al.OverSampleIterate(fractional_accuracy=1.0, sub_steps=[2]), + ) tracer = al.Tracer(galaxies=[gal_x1_lp]) @@ -853,8 +879,9 @@ def test__decorators__grid_iterate_in__iterates_array_result_correctly(gal_x1_lp assert (image == image_sub_2).all() - grid = al.Grid2DIterate.from_mask( - mask=mask, fractional_accuracy=0.95, sub_steps=[2, 4, 8] + grid = al.Grid2D.from_mask( + mask=mask, + over_sample=al.OverSampleIterate(fractional_accuracy=0.95, sub_steps=[2, 4, 8]), ) galaxy = al.Galaxy( @@ -887,7 +914,10 @@ def test__decorators__grid_iterate_in__method_returns_array_list__uses_highest_s origin=(0.001, 0.001), ) - grid = al.Grid2DIterate.from_mask(mask=mask, fractional_accuracy=1.0, sub_steps=[2]) + grid = al.Grid2D.from_mask( + mask=mask, + over_sample=al.OverSampleIterate(fractional_accuracy=1.0, sub_steps=[2]), + ) tracer = al.Tracer(galaxies=[gal_x1_lp]) @@ -899,8 +929,9 @@ def test__decorators__grid_iterate_in__method_returns_array_list__uses_highest_s assert (images[0] == image_sub_2).all() - grid = al.Grid2DIterate.from_mask( - mask=mask, fractional_accuracy=0.95, sub_steps=[2, 4, 8] + grid = al.Grid2D.from_mask( + mask=mask, + over_sample=al.OverSampleIterate(fractional_accuracy=0.95, sub_steps=[2, 4, 8]), ) galaxy = al.Galaxy( @@ -930,7 +961,10 @@ def test__decorators__grid_iterate_in__iterates_grid_result_correctly(gal_x1_mp) pixel_scales=(1.0, 1.0), ) - grid = al.Grid2DIterate.from_mask(mask=mask, fractional_accuracy=1.0, sub_steps=[2]) + grid = al.Grid2D.from_mask( + mask=mask, + over_sample=al.OverSampleIterate(fractional_accuracy=1.0, sub_steps=[2]), + ) galaxy = al.Galaxy( redshift=0.5, mass=al.mp.Isothermal(centre=(0.08, 0.08), einstein_radius=1.0) @@ -946,8 +980,9 @@ def test__decorators__grid_iterate_in__iterates_grid_result_correctly(gal_x1_mp) assert (deflections == deflections_sub_2).all() - grid = al.Grid2DIterate.from_mask( - mask=mask, fractional_accuracy=0.99, sub_steps=[2, 4, 8] + grid = al.Grid2D.from_mask( + mask=mask, + over_sample=al.OverSampleIterate(fractional_accuracy=0.99, sub_steps=[2, 4, 8]), ) galaxy = al.Galaxy( diff --git a/test_autolens/quantity/model/test_analysis_quantity.py b/test_autolens/quantity/model/test_analysis_quantity.py index 2b5c2883a..8e74b5b3d 100644 --- a/test_autolens/quantity/model/test_analysis_quantity.py +++ b/test_autolens/quantity/model/test_analysis_quantity.py @@ -8,50 +8,48 @@ directory = path.dirname(path.realpath(__file__)) -class TestAnalysisQuantity: - def test__make_result__result_quantity_is_returned( - self, dataset_quantity_7x7_array_2d - ): - model = af.Collection(galaxies=af.Collection(galaxy_0=al.Galaxy(redshift=0.5))) +def test__make_result__result_quantity_is_returned(dataset_quantity_7x7_array_2d): + model = af.Collection(galaxies=af.Collection(galaxy_0=al.Galaxy(redshift=0.5))) - analysis = al.AnalysisQuantity( - dataset=dataset_quantity_7x7_array_2d, func_str="convergence_2d_from" - ) + analysis = al.AnalysisQuantity( + dataset=dataset_quantity_7x7_array_2d, func_str="convergence_2d_from" + ) - search = al.m.MockSearch(name="test_search") + search = al.m.MockSearch(name="test_search") - result = search.fit(model=model, analysis=analysis) + result = search.fit(model=model, analysis=analysis) - assert isinstance(result, ResultQuantity) + assert isinstance(result, ResultQuantity) - def test__figure_of_merit__matches_correct_fit_given_galaxy_profiles( - self, dataset_quantity_7x7_array_2d - ): - galaxy = al.Galaxy(redshift=0.5, light=al.mp.Isothermal(einstein_radius=1.0)) - model = af.Collection(galaxies=af.Collection(galaxy=galaxy)) +def test__figure_of_merit__matches_correct_fit_given_galaxy_profiles( + dataset_quantity_7x7_array_2d, +): + galaxy = al.Galaxy(redshift=0.5, light=al.mp.Isothermal(einstein_radius=1.0)) - analysis = al.AnalysisQuantity( - dataset=dataset_quantity_7x7_array_2d, func_str="convergence_2d_from" - ) + model = af.Collection(galaxies=af.Collection(galaxy=galaxy)) - instance = model.instance_from_unit_vector([]) - fit_figure_of_merit = analysis.log_likelihood_function(instance=instance) + analysis = al.AnalysisQuantity( + dataset=dataset_quantity_7x7_array_2d, func_str="convergence_2d_from" + ) - tracer = analysis.tracer_via_instance_from(instance=instance) + instance = model.instance_from_unit_vector([]) + fit_figure_of_merit = analysis.log_likelihood_function(instance=instance) - fit = al.FitQuantity( - dataset=dataset_quantity_7x7_array_2d, - tracer=tracer, - func_str="convergence_2d_from", - ) + tracer = analysis.tracer_via_instance_from(instance=instance) - assert fit.log_likelihood == fit_figure_of_merit + fit = al.FitQuantity( + dataset=dataset_quantity_7x7_array_2d, + tracer=tracer, + func_str="convergence_2d_from", + ) - fit = al.FitQuantity( - dataset=dataset_quantity_7x7_array_2d, - tracer=tracer, - func_str="potential_2d_from", - ) + assert fit.log_likelihood == fit_figure_of_merit - assert fit.log_likelihood != fit_figure_of_merit + fit = al.FitQuantity( + dataset=dataset_quantity_7x7_array_2d, + tracer=tracer, + func_str="potential_2d_from", + ) + + assert fit.log_likelihood != fit_figure_of_merit