diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eb33428c5..ccdd8205f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,99 +1,99 @@ -name: Tests - -on: [push, pull_request] - -jobs: - unittest: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.9, '3.10', '3.11', '3.12'] - steps: - - name: Checkout PyAutoConf - uses: actions/checkout@v2 - with: - repository: rhayes777/PyAutoConf - path: PyAutoConf - - name: Checkout PyAutoFit - uses: actions/checkout@v2 - with: - repository: rhayes777/PyAutoFit - path: PyAutoFit - - name: Checkout PyAutoArray - uses: actions/checkout@v2 - with: - repository: Jammy2211/PyAutoArray - path: PyAutoArray - - name: Checkout PyAutoGalaxy - uses: actions/checkout@v2 - with: - repository: Jammy2211/PyAutoGalaxy - path: PyAutoGalaxy - - name: Checkout PyAutoLens - uses: actions/checkout@v2 - with: - path: PyAutoLens - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Extract branch name - shell: bash - run: | - cd PyAutoLens - echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" - id: extract_branch - - name: Change to same branch if exists in deps - shell: bash - run: | - export PACKAGES=("PyAutoConf" "PyAutoArray" "PyAutoFit" "PyAutoGalaxy") - export BRANCH="${{ steps.extract_branch.outputs.branch }}" - for PACKAGE in ${PACKAGES[@]}; do - pushd $PACKAGE - export existed_in_remote=$(git ls-remote --heads origin ${BRANCH}) - - if [[ -z ${existed_in_remote} ]]; then - echo "Branch $BRANCH did not exist in $PACKAGE" - else - echo "Branch $BRANCH did exist in $PACKAGE" - git fetch - git checkout $BRANCH - fi - popd - done - - name: Install dependencies - run: | - pip3 install --upgrade pip - pip3 install setuptools - pip3 install wheel - pip3 install pytest coverage pytest-cov - pip3 install -r PyAutoConf/requirements.txt - pip3 install -r PyAutoFit/requirements.txt - pip3 install -r PyAutoArray/requirements.txt - pip3 install -r PyAutoArray/optional_requirements.txt - pip3 install -r PyAutoGalaxy/requirements.txt - pip3 install -r PyAutoGalaxy/optional_requirements.txt - pip3 install -r PyAutoLens/requirements.txt - pip3 install -r PyAutoLens/optional_requirements.txt - - name: Run tests - run: | - export ROOT_DIR=`pwd` - export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoConf - export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoFit - export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoArray - export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoGalaxy - export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoLens - pushd PyAutoLens - pytest --cov autolens --cov-report xml:coverage.xml - - name: Slack send - if: ${{ failure() }} - id: slack - uses: slackapi/slack-github-action@v1.21.0 - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - with: - channel-id: C03S98FEDK2 - payload: | - { - "text": "${{ github.repository }}/${{ github.ref_name }} (Python ${{ matrix.python-version }}) build result: ${{ job.status }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - } +name: Tests + +on: [push, pull_request] + +jobs: + unittest: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9, '3.10', '3.11', '3.12'] + steps: + - name: Checkout PyAutoConf + uses: actions/checkout@v2 + with: + repository: rhayes777/PyAutoConf + path: PyAutoConf + - name: Checkout PyAutoFit + uses: actions/checkout@v2 + with: + repository: rhayes777/PyAutoFit + path: PyAutoFit + - name: Checkout PyAutoArray + uses: actions/checkout@v2 + with: + repository: Jammy2211/PyAutoArray + path: PyAutoArray + - name: Checkout PyAutoGalaxy + uses: actions/checkout@v2 + with: + repository: Jammy2211/PyAutoGalaxy + path: PyAutoGalaxy + - name: Checkout PyAutoLens + uses: actions/checkout@v2 + with: + path: PyAutoLens + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Extract branch name + shell: bash + run: | + cd PyAutoLens + echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + id: extract_branch + - name: Change to same branch if exists in deps + shell: bash + run: | + export PACKAGES=("PyAutoConf" "PyAutoArray" "PyAutoFit" "PyAutoGalaxy") + export BRANCH="${{ steps.extract_branch.outputs.branch }}" + for PACKAGE in ${PACKAGES[@]}; do + pushd $PACKAGE + export existed_in_remote=$(git ls-remote --heads origin ${BRANCH}) + + if [[ -z ${existed_in_remote} ]]; then + echo "Branch $BRANCH did not exist in $PACKAGE" + else + echo "Branch $BRANCH did exist in $PACKAGE" + git fetch + git checkout $BRANCH + fi + popd + done + - name: Install dependencies + run: | + pip3 install --upgrade pip + pip3 install setuptools + pip3 install wheel + pip3 install pytest coverage pytest-cov + pip3 install -r PyAutoConf/requirements.txt + pip3 install -r PyAutoFit/requirements.txt + pip3 install -r PyAutoArray/requirements.txt + pip3 install -r PyAutoArray/optional_requirements.txt + pip3 install -r PyAutoGalaxy/requirements.txt + pip3 install -r PyAutoGalaxy/optional_requirements.txt + pip3 install -r PyAutoLens/requirements.txt + pip3 install -r PyAutoLens/optional_requirements.txt + - name: Run tests + run: | + export ROOT_DIR=`pwd` + export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoConf + export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoFit + export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoArray + export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoGalaxy + export PYTHONPATH=$PYTHONPATH:$ROOT_DIR/PyAutoLens + pushd PyAutoLens + pytest --cov autolens --cov-report xml:coverage.xml + - name: Slack send + if: ${{ failure() }} + id: slack + uses: slackapi/slack-github-action@v1.21.0 + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + with: + channel-id: C03S98FEDK2 + payload: | + { + "text": "${{ github.repository }}/${{ github.ref_name }} (Python ${{ matrix.python-version }}) build result: ${{ job.status }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + } diff --git a/autolens/__init__.py b/autolens/__init__.py index 767a1c7f9..004171990 100644 --- a/autolens/__init__.py +++ b/autolens/__init__.py @@ -5,16 +5,12 @@ from autoarray.dataset.interferometer.dataset import ( Interferometer, ) -from autoarray.dataset.over_sampling import OverSamplingDataset from autoarray.dataset.grids import GridsInterface from autoarray.dataset.dataset_model import DatasetModel from autoarray.mask.mask_1d import Mask1D from autoarray.mask.mask_2d import Mask2D from autoarray.operators.convolver import Convolver -from autoarray.operators.over_sampling.uniform import OverSamplingUniform # noqa -from autoarray.operators.over_sampling.uniform import OverSamplerUniform # noqa -from autoarray.operators.over_sampling.iterate import OverSamplingIterate -from autoarray.operators.over_sampling.iterate import OverSamplerIterate +from autoarray.operators.over_sampling.over_sampler import OverSampler # noqa from autoarray.inversion.inversion.dataset_interface import DatasetInterface from autoarray.inversion.inversion.mapper_valued import MapperValued from autoarray.inversion.pixelization import image_mesh @@ -30,7 +26,6 @@ from autoarray.inversion.pixelization.mappers.mapper_grids import MapperGrids from autoarray.inversion.pixelization.mappers.factory import mapper_from as Mapper from autoarray.inversion.pixelization.border_relocator import BorderRelocator -from autoarray.operators.over_sampling.grid_oversampled import Grid2DOverSampled from autoarray.operators.transformer import TransformerDFT from autoarray.operators.transformer import TransformerNUFFT from autoarray.structures.arrays.uniform_1d import Array1D @@ -39,8 +34,6 @@ from autoarray.structures.grids.uniform_1d import Grid1D from autoarray.structures.grids.uniform_2d import Grid2D from autoarray.structures.grids.irregular_2d import Grid2DIrregular -from autoarray.structures.grids.irregular_2d import Grid2DIrregularUniform -from autoarray.operators.over_sampling.iterate import OverSamplingIterate from autoarray.structures.mesh.rectangular_2d import Mesh2DRectangular from autoarray.structures.mesh.voronoi_2d import Mesh2DVoronoi from autoarray.structures.mesh.delaunay_2d import Mesh2DDelaunay @@ -109,7 +102,7 @@ from .point.fit.positions.image.pair_all import FitPositionsImagePairAll from .point.fit.positions.image.pair_repeat import FitPositionsImagePairRepeat from .point.fit.positions.source.separations import FitPositionsSource -from .point.fit.positions.source.max_separation import FitPositionsSourceMaxSeparation +from .point.max_separation import SourceMaxSeparation from .point.model.analysis import AnalysisPoint from .point.solver import PointSolver from .point.solver.shape_solver import ShapeSolver @@ -123,4 +116,4 @@ conf.instance.register(__file__) -__version__ = "2024.9.21.2" +__version__ = "2025.1.18.7" diff --git a/autolens/aggregator/tracer.py b/autolens/aggregator/tracer.py index 4b3db87cb..a0d1efb3a 100644 --- a/autolens/aggregator/tracer.py +++ b/autolens/aggregator/tracer.py @@ -41,13 +41,15 @@ def _tracer_from( galaxies = instance.galaxies if hasattr(instance, "extra_galaxies"): - galaxies = galaxies + fit.instance.extra_galaxies + if fit.instance.extra_galaxies is not None: + galaxies = galaxies + fit.instance.extra_galaxies else: galaxies = fit.instance.galaxies if hasattr(fit.instance, "extra_galaxies"): - galaxies = galaxies + fit.instance.extra_galaxies + if fit.instance.extra_galaxies is not None: + galaxies = galaxies + fit.instance.extra_galaxies try: cosmology = instance.cosmology @@ -56,17 +58,18 @@ def _tracer_from( tracer = Tracer(galaxies=galaxies, cosmology=cosmology) - if len(fit.children) > 0: - logger.info( - """ - Using database for a fit with multiple summed Analysis objects. - - Tracer objects do not fully support this yet (e.g. model parameters which vary over analyses may be incorrect) - so proceed with caution! - """ - ) - - return [tracer] * len(fit.children) + if fit.children is not None: + if len(fit.children) > 0: + logger.info( + """ + Using database for a fit with multiple summed Analysis objects. + + Tracer objects do not fully support this yet (e.g. model parameters which vary over analyses may be incorrect) + so proceed with caution! + """ + ) + + return [tracer] * len(fit.children) return [tracer] diff --git a/autolens/analysis/plotter_interface.py b/autolens/analysis/plotter_interface.py index d62226e76..112cce7a4 100644 --- a/autolens/analysis/plotter_interface.py +++ b/autolens/analysis/plotter_interface.py @@ -26,19 +26,19 @@ class PlotterInterface(AgPlotterInterface): The path on the hard-disk to the `image` folder of the non-linear searches results. """ - def tracer(self, tracer: Tracer, grid: aa.type.Grid2DLike, during_analysis: bool): + def tracer(self, tracer: Tracer, grid: aa.type.Grid2DLike): """ Visualizes a `Tracer` object. - Images are output to the `image` folder of the `image_path` in a subfolder called `tracer`. When - used with a non-linear search the `image_path` points to the search's results folder and this function - visualizes the maximum log likelihood `Tracer` inferred by the search so far. + Images are output to the `image` folder of the `image_path`. When used with a non-linear search the `image_path` + points to the search's results folder and this function visualizes the maximum log likelihood `Tracer` + inferred by the search so far. - Visualization includes individual images of attributes of the tracer (e.g. its image, convergence, deflection - angles) and a subplot of all these attributes on the same figure. + Visualization includes a subplot of individual images of attributes of the tracer (e.g. its image, convergence, + deflection angles) and .fits files containing its attributes grouped together. - The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under the - [tracer] header. + The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under + the `tracer` header. Parameters ---------- @@ -47,14 +47,12 @@ def tracer(self, tracer: Tracer, grid: aa.type.Grid2DLike, during_analysis: bool grid A 2D grid of (y,x) arc-second coordinates used to perform ray-tracing, which is the masked grid tied to the dataset. - during_analysis - Whether visualization is performed during a non-linear search or once it is completed. """ def should_plot(name): return plot_setting(section="tracer", name=name) - mat_plot_2d = self.mat_plot_2d_from(subfolders="tracer") + mat_plot_2d = self.mat_plot_2d_from() tracer_plotter = TracerPlotter( tracer=tracer, @@ -66,81 +64,24 @@ def should_plot(name): if should_plot("subplot_galaxies_images"): tracer_plotter.subplot_galaxies_images() - tracer_plotter.figures_2d( - image=should_plot("image"), - source_plane=should_plot("source_plane_image"), - deflections_y=should_plot("deflections"), - deflections_x=should_plot("deflections"), - magnification=should_plot("magnification"), - ) - - mat_plot_2d.use_log10 = True - - tracer_plotter.figures_2d( - convergence=should_plot("convergence"), - potential=should_plot("potential"), - ) - - if should_plot("lens_image"): - tracer_plotter.figures_2d_of_planes( - plane_image=True, plane_index=0, zoom_to_brightest=False - ) - - mat_plot_2d.use_log10 = False - - if not during_analysis and should_plot("all_at_end_png"): - mat_plot_2d = self.mat_plot_2d_from( - subfolders=path.join("tracer", "end"), - ) - - tracer_plotter = TracerPlotter( - tracer=tracer, - grid=grid, - mat_plot_2d=mat_plot_2d, - include_2d=self.include_2d, - ) - - tracer_plotter.figures_2d( - image=True, - source_plane=True, - deflections_y=True, - deflections_x=True, - magnification=True, - ) - - mat_plot_2d.use_log10 = True - - tracer_plotter.figures_2d( - convergence=True, - potential=True, - ) - - tracer_plotter.figures_2d_of_planes( - plane_image=True, plane_index=0, zoom_to_brightest=False - ) - - mat_plot_2d.use_log10 = False + if should_plot("fits_tracer"): + number_plots = 4 - if not during_analysis and should_plot("all_at_end_fits"): - mat_plot_2d = self.mat_plot_2d_from( - subfolders=path.join("tracer", "fits"), format="fits" + multi_plotter = aplt.MultiFigurePlotter( + plotter_list=[tracer_plotter] * number_plots ) - tracer_plotter = TracerPlotter( - tracer=tracer, - grid=grid, - mat_plot_2d=mat_plot_2d, - include_2d=self.include_2d, - ) - - tracer_plotter.figures_2d( - image=True, - source_plane=True, - convergence=True, - potential=True, - deflections_y=True, - deflections_x=True, - magnification=True, + multi_plotter.output_to_fits( + func_name_list=["figures_2d"] * number_plots, + figure_name_list=[ + "convergence", + "potential", + "deflections_y", + "deflections_x", + ], + tag_list=["convergence", "potential", "deflections_y", "deflections_x"], + filename="tracer", + remove_fits_first=True, ) def image_with_positions(self, image: aa.Array2D, positions: aa.Grid2DIrregular): @@ -148,13 +89,13 @@ def image_with_positions(self, image: aa.Array2D, positions: aa.Grid2DIrregular) Visualizes the positions of a model-fit, where these positions are used to resample lens models where the positions to do trace within an input threshold of one another in the source-plane. - Images are output to the `image` folder of the `image_path` in a subfolder called `positions`. When - used with a non-linear search the `image_path` is the output folder of the non-linear search. + Images are output to the `image` folder of the `image_path`. When used with a non-linear search the `image_path` + is the output folder of the non-linear search. The visualization is an image of the strong lens with the positions overlaid. The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under the - [tracer] header. + `positions` header. Parameters ---------- @@ -167,7 +108,7 @@ def image_with_positions(self, image: aa.Array2D, positions: aa.Grid2DIrregular) def should_plot(name): return plot_setting(section=["positions"], name=name) - mat_plot_2d = self.mat_plot_2d_from(subfolders="positions") + mat_plot_2d = self.mat_plot_2d_from() if positions is not None: visuals_2d = aplt.Visuals2D(positions=positions) @@ -178,6 +119,8 @@ def should_plot(name): include_2d=self.include_2d, visuals_2d=visuals_2d, ) + image_plotter.set_filename("image_with_positions") + if should_plot("image_with_positions"): image_plotter.figure_2d() diff --git a/autolens/analysis/positions.py b/autolens/analysis/positions.py index 99080065a..76a791a92 100644 --- a/autolens/analysis/positions.py +++ b/autolens/analysis/positions.py @@ -13,8 +13,8 @@ from autogalaxy.analysis.analysis.dataset import AnalysisDataset from autolens.lens.tracer import Tracer -from autolens.point.fit.positions.source.max_separation import ( - FitPositionsSourceMaxSeparation, +from autolens.point.max_separation import ( + SourceMaxSeparation, ) from autolens import exc @@ -77,7 +77,7 @@ def output_positions_info(self, output_path: str, tracer: Tracer): ------- """ - positions_fit = FitPositionsSourceMaxSeparation( + positions_fit = SourceMaxSeparation( data=self.positions, noise_map=None, tracer=tracer ) @@ -141,7 +141,7 @@ def log_likelihood_function_positions_overwrite( if not tracer.has(cls=ag.mp.MassProfile) or len(tracer.planes) == 1: return - positions_fit = FitPositionsSourceMaxSeparation( + positions_fit = SourceMaxSeparation( data=self.positions, noise_map=None, tracer=tracer ) @@ -264,7 +264,7 @@ def log_likelihood_penalty_from(self, tracer: Tracer) -> Optional[float]: if not tracer.has(cls=ag.mp.MassProfile) or len(tracer.planes) == 1: return - positions_fit = FitPositionsSourceMaxSeparation( + positions_fit = SourceMaxSeparation( data=self.positions, noise_map=None, tracer=tracer ) diff --git a/autolens/analysis/preloads.py b/autolens/analysis/preloads.py new file mode 100644 index 000000000..e69de29bb diff --git a/autolens/analysis/result.py b/autolens/analysis/result.py index 4e6b78906..45a026101 100644 --- a/autolens/analysis/result.py +++ b/autolens/analysis/result.py @@ -1,3 +1,4 @@ +import logging import os import numpy as np from typing import Optional, Union @@ -9,12 +10,14 @@ from autolens.analysis.positions import PositionsLHResample from autolens.analysis.positions import PositionsLHPenalty -from autolens.point.fit.positions.source.max_separation import ( - FitPositionsSourceMaxSeparation, +from autolens.point.max_separation import ( + SourceMaxSeparation, ) from autolens.lens.tracer import Tracer from autolens.point.solver import PointSolver +logger = logging.getLogger(__name__) + class Result(AgResultDataset): @property @@ -44,7 +47,7 @@ def max_log_likelihood_positions_threshold(self) -> float: ------- """ - positions_fits = FitPositionsSourceMaxSeparation( + positions_fits = SourceMaxSeparation( data=self.analysis.positions_likelihood.positions, noise_map=None, tracer=self.max_log_likelihood_tracer, @@ -101,8 +104,76 @@ def image_plane_multiple_image_positions(self) -> aa.Grid2DIrregular: source_plane_coordinate=self.source_plane_centre.in_list[0], ) + if multiple_images.shape[0] == 1: + return self.image_plane_multiple_image_positions_for_single_image_from() + return aa.Grid2DIrregular(values=multiple_images) + def image_plane_multiple_image_positions_for_single_image_from( + self, increments: int = 20 + ) -> aa.Grid2DIrregular: + """ + If the standard point solver only locates one multiple image, finds one or more additional images, which are + not technically multiple image in the point source regime, but are close enough to it they can be used + in a position threshold likelihood. + + This is performed by incrementally moving the source-plane centre's coordinates towards the centre of the + source-plane at (0.0", 0.0"). This ensures that the centre will eventually go inside the caustic, where + multiple images are formed. + + To move the source-plane centre, the original source-plane centre is multiplied by a factor that decreases + from 1.0 to 0.0 in increments of 1/increments. For example, if the source-plane centre is (1.0", -0.5") and + the `factor` is 0.5, the input source-plane centre is (0.5", -0.25"). + + The multiple images are always computed for the same mass model, thus they will always be valid multiple images + for the model being fitted, but as the factor decrease the multiple images may move furhter from their observed + positions. + + Parameters + ---------- + increments + The number of increments the source-plane centre is moved to compute multiple images. + """ + + logger.info( + """ + Could not find multiple images for maximum likelihood lens model. + + Incrementally moving source centre inwards towards centre of source-plane until caustic crossing occurs + and multiple images are formed. + """ + ) + + grid = self.analysis.dataset.mask.derive_grid.all_false + + centre = self.source_plane_centre.in_list[0] + + solver = PointSolver.for_grid( + grid=grid, + pixel_scale_precision=0.001, + ) + + for i in range(1, increments): + factor = 1.0 - (1.0 * (i / increments)) + + multiple_images = solver.solve( + tracer=self.max_log_likelihood_tracer, + source_plane_coordinate=(centre[0] * factor, centre[1] * factor), + ) + + if multiple_images.shape[0] > 1: + return aa.Grid2DIrregular(values=multiple_images) + + logger.info( + """ + Could not find multiple images for maximum likelihood lens model, even after incrementally moving the source + centre inwards to the centre of the source-plane. + + Set the multiple image postiions to two images at (1.0", 1.0") so code continues to run. + """ + ) + return aa.Grid2DIrregular(values=[(1.0, 1.0), (1.0, 1.0)]) + def positions_threshold_from( self, factor=1.0, @@ -148,7 +219,7 @@ def positions_threshold_from( tracer = Tracer(galaxies=self.max_log_likelihood_galaxies) - positions_fits = FitPositionsSourceMaxSeparation( + positions_fits = SourceMaxSeparation( data=positions, noise_map=None, tracer=tracer ) @@ -168,7 +239,47 @@ def positions_likelihood_from( minimum_threshold=None, use_resample=False, positions: Optional[aa.Grid2DIrregular] = None, + mass_centre_radial_distance_min: float = None, ) -> Union[PositionsLHPenalty, PositionsLHResample]: + """ + Returns a `PositionsLH` object from the result of a lens model-fit, where the maximum log likelihood mass + and source models are used to determine the multiple image positions in the image-plane and source-plane + and ray-trace them to the source-plane to determine the threshold. + + In chained fits, for example the SLaM pipelines, this means that a simple initial fit (e.g. SIE mass model, + parametric source) can be used to determine the multiple image positions and threshold for a more complex + subsequent fit (e.g. power-law mass model, pixelized source). + + The mass model central image is removed from the solution, as this is rarely physically observed and therefore + should not be included in the likelihood penalty or resampling. It is removed by setting a positive + magnification threshold in the `PointSolver`. For strange lens models the central image may still be + solved for, in which case the `mass_centre_radial_distance_min` parameter can be used to remove it. + + Parameters + ---------- + factor + The value the computed threshold is multiplied by to make the position threshold larger or smaller than the + maximum log likelihood model's threshold. + minimum_threshold + The output threshold is rounded up to this value if it is below it, to avoid extremely small threshold + values. + use_resample + If `False` the `PositionsLH` object is created using the `PositionsLHPenalty` class, which uses the + threshold to apply a penalty term to the likelihood. If `True` the `PositionsLH` object is created using + the `PositionsLHResample` class, which resamples the positions to the threshold. + positions + If input, these positions are used instead of the computed multiple image positions from the lens mass + model. + mass_centre_radial_distance_min + The minimum radial distance from the mass model centre that a multiple image position must be to be + included in the likelihood penalty or resampling. If `None` all positions are used. This is an additional + method to remove central images that may make it through the point solver's magnification threshold. + + Returns + ------- + The `PositionsLH` object used to apply a likelihood penalty or resample the positions. + """ + if os.environ.get("PYAUTOFIT_TEST_MODE") == "1": return None @@ -177,6 +288,18 @@ def positions_likelihood_from( if positions is None else positions ) + + if mass_centre_radial_distance_min is not None: + mass_centre = self.max_log_likelihood_tracer.extract_attribute( + cls=ag.mp.MassProfile, attr_name="centre" + ) + + distances = positions.distances_to_coordinate_from( + coordinate=mass_centre[0] + ) + + positions = positions[distances > mass_centre_radial_distance_min] + threshold = self.positions_threshold_from( factor=factor, minimum_threshold=minimum_threshold, positions=positions ) diff --git a/autolens/config/general.yaml b/autolens/config/general.yaml index 32229bf70..75e85a7dd 100644 --- a/autolens/config/general.yaml +++ b/autolens/config/general.yaml @@ -1,4 +1,4 @@ -output: - fit_dill: false -test: - disable_positions_lh_inversion_check: false +output: + fit_dill: false +test: + disable_positions_lh_inversion_check: false diff --git a/autolens/config/non_linear.yaml b/autolens/config/non_linear.yaml index 334c3448e..2ed0f9508 100644 --- a/autolens/config/non_linear.yaml +++ b/autolens/config/non_linear.yaml @@ -1,66 +1,66 @@ -nest: - DynestyDynamic: - initialize: - method: prior - parallel: - force_x1_cpu: false - number_of_cores: 1 - printing: - silence: false - run: - dlogz_init: 0.01 - logl_max_init: .inf - maxcall: null - maxcall_init: null - maxiter: null - maxiter_init: null - n_effective: .inf - n_effective_init: .inf - nlive_init: 500 - search: - bootstrap: null - bound: multi - enlarge: null - facc: 0.2 - first_update: null - fmove: 0.9 - max_move: 100 - sample: rwalk - slices: 5 - update_interval: null - walks: 5 - updates: - iterations_per_update: 2500 - remove_state_files_at_end: true - DynestyStatic: - initialize: - method: prior - parallel: - number_of_cores: 1 - printing: - silence: false - run: - dlogz: null - logl_max: .inf - maxcall: null - maxiter: null - n_effective: null - search: - bootstrap: null - bound: multi - enlarge: null - facc: 0.2 - first_update: null - fmove: 0.9 - max_move: 100 - nlive: 50 - sample: rwalk - slices: 5 - update_interval: null - walks: 5 - updates: - iterations_per_update: 5000 - log_every_update: 1 - model_results_every_update: 1 - remove_state_files_at_end: true - visualize_every_update: 1 +nest: + DynestyDynamic: + initialize: + method: prior + parallel: + force_x1_cpu: false + number_of_cores: 1 + printing: + silence: false + run: + dlogz_init: 0.01 + logl_max_init: .inf + maxcall: null + maxcall_init: null + maxiter: null + maxiter_init: null + n_effective: .inf + n_effective_init: .inf + nlive_init: 500 + search: + bootstrap: null + bound: multi + enlarge: null + facc: 0.2 + first_update: null + fmove: 0.9 + max_move: 100 + sample: rwalk + slices: 5 + update_interval: null + walks: 5 + updates: + iterations_per_update: 2500 + remove_state_files_at_end: true + DynestyStatic: + initialize: + method: prior + parallel: + number_of_cores: 1 + printing: + silence: false + run: + dlogz: null + logl_max: .inf + maxcall: null + maxiter: null + n_effective: null + search: + bootstrap: null + bound: multi + enlarge: null + facc: 0.2 + first_update: null + fmove: 0.9 + max_move: 100 + nlive: 50 + sample: rwalk + slices: 5 + update_interval: null + walks: 5 + updates: + iterations_per_update: 5000 + log_every_update: 1 + model_results_every_update: 1 + remove_state_files_at_end: true + visualize_every_update: 1 diff --git a/autolens/config/visualize/plots.yaml b/autolens/config/visualize/plots.yaml index 19959d32a..7739397b0 100644 --- a/autolens/config/visualize/plots.yaml +++ b/autolens/config/visualize/plots.yaml @@ -1,98 +1,43 @@ - dataset: # Settings for plots of all datasets (e.g. ImagingPlotter, InterferometerPlotter). - subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the data, noise-map, etc.)? - data: false - noise_map: false - signal_to_noise_map: false - imaging: # Settings for plots of imaging datasets (e.g. ImagingPlotter) - psf: false - positions: # Settings for plots with resampling image-positions on (e.g. the image). - image_with_positions: true - fit: # Settings for plots of all fits (e.g. FitImagingPlotter, FitInterferometerPlotter). - subplot_fit: true # Plot subplot of all fit quantities for any dataset (e.g. the model data, residual-map, etc.)? - all_at_end_png: true # Plot all individual plots listed below as .png (even if False)? - all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)? - all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)? - subplot_of_planes: false # Plot subplot of the model-image, subtracted image and other quantities of each plane? - subplot_galaxies_images: false # Plot subplot of the image of each plane in the model? - data: false - noise_map: false - signal_to_noise_map: false - model_data: false - residual_map: false - chi_squared_map: false # Plot individual plots of the chi-squared-map? - residual_flux_fraction: false # Plot individual plots of the residual_flux_fraction? - model_images_of_planes: false # Plot individual plots of each plane's model image? - subtracted_images_of_planes: false # Plot individual plots of each plane's subtracted image? - plane_images_of_planes: false # Plot individual plots of each plane's image (e.g. in the source plane)? - fit_imaging: {} # Settings for plots of fits to imaging datasets (e.g. FitImagingPlotter). - tracer: # Settings for plots of tracers (e.g. TracerPlotter). - subplot_tracer: true # Plot subplot of all quantities in each tracer (e.g. images, convergence)? - all_at_end_png: true # Plot all individual plots listed below as .png (even if False)? - all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)? - all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)? - subplot_galaxies_images: false # Plot subplot of the image of each plane in the tracer? - image: false # Plot image of the tracer (e.g. lens and lensed source in the image-plane)? - source_plane_image: false # Plot image of the tracer's source-plane? - lens_image: false # Plot image of the foreground lens galaxy (log10)? - convergence: false # Plot image of the tracer's convergence (log10)? - potential: false # Plot image of the tracer's potential (log10)? - deflections: false # Plot images of the tracer's y and x deflections? - magnification: false # Plot image of the tracer's magnification? - galaxies_1d: # Settings for 1D plots of galaxies (e.g. GalaxiesPlotter). - image: false - convergence: false - potential: false - inversion: # Settings for plots of inversions (e.g. InversionPlotter). - subplot_inversion: true # Plot subplot of all quantities in each inversion (e.g. reconstrucuted image, reconstruction)? - subplot_mappings: true # Plot subplot of the image-to-source pixels mappings of each pixelization? - all_at_end_png: true # Plot all individual plots listed below as .png (even if False)? - all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)? - all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)? - errors: false - reconstructed_image: false - reconstruction: false - regularization_weights: false - adapt: # Settings for plots of adapt images used by adaptive pixelizations. - images_of_galaxies: true - model_image: true - interferometer: # Settings for plots of interferometer datasets (e.g. InterferometerPlotter). - amplitudes_vs_uv_distances: false - phases_vs_uv_distances: false - uv_wavelengths: false - dirty_image: false - dirty_noise_map: false - dirty_signal_to_noise_map: false - fit_interferometer: # Settings for plots of fits to interferometer datasets (e.g. FitInterferometerPlotter). - subplot_fit_dirty_images: false # Plot subplot of the dirty-images of all interferometer datasets? - subplot_fit_real_space: false # Plot subplot of the real-space images of all interferometer datasets? - amplitudes_vs_uv_distances: false - phases_vs_uv_distances: false - uv_wavelengths: false - dirty_image: false - dirty_noise_map: false - dirty_signal_to_noise_map: false - dirty_residual_map: false - dirty_normalized_residual_map: false - dirty_chi_squared_map: false - fit_quantity: # Settings for plots of fit quantities (e.g. FitQuantityPlotter). - all_at_end_png: true # Plot all individual plots listed below as .png (even if False)? - all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)? - all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)? - chi_squared_map: false - image: true - model_image: false - noise_map: false - residual_map: false - normalized_residual_map: false - galaxies: # Settings for plots of galaxies (e.g. GalaxiesPlotter). - subplot_galaxies: true # Plot subplot of all quantities in each galaxies group (e.g. images, convergence)? - all_at_end_png: true # Plot all individual plots listed below as .png (even if False)? - all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)? - all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)? - subplot_galaxy_images: false # Plot subplot of the image of each galaxy in the model? - image: false - source_plane_image: false - convergence: false - deflections: false - potential: false - magnification: false +dataset: # Settings for plots of all datasets (e.g. ImagingPlotter, InterferometerPlotter). + subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the data, noise-map, etc.)? + +positions: # Settings for plots with resampling image-positions on (e.g. the image). + image_with_positions: true + +point_dataset: # Settings for plots of point source datasets (e.g. PointDatasetPlotter). + subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the data, noise-map, etc.)? + +fit: # Settings for plots of all fits (e.g. FitImagingPlotter, FitInterferometerPlotter). + subplot_fit: true # Plot subplot of all fit quantities for any dataset (e.g. the model data, residual-map, etc.)? + subplot_fit_log10: true # Plot subplot of all fit quantities for any dataset using log10 color maps (e.g. the model data, residual-map, etc.)? + subplot_of_planes: false # Plot subplot of the model-image, subtracted image and other quantities of each plane? + subplot_galaxies_images: false # Plot subplot of the image of each plane in the model? + +fit_imaging: {} # Settings for plots of fits to imaging datasets (e.g. FitImagingPlotter). + +fit_interferometer: # Settings for plots of fits to interferometer datasets (e.g. FitInterferometerPlotter). + subplot_fit_dirty_images: false # Plot subplot of the dirty-images of all interferometer datasets? + subplot_fit_real_space: false # Plot subplot of the real-space images of all interferometer datasets? + +fit_point_dataset: {} # Settings for plots of fits to point source datasets (e.g. FitPointDatasetPlotter). + +tracer: # Settings for plots of tracers (e.g. TracerPlotter). + subplot_tracer: true # Plot subplot of all quantities in each tracer (e.g. images, convergence)? + subplot_galaxies_images: false # Plot subplot of the image of each plane in the tracer? + fits_tracer: false # Output tracer.fits file of tracer's convergence, potential, deflections_y and deflections_x? + +inversion: # Settings for plots of inversions (e.g. InversionPlotter). + subplot_inversion: true # Plot subplot of all quantities in each inversion (e.g. reconstrucuted image, reconstruction)? + subplot_mappings: true # Plot subplot of the image-to-source pixels mappings of each pixelization? + +adapt: # Settings for plots of adapt images used by adaptive pixelizations. + subplot_adapt_images: true # Plot subplot showing each adapt image used for adaptive pixelization? + +fit_quantity: # Settings for plots of fit quantities (e.g. FitQuantityPlotter). + subplot_fit: true + +galaxies: # Settings for plots of galaxies (e.g. GalaxiesPlotter). + subplot_galaxies: true # Plot subplot of all quantities in each galaxies group (e.g. images, convergence)? + subplot_galaxy_images: false # Plot subplot of the image of each galaxy in the model? + subplot_galaxies_1d: false # Plot subplot of all quantities in 1D of each galaxies group (e.g. images, convergence)? + subplot_galaxies_1d_decomposed: false # Plot subplot of all quantities in 1D decomposed of each galaxies group (e.g. images, convergence)? diff --git a/autolens/fixtures.py b/autolens/fixtures.py index 97aa26c0e..45c61caf4 100644 --- a/autolens/fixtures.py +++ b/autolens/fixtures.py @@ -3,6 +3,10 @@ from autogalaxy.fixtures import * +def make_grid_2d_7x7(): + return aa.Grid2D.from_mask(mask=make_mask_2d_7x7(), over_sample_size=1) + + def make_positions_x2(): return al.Grid2DIrregular(values=[(1.0, 1.0), (2.0, 2.0)]) diff --git a/autolens/imaging/fit_imaging.py b/autolens/imaging/fit_imaging.py index 8a08c9c1e..775367fac 100644 --- a/autolens/imaging/fit_imaging.py +++ b/autolens/imaging/fit_imaging.py @@ -46,7 +46,7 @@ def __init__( noise-map (if an inversion is performed the `log_evidence`, including additional terms describing the linear algebra solution, is computed). - When performing a `model-fit`via an `AnalysisImaging` object the `figure_of_merit` of this `FitImaging` object + When performing a `model-fit`via an `AnalysisImaging` object the `figure_of_merit` of this object is called and returned in the `log_likelihood_function`. Parameters @@ -77,36 +77,13 @@ def __init__( self.adapt_images = adapt_images self.settings_inversion = settings_inversion - @cached_property - def grids(self) -> aa.GridsInterface: - - grids = super().grids - - if grids.non_uniform is None: - return grids - - uniform = aa.Grid2D( - values=grids.non_uniform, - mask=self.dataset.mask, - over_sampling=self.dataset.over_sampling.non_uniform, - over_sampling_non_uniform=self.dataset.over_sampling.non_uniform - ) - - return aa.GridsInterface( - uniform=uniform, - non_uniform=grids.non_uniform, - pixelization=grids.pixelization, - blurring=grids.blurring, - border_relocator=grids.border_relocator - ) - @property def blurred_image(self) -> aa.Array2D: """ Returns the image of all light profiles in the fit's tracer convolved with the imaging dataset's PSF. """ return self.tracer.blurred_image_2d_from( - grid=self.grids.uniform, + grid=self.grids.lp, convolver=self.dataset.convolver, blurring_grid=self.grids.blurring, ) @@ -185,7 +162,7 @@ def galaxy_model_image_dict(self) -> Dict[ag.Galaxy, np.ndarray]: """ galaxy_blurred_image_2d_dict = self.tracer.galaxy_blurred_image_2d_dict_from( - grid=self.grids.uniform, + grid=self.grids.lp, convolver=self.dataset.convolver, blurring_grid=self.grids.blurring, ) @@ -256,7 +233,7 @@ def model_images_of_planes_list(self) -> List[aa.Array2D]: model_images_of_planes_list = [ aa.Array2D( - values=np.zeros(self.grids.uniform.shape_slim), mask=self.dataset.mask + values=np.zeros(self.grids.lp.shape_slim), mask=self.dataset.mask ) for i in range(self.tracer.total_planes) ] @@ -312,7 +289,7 @@ def unmasked_blurred_image(self) -> aa.Array2D: exc.raise_linear_light_profile_in_unmasked() return self.tracer.unmasked_blurred_image_2d_from( - grid=self.grids.uniform, psf=self.dataset.psf + grid=self.grids.lp, psf=self.dataset.psf ) @property @@ -328,7 +305,7 @@ def unmasked_blurred_image_of_planes_list(self) -> List[aa.Array2D]: exc.raise_linear_light_profile_in_unmasked() return self.tracer.unmasked_blurred_image_2d_list_from( - grid=self.grids.uniform, psf=self.dataset.psf + grid=self.grids.lp, psf=self.dataset.psf ) @property diff --git a/autolens/imaging/mock/mock_fit_imaging.py b/autolens/imaging/mock/mock_fit_imaging.py index 36bde0bac..763dd8f7e 100644 --- a/autolens/imaging/mock/mock_fit_imaging.py +++ b/autolens/imaging/mock/mock_fit_imaging.py @@ -30,13 +30,13 @@ def grid(self): if self._grid is not None: return self._grid - return super().grids.uniform + return super().grids.lp @property def grids(self) -> aa.GridsInterface: return aa.GridsInterface( - uniform=self.grid, + lp=self.grid, pixelization=self.grid, ) diff --git a/autolens/imaging/model/plotter_interface.py b/autolens/imaging/model/plotter_interface.py index 3ae546fe9..fbf023b3b 100644 --- a/autolens/imaging/model/plotter_interface.py +++ b/autolens/imaging/model/plotter_interface.py @@ -1,10 +1,11 @@ -from os import path from typing import List import autoarray.plot as aplt from autogalaxy.imaging.model.plotter_interface import PlotterInterfaceImaging as AgPlotterInterfaceImaging +from autogalaxy.imaging.model.plotter_interface import fits_to_fits + from autolens.analysis.plotter_interface import PlotterInterface from autolens.imaging.fit_imaging import FitImaging from autolens.imaging.plot.fit_imaging_plotters import FitImagingPlotter @@ -18,35 +19,30 @@ class PlotterInterfaceImaging(PlotterInterface): imaging_combined = AgPlotterInterfaceImaging.imaging_combined def fit_imaging( - self, fit: FitImaging, during_analysis: bool, subfolders: str = "fit_dataset" + self, fit: FitImaging, ): """ Visualizes a `FitImaging` object, which fits an imaging dataset. - Images are output to the `image` folder of the `image_path` in a subfolder called `fit`. When - used with a non-linear search the `image_path` points to the search's results folder and this function - visualizes the maximum log likelihood `FitImaging` inferred by the search so far. + Images are output to the `image` folder of the `image_path`. When used with a non-linear search the `image_path` + points to the search's results folder and this function visualizes the maximum log likelihood `FitImaging` + inferred by the search so far. - Visualization includes individual images of attributes of the `FitImaging` (e.g. the model data, residual map) - and a subplot of all `FitImaging`'s images on the same figure. + Visualization includes a subplot of individual images of attributes of the `FitImaging` (e.g. the model data, + residual map) and .fits files containing its attributes grouped together. - The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under the - [fit] header. + The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under + the `fit` and `fit_imaging` header. Parameters ---------- fit The maximum log likelihood `FitImaging` of the non-linear search which is used to plot the fit. - during_analysis - Whether visualization is performed during a non-linear search or once it is completed. - visuals_2d - An object containing attributes which may be plotted over the figure (e.g. the centres of mass and light - profiles). """ if plot_setting(section="tracer", name="subplot_tracer"): - mat_plot_2d = self.mat_plot_2d_from(subfolders="") + mat_plot_2d = self.mat_plot_2d_from() fit_plotter = FitImagingPlotter( fit=fit, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d @@ -57,29 +53,7 @@ def fit_imaging( def should_plot(name): return plot_setting(section=["fit", "fit_imaging"], name=name) - mat_plot_2d = self.mat_plot_2d_from(subfolders=subfolders) - - fit_plotter = FitImagingPlotter( - fit=fit, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d - ) - - fit_plotter.figures_2d( - data=should_plot("data"), - noise_map=should_plot("noise_map"), - signal_to_noise_map=should_plot("signal_to_noise_map"), - model_image=should_plot("model_data"), - residual_map=should_plot("residual_map"), - chi_squared_map=should_plot("chi_squared_map"), - normalized_residual_map=should_plot("normalized_residual_map"), - ) - - fit_plotter.figures_2d_of_planes( - subtracted_image=should_plot("subtracted_images_of_planes"), - model_image=should_plot("model_images_of_planes"), - plane_image=should_plot("plane_images_of_planes"), - ) - - mat_plot_2d = self.mat_plot_2d_from(subfolders="") + mat_plot_2d = self.mat_plot_2d_from() fit_plotter = FitImagingPlotter( fit=fit, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d @@ -103,78 +77,32 @@ def should_plot(name): except IndexError: pass - if not during_analysis and should_plot("all_at_end_png"): - - mat_plot_2d = self.mat_plot_2d_from( - subfolders=path.join("fit_dataset", "end"), - ) - - fit_plotter = FitImagingPlotter( - fit=fit, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d - ) - - fit_plotter.figures_2d( - data=True, - noise_map=True, - signal_to_noise_map=True, - model_image=True, - residual_map=True, - normalized_residual_map=True, - chi_squared_map=True, - ) - - fit_plotter.figures_2d_of_planes( - subtracted_image=True, model_image=True, plane_image=True - ) - - if not during_analysis and should_plot("all_at_end_fits"): - - mat_plot_2d = self.mat_plot_2d_from( - subfolders=path.join("fit_dataset", "fits"), format="fits" - ) - - fit_plotter = FitImagingPlotter( - fit=fit, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d - ) - - fit_plotter.figures_2d( - data=True, - noise_map=True, - signal_to_noise_map=True, - model_image=True, - residual_map=True, - normalized_residual_map=True, - chi_squared_map=True, - ) - - fit_plotter.figures_2d_of_planes( - subtracted_image=True, model_image=True, plane_image=True, interpolate_to_uniform=True - ) + fits_to_fits(should_plot=should_plot, fit=fit, mat_plot_2d=mat_plot_2d, fit_plotter_cls=FitImagingPlotter) def fit_imaging_combined(self, fit_list: List[FitImaging]): """ Output visualization of all `FitImaging` objects in a summed combined analysis, typically during or after a model-fit is performed. - Images are output to the `image` folder of the `image_path` in a subfolder called `combined`. When used - with a non-linear search the `image_path` is the output folder of the non-linear search. - `. - Visualization includes individual images of attributes of each fit (e.g. data, normalized residual-map) on - a single subplot, such that the full suite of multiple datasets can be viewed on the same figure. + Images are output to the `image` folder of the `image_path`. When used with a non-linear search the `image_path` + is the output folder of the non-linear search. + + Visualization includes a subplot of individual images of attributes of each fit (e.g. data, normalized + residual-map) on a single subplot, such that the full suite of multiple datasets can be viewed on the same figure. The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under - the `fit` header. + the `fit` and `fit_imaging` headers. Parameters ---------- - fit + fit_list The list of imaging fits which are visualized. """ def should_plot(name): return plot_setting(section=["fit", "fit_imaging"], name=name) - mat_plot_2d = self.mat_plot_2d_from(subfolders="combined") + mat_plot_2d = self.mat_plot_2d_from() fit_plotter_list = [ FitImagingPlotter( @@ -268,9 +196,9 @@ def make_subplot_fit(filename_suffix): open_subplot=False, ) - make_subplot_fit(filename_suffix="fit") + make_subplot_fit(filename_suffix="fit_combined") + + for plotter in multi_plotter.plotter_list: + plotter.mat_plot_2d.use_log10 = True - # for plotter in multi_plotter.plotter_list: - # plotter.mat_plot_2d.use_log10 = True - # - # make_subplot_fit(filename_suffix="fit_log10") \ No newline at end of file + make_subplot_fit(filename_suffix="fit_combined_log10") \ No newline at end of file diff --git a/autolens/imaging/model/visualizer.py b/autolens/imaging/model/visualizer.py index 25e094532..22611f7fd 100644 --- a/autolens/imaging/model/visualizer.py +++ b/autolens/imaging/model/visualizer.py @@ -76,9 +76,6 @@ def visualize( 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). - during_analysis - If True the visualization is being performed midway through the non-linear search before it is finished, - which may change which images are output. """ fit = analysis.fit_from(instance=instance) @@ -99,7 +96,7 @@ def visualize( ) try: - plotter_interface.fit_imaging(fit=fit, during_analysis=during_analysis) + plotter_interface.fit_imaging(fit=fit) except exc.InversionException: pass @@ -111,17 +108,16 @@ def visualize( grid = ag.Grid2D.from_extent(extent=extent, shape_native=shape_native) plotter_interface.tracer( - tracer=tracer, grid=grid, during_analysis=during_analysis + tracer=tracer, grid=grid, ) plotter_interface.galaxies( galaxies=tracer.galaxies, - grid=fit.grids.uniform, - during_analysis=during_analysis, + grid=fit.grids.lp, ) if fit.inversion is not None: if fit.inversion.has(cls=ag.AbstractMapper): plotter_interface.inversion( - inversion=fit.inversion, during_analysis=during_analysis + inversion=fit.inversion, ) @staticmethod diff --git a/autolens/imaging/plot/fit_imaging_plotters.py b/autolens/imaging/plot/fit_imaging_plotters.py index 80f6f5891..5ddfb2295 100644 --- a/autolens/imaging/plot/fit_imaging_plotters.py +++ b/autolens/imaging/plot/fit_imaging_plotters.py @@ -148,6 +148,8 @@ def figures_2d_of_planes( subtracted_image: bool = False, model_image: bool = False, plane_image: bool = False, + plane_noise_map: bool = False, + plane_signal_to_noise_map: bool = False, use_source_vmax: bool = False, zoom_to_brightest: bool = True, interpolate_to_uniform: bool = False, @@ -178,6 +180,14 @@ def figures_2d_of_planes( Whether to make a 2D plot (via `imshow`) of the image of a plane in its source-plane (e.g. unlensed). Depending on how the fit is performed, this could either be an image of light profiles of the reconstruction of an `Inversion`. + plane_noise_map + Whether to make a 2D plot of the noise-map of a plane in its source-plane, where the + noise map can only be computed when a pixelized source reconstruction is performed and they correspond to + the noise map in each reconstructed pixel as given by the inverse curvature matrix. + plane_signal_to_noise_map + Whether to make a 2D plot of the signal-to-noise map of a plane in its source-plane, + where the signal-to-noise map values can only be computed when a pixelized source reconstruction and they + are the ratio of reconstructed flux to error in each pixel. use_source_vmax If `True`, the maximum value of the lensed source (e.g. in the image-plane) is used to set the `vmax` of certain plots (e.g. the `data`) in order to ensure the lensed source is visible compared to the lens. @@ -283,6 +293,8 @@ def figures_2d_of_planes( elif self.tracer.planes[plane_index].has(cls=aa.Pixelization): + pix = self.tracer.planes[plane_index].cls_list_from(cls=aa.Pixelization)[0] + inversion_plotter = self.inversion_plotter_of_plane( plane_index=plane_index ) @@ -294,8 +306,41 @@ def figures_2d_of_planes( interpolate_to_uniform=interpolate_to_uniform ) - if use_source_vmax: - self.mat_plot_2d.cmap.kwargs.pop("vmax") + if use_source_vmax: + try: + self.mat_plot_2d.cmap.kwargs.pop("vmax") + except KeyError: + pass + + if plane_noise_map: + + if self.tracer.planes[plane_index].has(cls=aa.Pixelization): + + inversion_plotter = self.inversion_plotter_of_plane( + plane_index=plane_index + ) + + inversion_plotter.figures_2d_of_pixelization( + pixelization_index=0, + reconstruction_noise_map=True, + zoom_to_brightest=zoom_to_brightest, + interpolate_to_uniform=interpolate_to_uniform + ) + + if plane_signal_to_noise_map: + + if self.tracer.planes[plane_index].has(cls=aa.Pixelization): + + inversion_plotter = self.inversion_plotter_of_plane( + plane_index=plane_index + ) + + inversion_plotter.figures_2d_of_pixelization( + pixelization_index=0, + signal_to_noise_map=True, + zoom_to_brightest=zoom_to_brightest, + interpolate_to_uniform=interpolate_to_uniform + ) def subplot_of_planes(self, plane_index: Optional[int] = None): """ @@ -614,7 +659,12 @@ def subplot_mappings_of_plane(self, plane_index: Optional[int] = None, auto_file "total_mappings_pixels" ] - pix_indexes = inversion_plotter.inversion.brightest_pixel_list_from( + mapper = inversion_plotter.inversion.cls_list_from(cls=aa.AbstractMapper)[0] + mapper_valued = aa.MapperValued( + values=inversion_plotter.inversion.reconstruction_dict[mapper], + mapper=mapper, + ) + pix_indexes = mapper_valued.max_pixel_list_from( total_pixels=total_pixels, filter_neighbors=True ) diff --git a/autolens/imaging/simulator.py b/autolens/imaging/simulator.py index 2bc49bfeb..7d68d2007 100644 --- a/autolens/imaging/simulator.py +++ b/autolens/imaging/simulator.py @@ -9,21 +9,30 @@ class SimulatorImaging(aa.SimulatorImaging): def via_tracer_from(self, tracer : Tracer, grid : aa.type.Grid2DLike) -> aa.Imaging: """ - Simulate an `Imaging` dataset from an input tracer and grid. + Simulate an `Imaging` dataset from an input `Tracer` object and a 2D grid of (y,x) coordinates. - The tracer is used to perform ray-tracing and generate the image of the strong lens galaxies (e.g. - the lens light, lensed source light, etc) which is simulated. + The mass profiles of each galaxy in the tracer are used to perform ray-tracing of the input 2D grid and + their light profiles are used to generate the image of the galaxies which are simulated. The steps of the `SimulatorImaging` simulation process (e.g. PSF convolution, noise addition) are - described in the `SimulatorImaging` `__init__` method docstring. + described in the `SimulatorImaging` `__init__` method docstring, found in the PyAutoArray project. + + If one of more galaxy light profiles are a `LightProfileSNR` object, the `intensity` of the light profile is + automatically set such that the signal-to-noise ratio of the light profile is equal to its input + `signal_to_noise_ratio` value. + + For example, if a `LightProfileSNR` object has a `signal_to_noise_ratio` of 5.0, the intensity of the light + profile is set such that the peak surface brightness of the profile is 5.0 times the background noise level of + the image. Parameters ---------- tracer The tracer, which describes the ray-tracing and strong lens configuration used to simulate the imaging - dataset. + dataset as well as the light profiles of the galaxies used to simulate the image of the galaxies. grid - The image-plane grid which the image of the strong lens is generated on. + The 2D grid of (y,x) coordinates which the mass profiles of the galaxies in the tracer are ray-traced using + in order to generate the image of the galaxies via their light profiles. """ tracer.set_snr_of_snr_light_profiles( @@ -44,21 +53,30 @@ def via_tracer_from(self, tracer : Tracer, grid : aa.type.Grid2DLike) -> aa.Imag def via_galaxies_from(self, galaxies : List[ag.Galaxy], grid : aa.type.Grid2DLike) -> aa.Imaging: """ - Simulate an `Imaging` dataset from an input list of galaxies and grid. + Simulate an `Imaging` dataset from an input list of `Galaxy` objects and a 2D grid of (y,x) coordinates. - The galaxies are used to create a tracer, which performs ray-tracing and generate the image of the strong lens - galaxies (e.g. the lens light, lensed source light, etc) which is simulated. + The galaxies are used to create a `Tracer`. The mass profiles of each galaxy in the tracer are used to + perform ray-tracing of the input 2D grid and their light profiles are used to generate the image of the + galaxies which are simulated. The steps of the `SimulatorImaging` simulation process (e.g. PSF convolution, noise addition) are described in the `SimulatorImaging` `__init__` method docstring. + If one of more galaxy light profiles are a `LightProfileSNR` object, the `intensity` of the light profile is + automatically set such that the signal-to-noise ratio of the light profile is equal to its input + `signal_to_noise_ratio` value. + + For example, if a `LightProfileSNR` object has a `signal_to_noise_ratio` of 5.0, the intensity of the light + profile is set such that the peak surface brightness of the profile is 5.0 times the background noise level of + the image. + Parameters ---------- galaxies The galaxies used to create the tracer, which describes the ray-tracing and strong lens configuration used to simulate the imaging dataset. grid - The image-plane grid which the image of the strong lens is generated on. + The image-plane 2D grid of (y,x) coordinates grid which the image of the strong lens is generated on. """ tracer = Tracer(galaxies=galaxies) @@ -87,14 +105,20 @@ def via_deflections_and_galaxies_from(self, deflections : aa.VectorYX2D, galaxie The galaxies used to create the tracer, which describes the ray-tracing and strong lens configuration used to simulate the imaging dataset. grid - The image-plane grid which the image of the strong lens is generated on. + The image-plane 2D grid of (y,x) coordinates grid which the image of the strong lens is generated on. """ grid = aa.Grid2D.uniform( shape_native=deflections.shape_native, pixel_scales=deflections.pixel_scales, + over_sample_size=1 ) - deflected_grid = grid - deflections + deflected_grid = aa.Grid2D( + values=grid - deflections, + mask=grid.mask, + over_sample_size=1, + over_sampled=grid - deflections + ) image = sum(map(lambda g: g.image_2d_from(grid=deflected_grid), galaxies)) @@ -105,10 +129,10 @@ def via_source_image_from(self, tracer : Tracer, grid : aa.type.Grid2DLike, sour Simulate an `Imaging` dataset from an input image of a source galaxy. This input image is on a uniform and regular 2D array, meaning it can simulate the source's irregular - and assymetric source galaxy morphological features. + and asymmetric source galaxy morphological features. The typical use case is inputting the image of an irregular galaxy in the source-plane (whose values are - on a uniform array) and using this function computing the lensed image of this source galaxy. + on a uniform array) and using this function to compute the lensed image of this source galaxy. The tracer is used to perform ray-tracing and generate the image of the strong lens galaxies (e.g. the lens light, lensed source light, etc) which is simulated. @@ -125,7 +149,7 @@ def via_source_image_from(self, tracer : Tracer, grid : aa.type.Grid2DLike, sour The tracer, which describes the ray-tracing and strong lens configuration used to simulate the imaging dataset. grid - The image-plane grid which the image of the strong lens is generated on. + The image-plane 2D grid of (y,x) coordinates grid which the image of the strong lens is generated on. source_image The image of the source-plane and source galaxy which is interpolated to compute the lensed image. """ diff --git a/autolens/interferometer/fit_interferometer.py b/autolens/interferometer/fit_interferometer.py index e0d624db5..7c9713186 100644 --- a/autolens/interferometer/fit_interferometer.py +++ b/autolens/interferometer/fit_interferometer.py @@ -45,7 +45,7 @@ def __init__( algebra solution, is computed). When performing a model-fit` via ` AnalysisInterferometer` object the `figure_of_merit` of - this `FitInterferometer` object is called and returned in the `log_likelihood_function`. + this object is called and returned in the `log_likelihood_function`. Parameters ---------- @@ -92,7 +92,7 @@ def profile_visibilities(self) -> aa.Visibilities: transform to the sum of light profile images. """ return self.tracer.visibilities_from( - grid=self.grids.uniform, transformer=self.dataset.transformer + grid=self.grids.lp, transformer=self.dataset.transformer ) @property @@ -164,7 +164,7 @@ def galaxy_model_image_dict(self) -> Dict[ag.Galaxy, np.ndarray]: data being fitted. """ galaxy_model_image_dict = self.tracer.galaxy_image_2d_dict_from( - grid=self.grids.uniform + grid=self.grids.lp ) galaxy_linear_obj_image_dict = self.galaxy_linear_obj_data_dict_from( @@ -186,7 +186,7 @@ def galaxy_model_visibilities_dict(self) -> Dict[ag.Galaxy, np.ndarray]: are solved for first via the inversion. """ galaxy_model_visibilities_dict = self.tracer.galaxy_visibilities_dict_from( - grid=self.grids.uniform, transformer=self.dataset.transformer + grid=self.grids.lp, transformer=self.dataset.transformer ) galaxy_linear_obj_visibilities_dict = self.galaxy_linear_obj_data_dict_from( diff --git a/autolens/interferometer/model/plotter_interface.py b/autolens/interferometer/model/plotter_interface.py index 3b7574605..96c6e014c 100644 --- a/autolens/interferometer/model/plotter_interface.py +++ b/autolens/interferometer/model/plotter_interface.py @@ -4,6 +4,8 @@ PlotterInterfaceInterferometer as AgPlotterInterfaceInterferometer, ) +from autogalaxy.interferometer.model.plotter_interface import fits_to_fits + from autolens.interferometer.fit_interferometer import FitInterferometer from autolens.interferometer.plot.fit_interferometer_plotters import ( FitInterferometerPlotter, @@ -19,37 +21,30 @@ class PlotterInterfaceInterferometer(PlotterInterface): def fit_interferometer( self, fit: FitInterferometer, - during_analysis: bool, - subfolders: str = "fit_dataset", ): """ Visualizes a `FitInterferometer` object, which fits an interferometer dataset. - Images are output to the `image` folder of the `image_path` in a subfolder called `fit`. When - used with a non-linear search the `image_path` is the output folder of the non-linear search. + Images are output to the `image` folder of the `image_path`. When used with a non-linear search the `image_path` + is the output folder of the non-linear search. - Visualization includes individual images of attributes of the `FitInterferometer` (e.g. the model data, - residual map) and a subplot of all `FitInterferometer`'s images on the same figure. + Visualization includes a subplot of individual images of attributes of the `FitInterferometer` (e.g. the model + data,residual map) and .fits files containing its attributes grouped together. - The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under the - [fit] header. + The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under + the `fit` and `fit_interferometer` headers. Parameters ---------- fit The maximum log likelihood `FitInterferometer` of the non-linear search which is used to plot the fit. - during_analysis - Whether visualization is performed during a non-linear search or once it is completed. - visuals_2d - An object containing attributes which may be plotted over the figure (e.g. the centres of mass and light - profiles). """ def should_plot(name): return plot_setting(section=["fit", "fit_interferometer"], name=name) - mat_plot_1d = self.mat_plot_1d_from(subfolders="") - mat_plot_2d = self.mat_plot_2d_from(subfolders="") + mat_plot_1d = self.mat_plot_1d_from() + mat_plot_2d = self.mat_plot_2d_from() fit_plotter = FitInterferometerPlotter( fit=fit, @@ -67,8 +62,8 @@ def should_plot(name): if should_plot("subplot_fit_real_space"): fit_plotter.subplot_fit_real_space() - mat_plot_1d = self.mat_plot_1d_from(subfolders=subfolders) - mat_plot_2d = self.mat_plot_2d_from(subfolders=subfolders) + mat_plot_1d = self.mat_plot_1d_from() + mat_plot_2d = self.mat_plot_2d_from() fit_plotter = FitInterferometerPlotter( fit=fit, @@ -77,89 +72,14 @@ def should_plot(name): mat_plot_2d=mat_plot_2d, ) - fit_plotter.figures_2d( - data=should_plot("data"), - noise_map=should_plot("noise_map"), - signal_to_noise_map=should_plot("signal_to_noise_map"), - model_data=should_plot("model_data"), - residual_map_real=should_plot("residual_map"), - chi_squared_map_real=should_plot("chi_squared_map"), - normalized_residual_map_real=should_plot("normalized_residual_map"), - residual_map_imag=should_plot("residual_map"), - chi_squared_map_imag=should_plot("chi_squared_map"), - normalized_residual_map_imag=should_plot("normalized_residual_map"), - dirty_image=should_plot("data"), - dirty_noise_map=should_plot("noise_map"), - dirty_signal_to_noise_map=should_plot("signal_to_noise_map"), - dirty_model_image=should_plot("model_data"), - dirty_residual_map=should_plot("residual_map"), - dirty_normalized_residual_map=should_plot("normalized_residual_map"), - dirty_chi_squared_map=should_plot("chi_squared_map"), - ) - if plot_setting(section="inversion", name="subplot_mappings"): fit_plotter.subplot_mappings_of_plane( plane_index=len(fit.tracer.planes) - 1 ) - if not during_analysis and should_plot("all_at_end_png"): - mat_plot_1d = self.mat_plot_1d_from(subfolders=path.join(subfolders, "end")) - mat_plot_2d = self.mat_plot_2d_from(subfolders=path.join(subfolders, "end")) - - fit_plotter = FitInterferometerPlotter( - fit=fit, - include_2d=self.include_2d, - mat_plot_1d=mat_plot_1d, - mat_plot_2d=mat_plot_2d, - ) - - fit_plotter.figures_2d( - data=True, - noise_map=True, - signal_to_noise_map=True, - model_data=True, - residual_map_real=True, - chi_squared_map_real=True, - normalized_residual_map_real=True, - residual_map_imag=True, - chi_squared_map_imag=True, - normalized_residual_map_imag=True, - dirty_image=True, - dirty_noise_map=True, - dirty_signal_to_noise_map=True, - dirty_model_image=True, - dirty_residual_map=True, - dirty_normalized_residual_map=True, - dirty_chi_squared_map=True, - ) - - plane_index_max = len(fit.tracer.planes) - 1 - - fit_plotter.figures_2d_of_planes( - plane_index=plane_index_max, plane_image=True - ) - - if not during_analysis and should_plot("all_at_end_fits"): - mat_plot_2d = self.mat_plot_2d_from( - subfolders=path.join("fit_dataset", "fits"), format="fits" - ) - - fit_plotter = FitInterferometerPlotter( - fit=fit, include_2d=self.include_2d, mat_plot_2d=mat_plot_2d - ) - - fit_plotter.figures_2d( - dirty_image=True, - dirty_noise_map=True, - dirty_signal_to_noise_map=True, - dirty_model_image=True, - dirty_residual_map=True, - dirty_normalized_residual_map=True, - dirty_chi_squared_map=True, - ) - - plane_index_max = len(fit.tracer.planes) - 1 - - fit_plotter.figures_2d_of_planes( - plane_index=plane_index_max, plane_image=True - ) + fits_to_fits( + should_plot=should_plot, + fit=fit, + mat_plot_2d=mat_plot_2d, + fit_plotter_cls=FitInterferometerPlotter, + ) diff --git a/autolens/interferometer/model/visualizer.py b/autolens/interferometer/model/visualizer.py index 290864c44..74cb75eb7 100644 --- a/autolens/interferometer/model/visualizer.py +++ b/autolens/interferometer/model/visualizer.py @@ -78,9 +78,6 @@ def visualize( 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). - during_analysis - If True the visualization is being performed midway through the non-linear search before it is finished, - which may change which images are output. """ fit = analysis.fit_from(instance=instance) @@ -101,7 +98,7 @@ def visualize( try: plotter_interface.fit_interferometer( - fit=fit, during_analysis=during_analysis + fit=fit, ) except exc.InversionException: pass @@ -109,17 +106,17 @@ def visualize( tracer = fit.tracer_linear_light_profiles_to_light_profiles plotter_interface.tracer( - tracer=tracer, grid=fit.grids.uniform, during_analysis=during_analysis + tracer=tracer, + grid=fit.grids.lp, ) plotter_interface.galaxies( galaxies=tracer.galaxies, - grid=fit.grids.uniform, - during_analysis=during_analysis, + grid=fit.grids.lp, ) if fit.inversion is not None: try: plotter_interface.inversion( - inversion=fit.inversion, during_analysis=during_analysis + inversion=fit.inversion, ) except IndexError: pass diff --git a/autolens/interferometer/plot/fit_interferometer_plotters.py b/autolens/interferometer/plot/fit_interferometer_plotters.py index f064dea82..07f35ee6c 100644 --- a/autolens/interferometer/plot/fit_interferometer_plotters.py +++ b/autolens/interferometer/plot/fit_interferometer_plotters.py @@ -120,7 +120,7 @@ def tracer_plotter(self) -> TracerPlotter: """ return TracerPlotter( tracer=self.tracer, - grid=self.fit.grids.uniform, + grid=self.fit.grids.lp, mat_plot_2d=self.mat_plot_2d, visuals_2d=self.visuals_2d, include_2d=self.include_2d, @@ -229,7 +229,12 @@ def subplot_mappings_of_plane( "total_mappings_pixels" ] - pix_indexes = inversion_plotter.inversion.brightest_pixel_list_from( + mapper = inversion_plotter.inversion.cls_list_from(cls=aa.AbstractMapper)[0] + mapper_valued = aa.MapperValued( + values=inversion_plotter.inversion.reconstruction_dict[mapper], + mapper=mapper, + ) + pix_indexes = mapper_valued.max_pixel_list_from( total_pixels=total_pixels, filter_neighbors=True ) @@ -370,7 +375,10 @@ def figures_2d_of_planes( self, plane_index: Optional[int] = None, plane_image: bool = False, + plane_noise_map: bool = False, + plane_signal_to_noise_map: bool = False, zoom_to_brightest: bool = True, + interpolate_to_uniform: bool = False, ): """ Plots images representing each individual `Plane` in the fit's `Tracer` in 2D, which are computed via the @@ -390,9 +398,20 @@ def figures_2d_of_planes( Whether to make a 2D plot (via `imshow`) of the image of a plane in its source-plane (e.g. unlensed). Depending on how the fit is performed, this could either be an image of light profiles of the reconstruction of an `Inversion`. + plane_noise_map + Whether to make a 2D plot of the noise-map of a plane in its source-plane, where the + noise map can only be computed when a pixelized source reconstruction is performed and they correspond to + the noise map in each reconstructed pixel as given by the inverse curvature matrix. + plane_signal_to_noise_map + Whether to make a 2D plot of the signal-to-noise map of a plane in its source-plane, + where the signal-to-noise map values can only be computed when a pixelized source reconstruction and they + are the ratio of reconstructed flux to error in each pixel. zoom_to_brightest For images not in the image-plane (e.g. the `plane_image`), whether to automatically zoom the plot to the brightest regions of the galaxies being plotted as opposed to the full extent of the grid. + interpolate_to_uniform + If `True`, the mapper's reconstruction is interpolated to a uniform grid before plotting, for example + meaning that an irregular Delaunay grid can be plotted as a uniform grid. """ if plane_image: if not self.tracer.planes[plane_index].has(cls=aa.Pixelization): @@ -408,6 +427,33 @@ def figures_2d_of_planes( pixelization_index=0, reconstruction=True, zoom_to_brightest=zoom_to_brightest, + interpolate_to_uniform=interpolate_to_uniform, + ) + + if plane_noise_map: + if self.tracer.planes[plane_index].has(cls=aa.Pixelization): + inversion_plotter = self.inversion_plotter_of_plane( + plane_index=plane_index + ) + + inversion_plotter.figures_2d_of_pixelization( + pixelization_index=0, + reconstruction_noise_map=True, + zoom_to_brightest=zoom_to_brightest, + interpolate_to_uniform=interpolate_to_uniform, + ) + + if plane_signal_to_noise_map: + if self.tracer.planes[plane_index].has(cls=aa.Pixelization): + inversion_plotter = self.inversion_plotter_of_plane( + plane_index=plane_index + ) + + inversion_plotter.figures_2d_of_pixelization( + pixelization_index=0, + signal_to_noise_map=True, + zoom_to_brightest=zoom_to_brightest, + interpolate_to_uniform=interpolate_to_uniform, ) def subplot_fit_real_space(self): diff --git a/autolens/interferometer/simulator.py b/autolens/interferometer/simulator.py index b7592abbb..91f46b57f 100644 --- a/autolens/interferometer/simulator.py +++ b/autolens/interferometer/simulator.py @@ -1,4 +1,7 @@ +from typing import List + import autoarray as aa +import autogalaxy as ag from autolens.lens.tracer import Tracer @@ -18,7 +21,7 @@ def via_tracer_from(self, tracer, grid): An arrays representing the effective exposure time of each pixel. psf: PSF An arrays describing the PSF the simulated image is blurred with. - add_poisson_noise: Bool + add_poisson_noise_to_data: Bool If `True` poisson noise_maps is simulated and added to the image, based on the total counts in each image pixel noise_seed: int @@ -49,13 +52,44 @@ def via_galaxies_from(self, galaxies, grid): return self.via_tracer_from(tracer=tracer, grid=grid) - def via_deflections_and_galaxies_from(self, deflections, galaxies): + def via_deflections_and_galaxies_from( + self, deflections: aa.VectorYX2D, galaxies: List[ag.Galaxy] + ) -> aa.Imaging: + """ + Simulate an `Imaging` dataset from an input deflection angle map and list of galaxies. + + The input deflection angle map ray-traces the image-plane coordinates from the image-plane to source-plane, + via the lens equation. + + This traced grid is then used to evaluate the light of the list of galaxies, which therefore simulate the + image of the strong lens. + + This function is used in situations where one has access to a deflection angle map which does not suit being + ray-traced using a `Tracer` object (e.g. deflection angles from a cosmological simulation of a galaxy). + + The steps of the `SimulatorImaging` simulation process (e.g. PSF convolution, noise addition) are + described in the `SimulatorImaging` `__init__` method docstring. + + Parameters + ---------- + galaxies + The galaxies used to create the tracer, which describes the ray-tracing and strong lens configuration + used to simulate the imaging dataset. + grid + The image-plane 2D grid of (y,x) coordinates grid which the image of the strong lens is generated on. + """ grid = aa.Grid2D.uniform( shape_native=deflections.shape_native, pixel_scales=deflections.pixel_scales, + over_sample_size=1, ) - deflected_grid = grid - deflections + deflected_grid = aa.Grid2D( + values=grid - deflections, + mask=grid.mask, + over_sample_size=1, + over_sampled=grid - deflections, + ) image = sum(map(lambda g: g.image_2d_from(grid=deflected_grid), galaxies)) diff --git a/autolens/lens/sensitivity.py b/autolens/lens/sensitivity.py index d528acd22..fccd6a6e1 100644 --- a/autolens/lens/sensitivity.py +++ b/autolens/lens/sensitivity.py @@ -360,6 +360,181 @@ def set_auto_filename( return False + def sensitivity_to_fits(self): + + log_likelihoods = self.result.figure_of_merit_array( + use_log_evidences=False, + remove_zeros=False, + ) + + mat_plot_2d = aplt.MatPlot2D( + output=aplt.Output( + path=self.mat_plot_2d.output.path, + filename="sensitivity_log_likelihood", + format="fits", + ) + ) + + mat_plot_2d.plot_array( + array=log_likelihoods, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(), + ) + + + try: + log_evidences = self.result.figure_of_merit_array( + use_log_evidences=True, + remove_zeros=False, + ) + + mat_plot_2d = aplt.MatPlot2D( + output=aplt.Output( + path=self.mat_plot_2d.output.path, + filename="sensitivity_log_evidence", + format="fits", + ) + ) + + mat_plot_2d.plot_array( + array=log_evidences, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(), + ) + + except TypeError: + pass + + def subplot_sensitivity(self): + log_likelihoods = self.result.figure_of_merit_array( + use_log_evidences=False, + remove_zeros=True, + ) + + try: + log_evidences = self.result.figure_of_merit_array( + use_log_evidences=True, + remove_zeros=True, + ) + except TypeError: + log_evidences = np.zeros_like(log_likelihoods) + + self.open_subplot_figure(number_subplots=8, subplot_shape=(2, 4)) + + plotter = aplt.Array2DPlotter( + array=self.data_subtracted, + mat_plot_2d=self.mat_plot_2d, + ) + + plotter.figure_2d() + + self.mat_plot_2d.plot_array( + array=log_evidences, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(title="Increase in Log Evidence"), + ) + + self.mat_plot_2d.plot_array( + array=log_likelihoods, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(title="Increase in Log Likelihood"), + ) + + above_threshold = np.where(log_likelihoods > 5.0, 1.0, 0.0) + + above_threshold = aa.Array2D(values=above_threshold, mask=log_likelihoods.mask) + + self.mat_plot_2d.plot_array( + array=above_threshold, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(title="Log Likelihood > 5.0"), + ) + + try: + log_evidences_base = self.result._array_2d_from( + self.result.log_evidences_base + ) + log_evidences_perturbed = self.result._array_2d_from( + self.result.log_evidences_perturbed + ) + + log_evidences_base_min = np.nanmin( + np.where(log_evidences_base == 0, np.nan, log_evidences_base) + ) + log_evidences_base_max = np.nanmax( + np.where(log_evidences_base == 0, np.nan, log_evidences_base) + ) + log_evidences_perturbed_min = np.nanmin( + np.where(log_evidences_perturbed == 0, np.nan, log_evidences_perturbed) + ) + log_evidences_perturbed_max = np.nanmax( + np.where(log_evidences_perturbed == 0, np.nan, log_evidences_perturbed) + ) + + self.mat_plot_2d.cmap.kwargs["vmin"] = np.min( + [log_evidences_base_min, log_evidences_perturbed_min] + ) + self.mat_plot_2d.cmap.kwargs["vmax"] = np.max( + [log_evidences_base_max, log_evidences_perturbed_max] + ) + + self.mat_plot_2d.plot_array( + array=log_evidences_base, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(title="Log Evidence Base"), + ) + + self.mat_plot_2d.plot_array( + array=log_evidences_perturbed, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(title="Log Evidence Perturb"), + ) + except TypeError: + pass + + log_likelihoods_base = self.result._array_2d_from( + self.result.log_likelihoods_base + ) + log_likelihoods_perturbed = self.result._array_2d_from( + self.result.log_likelihoods_perturbed + ) + + log_likelihoods_base_min = np.nanmin( + np.where(log_likelihoods_base == 0, np.nan, log_likelihoods_base) + ) + log_likelihoods_base_max = np.nanmax( + np.where(log_likelihoods_base == 0, np.nan, log_likelihoods_base) + ) + log_likelihoods_perturbed_min = np.nanmin( + np.where(log_likelihoods_perturbed == 0, np.nan, log_likelihoods_perturbed) + ) + log_likelihoods_perturbed_max = np.nanmax( + np.where(log_likelihoods_perturbed == 0, np.nan, log_likelihoods_perturbed) + ) + + self.mat_plot_2d.cmap.kwargs["vmin"] = np.min( + [log_likelihoods_base_min, log_likelihoods_perturbed_min] + ) + self.mat_plot_2d.cmap.kwargs["vmax"] = np.max( + [log_likelihoods_base_max, log_likelihoods_perturbed_max] + ) + + self.mat_plot_2d.plot_array( + array=log_likelihoods_base, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(title="Log Likelihood Base"), + ) + + self.mat_plot_2d.plot_array( + array=log_likelihoods_perturbed, + visuals_2d=self.visuals_2d, + auto_labels=AutoLabels(title="Log Likelihood Perturb"), + ) + + self.mat_plot_2d.output.subplot_to_figure(auto_filename="subplot_sensitivity") + + self.close_subplot_figure() + def subplot_figures_of_merit_grid( self, use_log_evidences: bool = True, diff --git a/autolens/lens/to_inversion.py b/autolens/lens/to_inversion.py index da50371d3..2e020775b 100644 --- a/autolens/lens/to_inversion.py +++ b/autolens/lens/to_inversion.py @@ -116,7 +116,7 @@ def traced_grid_2d_list_of_inversion(self) -> List[aa.type.Grid2DLike]: The traced grids of the inversion, which are cached for efficiency. """ return self.tracer.traced_grid_2d_list_from( - grid=self.dataset.grids.pixelization.over_sampler.over_sampled_grid + grid=self.dataset.grids.pixelization ) @cached_property @@ -157,32 +157,10 @@ def lp_linear_func_list_galaxy_dict( lp_linear_galaxy_dict_list = {} - perform_over_sampling = aa.perform_over_sampling_from( - grid=self.dataset.grids.uniform + traced_grids_of_planes_list = self.tracer.traced_grid_2d_list_from( + grid=self.dataset.grids.lp ) - if perform_over_sampling: - grid_input = self.dataset.grids.uniform.over_sampler.over_sampled_grid - grid_input.over_sampling = None - - traced_grids_of_planes_list = self.tracer.traced_grid_2d_list_from( - grid=grid_input - ) - - traced_grids_of_planes_list = [ - aa.Grid2DOverSampled( - grid=grid, - over_sampler=self.dataset.grids.uniform.over_sampler, - pixels_in_mask=self.dataset.mask.pixels_in_mask, - ) - for grid in traced_grids_of_planes_list - ] - - else: - traced_grids_of_planes_list = self.tracer.traced_grid_2d_list_from( - grid=self.dataset.grids.uniform - ) - if self.dataset.grids.blurring is not None: traced_blurring_grids_of_planes_list = self.tracer.traced_grid_2d_list_from( grid=self.dataset.grids.blurring @@ -194,7 +172,7 @@ def lp_linear_func_list_galaxy_dict( for plane_index, galaxies in enumerate(self.planes): grids = aa.GridsInterface( - uniform=traced_grids_of_planes_list[plane_index], + lp=traced_grids_of_planes_list[plane_index], blurring=traced_blurring_grids_of_planes_list[plane_index], ) diff --git a/autolens/lens/tracer.py b/autolens/lens/tracer.py index 5b4f93114..3e19bf0bb 100644 --- a/autolens/lens/tracer.py +++ b/autolens/lens/tracer.py @@ -14,84 +14,6 @@ from autolens.lens import tracer_util -def over_sample(func): - """ - Homogenize the inputs and outputs of functions that take 1D or 2D grids of coordinates and return a 1D ndarray - which is converted to an `Array2D`, `ArrayIrregular` or `Array1D` object. - - Parameters - ---------- - func - A function which computes a set of values from a 1D or 2D grid of coordinates. - - Returns - ------- - A function that has its outputs homogenized to `Array2D`, `ArrayIrregular` or `Array1D` objects. - """ - - @wraps(func) - def wrapper( - obj: object, - grid: Union[np.ndarray, aa.Grid2D, aa.Grid2DIrregular], - *args, - **kwargs, - ) -> Union[np.ndarray, aa.Array2D, aa.ArrayIrregular, List]: - """ - - Parameters - ---------- - obj - An object whose function uses grid_like inputs to compute quantities at every coordinate on the grid. - grid - A grid_like object of coordinates on which the function values are evaluated. - - Returns - ------- - The function values evaluated on the grid with the same structure as the input grid_like object. - """ - - grid_input = grid - - over_sampler_used = False - - if isinstance(grid, aa.Grid2D): - if ( - grid.over_sampling_non_uniform is not None - and obj.upper_plane_index_with_light_profile > 0 - ): - over_sampler = grid.over_sampling_non_uniform.over_sampler_from( - mask=grid.mask - ) - grid_input = over_sampler.over_sampled_grid - grid_input.over_sampling = None - over_sampler_used = True - - elif isinstance(grid.over_sampling, aa.OverSamplingUniform): - over_sampler = grid.over_sampler - grid_input = over_sampler.over_sampled_grid - grid_input.over_sampling = None - over_sampler_used = True - - result = func(obj, grid_input, *args, **kwargs) - - if over_sampler_used: - if isinstance(result, list): - return [ - over_sampler.binned_array_2d_from(array=result_i) - for result_i in result - ] - elif isinstance(result, dict): - return { - key: over_sampler.binned_array_2d_from(array=result_i) - for key, result_i in result.items() - } - return over_sampler.binned_array_2d_from(array=result) - - return result - - return wrapper - - class Tracer(ABC, ag.OperateImageGalaxies, ag.OperateDeflections): def __init__( self, @@ -341,13 +263,37 @@ def traced_grid_2d_list_from( planes. """ - return tracer_util.traced_grid_2d_list_from( + grid_2d_list = tracer_util.traced_grid_2d_list_from( planes=self.planes, grid=grid, cosmology=self.cosmology, plane_index_limit=plane_index_limit, ) + if isinstance(grid, aa.Grid2D): + grid_2d_over_sampled_list = tracer_util.traced_grid_2d_list_from( + planes=self.planes, + grid=grid.over_sampled, + cosmology=self.cosmology, + plane_index_limit=plane_index_limit, + ) + + grid_2d_new_list = [] + + for i in range(len(grid_2d_list)): + grid_2d_new = aa.Grid2D( + values=grid_2d_list[i], + mask=grid.mask, + over_sampled=grid_2d_over_sampled_list[i], + over_sample_size=grid.over_sample_size, + ) + + grid_2d_new_list.append(grid_2d_new) + + return grid_2d_new_list + + return grid_2d_list + def grid_2d_at_redshift_from( self, grid: aa.type.Grid2DLike, redshift: float ) -> aa.type.Grid2DLike: @@ -422,7 +368,6 @@ def upper_plane_index_with_light_profile(self) -> int: ] ) - @over_sample def image_2d_list_from( self, grid: aa.type.Grid2DLike, @@ -470,12 +415,6 @@ def image_2d_list_from( therefore is used to pass the `operated_only` input to these methods. """ - if hasattr(grid, "over_sampling"): - if isinstance(grid.over_sampling, aa.OverSamplingIterate): - return self.image_2d_list_over_sampled_from( - grid=grid, operated_only=operated_only - ) - traced_grid_list = self.traced_grid_2d_list_from( grid=grid, plane_index_limit=self.upper_plane_index_with_light_profile ) @@ -485,18 +424,18 @@ def image_2d_list_from( for plane_index in range(len(traced_grid_list)): galaxies = self.planes[plane_index] - image_2d_list.append( - sum( - [ - galaxy.image_2d_from( - grid=traced_grid_list[plane_index], - operated_only=operated_only, - ) - for galaxy in galaxies - ] - ) + image_2d = sum( + [ + galaxy.image_2d_from( + grid=traced_grid_list[plane_index], + operated_only=operated_only, + ) + for galaxy in galaxies + ] ) + image_2d_list.append(image_2d) + if self.upper_plane_index_with_light_profile < self.total_planes - 1: if isinstance(grid, aa.Grid2D): image_2d = aa.Array2D( @@ -512,145 +451,6 @@ def image_2d_list_from( return image_2d_list - @over_sample - def image_2d_of_plane_from( - self, - grid: aa.type.Grid2DLike, - plane_index: int, - operated_only: Optional[bool] = None, - ) -> aa.Array2D: - """ - Returns a 2D image of an input plane from a 2D grid of Cartesian (y,x) coordinates. - - The image of the plane is computed by ray-tracing the grid using te mass profiles of all galaxies before the - input plane and then summing the images of all galaxies in that plane. If a plane has no galaxies, or if the - galaxies in a plane, has no light profiles, a numpy array of zeros is returned. - - For example, if the tracer's planes contain galaxies at redshifts z=0.5, z=1.0 and z=2.0, and the galaxies - at redshifts z=0.5 and z=1.0 have light and mass profiles, the returned image for `plane_index=1` will be the - image of the galaxy at z=1.0, where the image at redshift z=1.0 will include the lensing effects of the - galaxies at z=0.5. The image at redshift z=2.0 will be ignored. - - The `plane_index` input specifies which plane the image os returned for. This calculation saves computational - time compared to `image_2d_list_from` when only the image of a specific plane is needed. It is also used to - perform iterative over-sampling calculations. - - The images output by this function do not include instrument operations, such as PSF convolution (for imaging - data) or a Fourier transform (for interferometer data). - - Inherited methods in the `autogalaxy.operate.image` package can apply these operations to the images. - These functions may have the `operated_only` input passed to them, which is why this function includes - the `operated_only` input. - - If the `operated_only` input is included, the function omits light profiles which are parents of - the `LightProfileOperated` object, which signifies that the light profile represents emission that has - already had the instrument operations (e.g. PSF convolution, a Fourier transform) applied to it and therefore - that operation is not performed again. - - See the `autogalaxy.profiles.light` package for details of how images are computed from a light - profile. - - Parameters - ---------- - grid - The 2D (y, x) coordinates where values of the image are evaluated. - plane_index - The plane index of the plane the image is computed. - operated_only - The returned list from this function contains all light profile images, and they are never operated on - (e.g. via the imaging PSF). However, inherited methods in the `autogalaxy.operate.image` package can - apply these operations to the images, which may have the `operated_only` input passed to them. This input - therefore is used to pass the `operated_only` input to these methods. - """ - - if not self.planes[plane_index].has(cls=ag.LightProfile): - if isinstance(grid, aa.Grid2D): - return aa.Array2D(values=np.zeros(shape=grid.shape[0]), mask=grid.mask) - else: - return aa.ArrayIrregular(values=np.zeros(grid.shape[0])) - - traced_grid_list = self.traced_grid_2d_list_from( - grid=grid, plane_index_limit=plane_index - ) - - return sum( - [ - galaxy.image_2d_from( - grid=traced_grid_list[plane_index], - operated_only=operated_only, - ) - for galaxy in self.planes[plane_index] - ] - ) - - def image_2d_list_over_sampled_from( - self, - grid: aa.type.Grid2DLike, - operated_only: Optional[bool] = None, - ) -> List[aa.Array2D]: - """ - Returns a list of the 2D images for each plane from a 2D grid of Cartesian (y,x) coordinates where adaptive - and iterative over-sampling is used to compute the image. - - The image of each plane is computed by iteratively ray-tracing the grid using the mass profiles of each - galaxies and then summing the images of all galaxies in that plane, until a threshold level of accuracy - defined by the over-sampling grid is met. If a plane has no galaxies, or if the galaxies in a plane - has no light profiles, a numpy array of zeros is returned. - - For example, if the tracer's planes contain galaxies at redshifts z=0.5, z=1.0 and z=2.0, and the galaxies - at redshifts z=0.5 and z=1.0 have light and mass profiles, the returned list of images will be the image of the - galaxies at z=0.5 and z=1.0, where the image at redshift z=1.0 will include the lensing effects of the galaxies - at z=0.5. The image at redshift z=2.0 will be a numpy array of zeros. - - The implementation of this function has to wrap a function in the iterative over sampler which performs the - iterative over-sampling calculation. This requires a function to be defined internally in this function - which meets the requirements of the over-sample. - - The images output by this function do not include instrument operations, such as PSF convolution (for imaging - data) or a Fourier transform (for interferometer data). - - Inherited methods in the `autogalaxy.operate.image` package can apply these operations to the images. - These functions may have the `operated_only` input passed to them, which is why this function includes - the `operated_only` input. - - If the `operated_only` input is included, the function omits light profiles which are parents of - the `LightProfileOperated` object, which signifies that the light profile represents emission that has - already had the instrument operations (e.g. PSF convolution, a Fourier transform) applied to it and therefore - that operation is not performed again. - - See the `autogalaxy.profiles.light` package for details of how images are computed from a light - profile. - - Parameters - ---------- - grid - The 2D (y, x) coordinates where values of the image are evaluated, which has an iterative over-sampling - applied to it. - operated_only - The returned list from this function contains all light profile images, and they are never operated on - (e.g. via the imaging PSF). However, inherited methods in the `autogalaxy.operate.image` package can - apply these operations to the images, which may have the `operated_only` input passed to them. This input - therefore is used to pass the `operated_only` input to these methods. - """ - - image_2d_list = [] - - for plane_index in range(len(self.planes)): - - def func(obj, grid, *args, **kwargs): - return self.image_2d_of_plane_from( - grid=grid, - operated_only=operated_only, - plane_index=plane_index, - ) - - image_2d = grid.over_sampler.array_via_func_from(func=func, obj=self) - - image_2d_list.append(image_2d) - - return image_2d_list - - @over_sample @aa.grid_dec.to_array @aa.profile_func def image_2d_from( @@ -744,27 +544,20 @@ def image_2d_via_input_plane_image_from( pixel_scales=plane_image.pixel_scales, ) - grid_input = grid - - if isinstance(grid, aa.Grid2D): - if isinstance(grid.over_sampling, aa.OverSamplingUniform): - grid_input = grid.over_sampler.over_sampled_grid - traced_grid = self.traced_grid_2d_list_from( - grid=grid_input, plane_index_limit=plane_index + grid=grid, plane_index_limit=plane_index )[plane_index] image = griddata( points=plane_grid, values=plane_image, - xi=traced_grid, + xi=traced_grid.over_sampled, fill_value=0.0, method="linear", ) if isinstance(grid, aa.Grid2D): - if isinstance(grid.over_sampling, aa.OverSamplingUniform): - image = grid.over_sampler.binned_array_2d_from(array=image) + image = grid.over_sampler.binned_array_2d_from(array=image) if include_other_planes: image_list = self.image_2d_list_from(grid=grid, operated_only=False) @@ -781,7 +574,6 @@ def image_2d_via_input_plane_image_from( mask=grid.mask, ) - @over_sample def galaxy_image_2d_dict_from( self, grid: aa.type.Grid2DLike, operated_only: Optional[bool] = None ) -> Dict[ag.Galaxy, np.ndarray]: diff --git a/autolens/plot/get_visuals/two_d.py b/autolens/plot/get_visuals/two_d.py index 354fdf9bd..9b1d40b54 100644 --- a/autolens/plot/get_visuals/two_d.py +++ b/autolens/plot/get_visuals/two_d.py @@ -169,7 +169,7 @@ def via_fit_imaging_from(self, fit: FitImaging) -> aplt.Visuals2D: visuals_2d_via_mask = self.via_mask_from(mask=fit.mask) visuals_2d_via_tracer = self.via_tracer_from( - tracer=fit.tracer, grid=fit.grids.uniform, plane_index=0 + tracer=fit.tracer, grid=fit.grids.lp, plane_index=0 ) return visuals_2d_via_mask + visuals_2d_via_tracer diff --git a/autolens/point/dataset.py b/autolens/point/dataset.py index f4aa76f85..75e084fb1 100644 --- a/autolens/point/dataset.py +++ b/autolens/point/dataset.py @@ -83,3 +83,11 @@ def info(self) -> str: info += f"fluxes : {self.fluxes}\n" info += f"fluxes_noise_map : {self.fluxes_noise_map}\n" return info + + def extent_from(self, buffer: float = 0.1): + y_max = max(self.positions[:, 0]) + buffer + y_min = min(self.positions[:, 0]) - buffer + x_max = max(self.positions[:, 1]) + buffer + x_min = min(self.positions[:, 1]) - buffer + + return [y_min, y_max, x_min, x_max] diff --git a/autolens/point/fit/abstract.py b/autolens/point/fit/abstract.py new file mode 100644 index 000000000..ade14c149 --- /dev/null +++ b/autolens/point/fit/abstract.py @@ -0,0 +1,176 @@ +from abc import ABC +from functools import partial +from typing import Optional, Tuple + +import autoarray as aa +import autogalaxy as ag + +from autolens.point.solver import PointSolver +from autolens.lens.tracer import Tracer + +from autolens import exc + + +class AbstractFitPoint(aa.AbstractFit, ABC): + def __init__( + self, + name: str, + data: aa.Grid2DIrregular, + noise_map: aa.ArrayIrregular, + tracer: Tracer, + solver: PointSolver, + profile: Optional[ag.ps.Point] = None, + ): + """ + Abstract class to fit a point source dataset using a `Tracer` object, including different components + of the point source data like positions, fluxes and time delays. + + All sub-classes which fit specifc components of the point source data (e.g. positions, fluxes, time delays) + inherit from this class, to provide them with general point-source functionality used in most + calculations, for example the deflection angles and magnification of the point source. + + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. + + This fit object is used in the `FitPointDataset` to perform fitting of a `PointDataset`, which may also fit + the point dataset poisitions, fluxes and / or time delays. + + When performing a model-fit via an `AnalysisPoint` object the `figure_of_merit` of the child class + is called and returned in the `log_likelihood_function`. + + Parameters + ---------- + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. + """ + + self.name = name + self._data = data + self._noise_map = noise_map + self.tracer = tracer + self.solver = solver + + self.profile = profile or tracer.extract_profile(profile_name=name) + + if self.profile is None: + raise exc.PointExtractionException( + f"For the point-source named {name} there was no matching point source profile " + f"in the tracer (make sure your tracer's point source name is the same the dataset name." + ) + + @property + def data(self): + return self._data + + @property + def noise_map(self): + return self._noise_map + + @property + def deflections_func(self): + """ + Returns the deflection angle function, which for example given input image-plane positions computes their + deflection angles. + + The use of this specific `deflections_func` property is not typical, using the `partial` function to wrap + a deflections method of the tracer. This is essentially a trick so that, depending on whether multi-plane + ray-tracing is to be performed, a different deflection function is used. This function is then + used in `magnifications_at_positions` to compute the magnification of the point source account for + multi-plane ray-tracing. + + For multi-plane ray-tracing with more than 2 planes, the deflection function determines the index of the + plane with the last mass profile such that the deflection function does not perform unnecessary computations + beyond this plane. + + TODO: Simplify this property and calculation of the deflection angles, as this property is confusing. + TODO: This could be done by allowing for the Hessian to receive planes as input. + """ + + if len(self.tracer.planes) > 2: + upper_plane_index = self.tracer.extract_plane_index_of_profile( + profile_name=self.name + ) + + return partial( + self.tracer.deflections_between_planes_from, + plane_i=0, + plane_j=upper_plane_index, + ) + + return self.tracer.deflections_yx_2d_from + + @property + def magnifications_at_positions(self) -> aa.ArrayIrregular: + """ + The magnification of every observed position in the image-plane, which is computed from the tracer's deflection + angle map via the Hessian. + + These magnifications are used for two purposes: + + 1) For a source-plane chi-squared calculation, the residuals are multiplied by the magnification to account for + how the noise in the image-plane positions is magnified to the source-plane, thus defining a + better chi-squared. + + 2) For fitting the fluxes of point sources, the magnification is used to scale the flux of the point source + in the source-plane to the image-plane, thus computing the model image-plane fluxes. + """ + return abs( + self.tracer.magnification_2d_via_hessian_from( + grid=self.positions, deflections_func=self.deflections_func + ) + ) + + @property + def source_plane_coordinate(self) -> Tuple[float, float]: + """ + Returns the centre of the point-source in the source-plane, which is used when computing the model + image-plane positions from the tracer. + + Returns + ------- + The (y,x) arc-second coordinates of the point-source in the source-plane. + """ + return self.profile.centre + + @property + def source_plane_index(self) -> int: + """ + Returns the integer plane index containing the point source galaxy, which is used when computing the deflection + angles of image-plane positions from the tracer. + + This index is used to ensure that if multi-plane tracing is used when solving the model image-plane positions, + the correct source-plane is used to compute the model positions whilst accounting for multi-plane lensing. + + Returns + ------- + The index of the plane containing the point-source galaxy. + """ + return self.tracer.extract_plane_index_of_profile(profile_name=self.name) + + @property + def source_plane_redshift(self) -> float: + """ + Returns the redshift of the plane containing the point source galaxy, which is used when computing the + deflection angles of image-plane positions from the tracer. + + This redshift is used to ensure that if multi-plane tracing is used when solving the model image-plane + positions, the correct source-plane is used to compute the model positions whilst accounting for multi-plane + lensing. + + Returns + ------- + The redshift of the plane containing the point-source galaxy. + """ + return self.tracer.planes[self.source_plane_index].redshift diff --git a/autolens/point/fit/dataset.py b/autolens/point/fit/dataset.py index 24c744fa1..56b50f3d3 100644 --- a/autolens/point/fit/dataset.py +++ b/autolens/point/fit/dataset.py @@ -26,6 +26,58 @@ def __init__( fit_positions_cls=FitPositionsImagePair, run_time_dict: Optional[Dict] = None, ): + """ + Fits a point source dataset using a `Tracer` object, where the following components of the point source data + may be fitted: + + - The positions of the point source in the image-plane, where the chi-squared could be defined as an image-plane + or source-plane chi-squared. + + - The fluxes of the point source, which use the magnification of the point source to compute the fluxes in the + image-plane. + + - The time delays of the point source (NOT IMPLEMENTED YET). + + The fit may use one or combinations of the above components to compute the log likelihood, depending on what + components are available in the point source dataset and the model point source profiles input. For example: + + - The `ps.Point` object has a `centre` but does not have a flux, so the fluxes are not fitted, meaning only + positions are fitted. + + - The `ps.PointFlux` object has a `centre` and a flux, therefore both the positions and fluxes are fitted. + + The fit performs the following steps: + + 1) Fit the positions of the point source dataset using the input `fit_positions_cls` object, which could be an + image-plane or source-plane chi-squared. + + 2) Fit the fluxes of the point source dataset using the `FitFluxes` object, where the object type may be + extended in the future to support different types of point source profiles. + + 3) Time delays are not currently supported but this API will extend to include time delays in the future. + + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. + + When performing a `model-fit`via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. + + Parameters + ---------- + dataset + The point source dataset which is fitted. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + fit_positions_cls + The class used to fit the positions of the point source dataset, which could be an image-plane or + source-plane chi-squared. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. + """ self.dataset = dataset self.tracer = tracer self.solver = solver @@ -65,6 +117,10 @@ def model_obj(self): @property def log_likelihood(self) -> float: + """ + Returns the overall `log_likelihood` of the point source dataset, which is the sum of the log likelihoods of + each individual component of the point source dataset that is fitted (e.g. positions, fluxes, time delays). + """ log_likelihood_positions = ( self.positions.log_likelihood if self.positions is not None else 0.0 ) @@ -74,4 +130,8 @@ def log_likelihood(self) -> float: @property def figure_of_merit(self) -> float: + """ + The `figure_of_merit` of the point source dataset, which is the value the `AnalysisPoint` object calls to + perform a model-fit. + """ return self.log_likelihood diff --git a/autolens/point/fit/fluxes.py b/autolens/point/fit/fluxes.py index 785cac506..788368c1a 100644 --- a/autolens/point/fit/fluxes.py +++ b/autolens/point/fit/fluxes.py @@ -1,15 +1,15 @@ -from functools import partial from typing import Optional import autoarray as aa import autogalaxy as ag +from autolens.point.fit.abstract import AbstractFitPoint from autolens.lens.tracer import Tracer from autolens import exc -class FitFluxes(aa.AbstractFit): +class FitFluxes(AbstractFitPoint): def __init__( self, name: str, @@ -19,76 +19,83 @@ def __init__( tracer: Tracer, profile: Optional[ag.ps.Point] = None, ): - self.name = name - self._data = data - self._noise_map = noise_map - self.positions = positions - self.tracer = tracer + """ + Fits the fluxes of a a point source dataset using a `Tracer` object, where every model flux of the point-source + is compared with its observed flux. - self.profile = ( - tracer.extract_profile(profile_name=name) if profile is None else profile - ) + The fit performs the following steps: - if self.profile is None: - raise exc.PointExtractionException( - f"For the point-source named {name} there was no matching point source profile " - f"in the tracer (make sure your tracer's point source name is the same the dataset name." - ) + 1) Compute the magnification of the input image-plane `positions` via the Hessian of the tracer's deflection angles. - elif not hasattr(self.profile, "flux"): - raise exc.PointExtractionException( - f"For the point-source named {name} the extracted point source was the " - f"class {self.profile.__class__.__name__} and therefore does " - f"not contain a flux component." - ) + 2) Determine the image-plane model fluxes by multiplying the source-plane flux with these magnifications. - @property - def data(self): - return self._data + 3) Subtract the observed fluxes from the model fluxes to compute the residual fluxes, called the `residual_map`. - @property - def noise_map(self): - return self._noise_map + 4) Compute the chi-squared of each flux as the square of the residual divided by the RMS noise-map value. - @property - def deflections_func(self): - """ - Returns the defleciton function, which given the image-plane positions computes their deflection angles. + 5) Sum the chi-squared values to compute the overall log likelihood of the fit. - For multi-plane ray-tracing with more than 2 planes, the deflection function determines the index of the - plane with the last mass profile such that the deflection function does not perform unnecessary computations - beyond this plane. - """ + Flux based fitting in the source code always inputs the observed positions of the point dataset as the input + `positions`, but the following changes could be implemented and used in the future: - if len(self.tracer.planes) > 2: - upper_plane_index = self.tracer.extract_plane_index_of_profile( - profile_name=self.name - ) + - Use the model positions instead of the observed positions to compute the fluxes, which would therefore + require the centre of the point source in the source-plane to be used and for the `PointSolver` to determine + the image-plane positions via ray-tracing triangles to and from the source-plane. This would require + care in pairing model positions to observed positions where fluxes are computed. - return partial( - self.tracer.deflections_between_planes_from, - plane_i=0, - plane_j=upper_plane_index, - ) + - The "size" of the point-source is not currently supported, however the `ShapeSolver` implemented in the + source code does allow for magnifications to be computed based on point sources with a shape (e.g. a + `Circle` where its radius is a free parameter). - return self.tracer.deflections_yx_2d_from + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. - @property - def magnifications(self): - """ - The magnification of every position in the image-plane, which is computed from the tracer's deflection - angle map via the Hessian. + This fit object is used in the `FitPointDataset` to perform position based fitting of a `PointDataset`, + which may also fit other components of the point dataset like fluxes or time delays. + + When performing a `model-fit` via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. + + Parameters + ---------- + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + positions + The positions of the point source in the image-plane where the fluxes are calculated. These are currently + always the observed positions of the point source in the source code, but other positions, like the + model positions, could be used in the future. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. """ - return abs( - self.tracer.magnification_2d_via_hessian_from( - grid=self.positions, deflections_func=self.deflections_func - ) + self.positions = positions + + super().__init__( + name=name, + data=data, + noise_map=noise_map, + tracer=tracer, + solver=None, + profile=profile, ) + if not hasattr(self.profile, "flux"): + raise exc.PointExtractionException( + f"For the point-source named {name} the extracted point source was the " + f"class {self.profile.__class__.__name__} and therefore does " + f"not contain a flux component." + ) + @property def model_data(self): """ - The model-fluxes of the tracer at each of the image-plane positions. + The model-fluxes of the tracer at each of the input image-plane positions. Only point sources which are a `PointFlux` type, and therefore which include a model parameter for its flux, are used. @@ -96,7 +103,7 @@ def model_data(self): return aa.ArrayIrregular( values=[ magnification * self.profile.flux - for magnification in self.magnifications + for magnification in self.magnifications_at_positions ] ) @@ -107,8 +114,8 @@ def model_fluxes(self) -> aa.ArrayIrregular: @property def residual_map(self) -> aa.ArrayIrregular: """ - Returns the residual map, over riding the parent method so that the result is converted to a - `ArrayIrregular` object. + Returns the difference between the observed and model fluxes of the point source, which is the residual flux + of a point source flux fit. """ residual_map = super().residual_map @@ -117,7 +124,8 @@ def residual_map(self) -> aa.ArrayIrregular: @property def chi_squared(self) -> float: """ - Returns the chi-squared terms of the model data's fit to an dataset, by summing the chi-squared-map. + Returns the chi-squared of the fit of the point source fluxes, which is the residual flux values divided by the + RMS noise-map values squared. """ return ag.util.fit.chi_squared_from( chi_squared_map=self.chi_squared_map, diff --git a/autolens/point/fit/positions/abstract.py b/autolens/point/fit/positions/abstract.py index e99e5ee6e..5b3d176a3 100644 --- a/autolens/point/fit/positions/abstract.py +++ b/autolens/point/fit/positions/abstract.py @@ -1,16 +1,15 @@ from abc import ABC -from typing import Optional, Tuple +from typing import Optional import autoarray as aa import autogalaxy as ag +from autolens.point.fit.abstract import AbstractFitPoint from autolens.point.solver import PointSolver from autolens.lens.tracer import Tracer -from autolens import exc - -class AbstractFitPositions(aa.AbstractFit, ABC): +class AbstractFitPositions(AbstractFitPoint, ABC): def __init__( self, name: str, @@ -21,80 +20,53 @@ def __init__( profile: Optional[ag.ps.Point] = None, ): """ - A lens position fitter, which takes a set of positions (e.g. from a plane in the tracer) and computes \ - their maximum separation, such that points which tracer closer to one another have a higher log_likelihood. + Abstract class to fit the positions of a a point source dataset using a `Tracer` object, where the specific + implementation of the chi-squared is defined in the sub-class. - Parameters - ---------- - data : Grid2DIrregular - The (y,x) arc-second coordinates of positions which the maximum distance and log_likelihood is computed using. - noise_value - The noise-value assumed when computing the log likelihood. - """ + The fit performs the following steps: - self.name = name - self._data = data - self._noise_map = noise_map - self.tracer = tracer - self.solver = solver + 1) Determine the source-plane centre of the point source, which could be a free model parameter or computed + as the barycenter of ray-traced positions in the source-plane, using name pairing (see below). - self.profile = ( - tracer.extract_profile(profile_name=name) if profile is None else profile - ) + 2) Using the sub-class specific chi-squared, compute the residuals of each image-plane position, chi-squared + and overall log likelihood of the fit. - if self.profile is None: - raise exc.PointExtractionException( - f"For the point-source named {name} there was no matching point source profile " - f"in the tracer (make sure your tracer's point source name is the same the dataset name." - ) + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. - @property - def data(self): - return self._data - - @property - def noise_map(self): - return self._noise_map - - @property - def source_plane_coordinate(self) -> Tuple[float, float]: - """ - Returns the centre of the point-source in the source-plane, which is used when computing the model - image-plane positions from the tracer. + This fit object is used in the `FitPointDataset` to perform position based fitting of a `PointDataset`, + which may also fit other components of the point dataset like fluxes or time delays. - Returns - ------- - The (y,x) arc-second coordinates of the point-source in the source-plane. - """ - return self.profile.centre + When performing a `model-fit`via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. - @property - def source_plane_index(self) -> int: + Parameters + ---------- + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. """ - Returns the integer plane index containing the point source galaxy, which is used when computing the model - image-plane positions from the tracer. - - This index is used to ensure that if multi-plane tracing is used when solving the model image-plane positions, - the correct source-plane is used to compute the model positions whilst accounting for multi-plane lensing. - Returns - ------- - The index of the plane containing the point-source galaxy. - """ - return self.tracer.extract_plane_index_of_profile(profile_name=self.name) + super().__init__( + name=name, + data=data, + noise_map=noise_map, + tracer=tracer, + solver=solver, + profile=profile, + ) @property - def source_plane_redshift(self) -> float: - """ - Returns the redshift of the plane containing the point source galaxy, which is used when computing the model - image-plane positions from the tracer. - - This redshift is used to ensure that if multi-plane tracing is used when solving the model image-plane - positions, the correct source-plane is used to compute the model positions whilst accounting for multi-plane - lensing. - - Returns - ------- - The redshift of the plane containing the point-source galaxy. - """ - return self.tracer.planes[self.source_plane_index].redshift + def positions(self): + return self.data diff --git a/autolens/point/fit/positions/image/abstract.py b/autolens/point/fit/positions/image/abstract.py index 4a79bc5d3..5ab480dc0 100644 --- a/autolens/point/fit/positions/image/abstract.py +++ b/autolens/point/fit/positions/image/abstract.py @@ -1,4 +1,5 @@ from abc import ABC +import numpy as np from typing import Optional import autoarray as aa @@ -20,15 +21,46 @@ def __init__( profile: Optional[ag.ps.Point] = None, ): """ - A lens position fitter, which takes a set of positions (e.g. from a plane in the tracer) and computes \ - their maximum separation, such that points which tracer closer to one another have a higher log_likelihood. + Abstract class to fit the positions of a point source dataset using a `Tracer` object with an image-plane + chi-squared, where the specific implementation of the image-plane chi-squared is defined in the sub-class. + + The fit performs the following steps: + + 1) Determine the source-plane centre of the point source, which could be a free model parameter or computed + as the barycenter of ray-traced positions in the source-plane, using name pairing (see below). + + 2) Determine the image-plane model positions using the `PointSolver` and the source-plane centre of the point + source (e.g. ray tracing triangles to and from the image and source planes), including accounting for + multi-plane ray-tracing. + + 3) Using the sub-class specific chi-squared, compute the residuals of each image-plane position, chi-squared + and overall log likelihood of the fit. + + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. + + This fit object is used in the `FitPointDataset` to perform position based fitting of a `PointDataset`, + which may also fit other components of the point dataset like fluxes or time delays. + + When performing a `model-fit`via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. Parameters ---------- - data : Grid2DIrregular - The (y,x) arc-second coordinates of positions which the maximum distance and log_likelihood is computed using. - noise_value - The noise-value assumed when computing the log likelihood. + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. """ super().__init__( @@ -41,7 +73,24 @@ def __init__( ) @staticmethod - def square_distance(coord1, coord2): + def square_distance( + coord1: np.array, + coord2: np.array, + ) -> float: + """ + Calculate the square distance between two points. + + Parameters + ---------- + coord1 + The first point to calculate the distance between. + coord2 + The second point to calculate the distance between. + + Returns + ------- + The square distance between the two points + """ return (coord1[0] - coord2[0]) ** 2 + (coord1[1] - coord2[1]) ** 2 @property diff --git a/autolens/point/fit/positions/image/pair.py b/autolens/point/fit/positions/image/pair.py index 4447a7604..e7af82c2b 100644 --- a/autolens/point/fit/positions/image/pair.py +++ b/autolens/point/fit/positions/image/pair.py @@ -8,15 +8,62 @@ class FitPositionsImagePair(AbstractFitPositionsImagePair): """ - A lens position fitter, which takes a set of positions (e.g. from a plane in the tracer) and computes \ - their maximum separation, such that points which tracer closer to one another have a higher log_likelihood. + Fits the positions of a point source dataset using a `Tracer` object with an image-plane chi-squared where every + model position of the point-source is paired with its closest observed position, without allowing for repeated + pairings of the same observed position to model positions. + + By not allowing for repeated pairings, this can produce behaviour such as a model position not being paired to + its closest observed position, but instead being paired to a further observed position, if doing so + means that the overall distances of pairings are reduced. + + THIS FIT CURRENTLY GIVES UNRELIABLE RESULTS, BECAUSE IT GOES TO SOLUTIONS WHERE THE NUMBER OF MODEL POSITIONS + IS BELOW THE NUMBER OF DATA POSITIONS, REDUCING THE CHI-SQUARED TO LOW VALUES. PYAUTOLENS SHOULD BE UPDATED TO + PENALIZE THIS BEHAVIOUR BEFORE THIS FIT CAN BE USED. THIS REISDUAL MAP PROPERTY MAY ALSO NEED TO BE EXTENDED + TO ACCOUNT FOR NOISE. + + The fit performs the following steps: + + 1) Determine the source-plane centre of the point source, which could be a free model parameter or computed + as the barycenter of ray-traced positions in the source-plane, using name pairing (see below). + + 2) Determine the image-plane model positions using the `PointSolver` and the source-plane centre of the point + source (e.g. ray tracing triangles to and from the image and source planes), including accounting for + multi-plane ray-tracing. + + 3) Pair each model position with the observed position, not allowing for repeated pairings of the same + observed position to model positions, to compute the `residual_map`. This may result in some observed + positions not being paired to their closest model position, if doing so reduces the overall distances of + pairings. + + 5) Compute the chi-squared of each position as the square of the residual divided by the RMS noise-map value. + + 6) Sum the chi-squared values to compute the overall log likelihood of the fit. + + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. + + This fit object is used in the `FitPointDataset` to perform position based fitting of a `PointDataset`, + which may also fit other components of the point dataset like fluxes or time delays. + + When performing a `model-fit`via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. Parameters ---------- - data : Grid2DIrregular - The (y,x) arc-second coordinates of positions which the maximum distance and log_likelihood is computed using. - noise_value - The noise-value assumed when computing the log likelihood. + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. """ @property diff --git a/autolens/point/fit/positions/image/pair_all.py b/autolens/point/fit/positions/image/pair_all.py index 768101f2f..00bd4a454 100644 --- a/autolens/point/fit/positions/image/pair_all.py +++ b/autolens/point/fit/positions/image/pair_all.py @@ -7,35 +7,145 @@ class FitPositionsImagePairAll(AbstractFitPositionsImagePair): """ - A lens position fitter, which takes a set of positions (e.g. from a plane in the tracer) and computes \ - their maximum separation, such that points which tracer closer to one another have a higher log_likelihood. + Fits the positions of a a point source dataset using a `Tracer` object with an image-plane chi-squared where every + model position of the point-source is paired with all other observed positions using the probability of each + model posiition explaining each observed position. + + Pairing all model positions with all observed positions is a less intuitive and commonly used approach + than other methods, for example pairing each position one-to-one. The scheme was proposed in the paper + below and provides a number of benefits, for example being a fully Bayesian approach to the problem and + linearizing aspects of the problem. + + https://arxiv.org/abs/2406.15280 + + THIS IMPLEMENTATION DOES NOT CURRRENTLY BREAK DOWN THE CALCULATION INTO A RESIDUAL MAP AND CHI-SQUARED, + GOING STRAIGHT TO A `log_likelihood`. FUTURE WORK WILL WORK OUT HOW TO EXPRESS THIS IN TERMS OF A CHI-SQUARED + AND RESIDUAL MAP. + + The fit performs the following steps: + + 1) Determine the source-plane centre of the point source, which could be a free model parameter or computed + as the barycenter of ray-traced positions in the source-plane, using name pairing (see below). + + 2) Determine the image-plane model positions using the `PointSolver` and the source-plane centre of the point + source (e.g. ray tracing triangles to and from the image and source planes), including accounting for + multi-plane ray-tracing. + + 3) Pair every model position with every observed position and return the overall log likelihood of the fit. + + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. + + This fit object is used in the `FitPointDataset` to perform position based fitting of a `PointDataset`, + which may also fit other components of the point dataset like fluxes or time delays. + + When performing a `model-fit`via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. Parameters ---------- - data : Grid2DIrregular - The (y,x) arc-second coordinates of positions which the maximum distance and log_likelihood is computed using. - noise_value - The noise-value assumed when computing the log likelihood. + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. """ - @property - def noise_map(self): - noise_map = [] + def log_p( + self, + data_position: np.ndarray, + model_position: np.ndarray, + sigma: float, + ) -> float: + """ + Compute the log probability of a given model coordinate explaining a given observed coordinate. + + Accounts for noise, with noiser image coordinates having a comparatively lower log probability. + + Parameters + ---------- + data_position + The observed coordinate. + model_position + The model coordinate. + sigma + The noise associated with the observed coordinate. - for i in range(len(self.data)): - for j in range(len(self.model_data)): - noise_map.append(self._noise_map[i]) + Returns + ------- + The log probability of the model coordinate explaining the observed coordinate. + """ + chi2 = self.square_distance(data_position, model_position) / sigma**2 + return -np.log(np.sqrt(2 * np.pi * sigma**2)) - 0.5 * chi2 - return aa.ArrayIrregular(values=noise_map) + def all_permutations_log_likelihoods(self) -> np.ndarray: + """ + Compute the log likelihood for each permutation whereby the model could explain the observed image coordinates. + + For example, if there are two observed image coordinates and two model image coordinates, the log likelihood + for each permutation is: + + P(data_0 | model_0) * P(data_1 | model_1) + P(data_0 | model_1) * P(data_1 | model_0) + P(data_0 | model_0) * P(data_1 | model_0) + P(data_0 | model_1) * P(data_1 | model_1) + + This is every way in which the coordinates generated by the model can explain the observed coordinates. + """ + return np.array( + [ + np.log( + np.sum( + [ + np.exp( + self.log_p( + data_position, + model_position, + sigma, + ) + ) + for model_position in self.model_data + if not np.isnan(model_position).any() + ] + ) + ) + for data_position, sigma in zip(self.data, self.noise_map) + ] + ) @property - def residual_map(self) -> aa.ArrayIrregular: - residual_map = [] + def chi_squared(self) -> float: + """ + Compute the log likelihood of the model image coordinates explaining the observed image coordinates. + + This is the sum across all permutations of the observed image coordinates of the log probability of each + model image coordinate explaining the observed image coordinate. - for model_data in self.model_data: - for data in self.data: - distance = np.sqrt(self.square_distance(data, model_data)) + For example, if there are two observed image coordinates and two model image coordinates, the log likelihood + is the sum of the log probabilities: - residual_map.append(distance) + P(data_0 | model_0) * P(data_1 | model_1) + + P(data_0 | model_1) * P(data_1 | model_0) + + P(data_0 | model_0) * P(data_1 | model_0) + + P(data_0 | model_1) * P(data_1 | model_1) - return aa.ArrayIrregular(values=residual_map) + This is every way in which the coordinates generated by the model can explain the observed coordinates. + """ + n_non_nan_model_positions = np.count_nonzero( + ~np.isnan( + self.model_data, + ).any(axis=1) + ) + n_permutations = n_non_nan_model_positions ** len(self.data) + return -2.0 * ( + -np.log(n_permutations) + np.sum(self.all_permutations_log_likelihoods()) + ) diff --git a/autolens/point/fit/positions/image/pair_repeat.py b/autolens/point/fit/positions/image/pair_repeat.py index 7bef10256..9d6da6b99 100644 --- a/autolens/point/fit/positions/image/pair_repeat.py +++ b/autolens/point/fit/positions/image/pair_repeat.py @@ -7,15 +7,51 @@ class FitPositionsImagePairRepeat(AbstractFitPositionsImagePair): """ - A lens position fitter, which takes a set of positions (e.g. from a plane in the tracer) and computes \ - their maximum separation, such that points which tracer closer to one another have a higher log_likelihood. + Fits the positions of a a point source dataset using a `Tracer` object with an image-plane chi-squared where every + model position of the point-source is paired with its closest observed position, allowing for repeated pairings of + the same observed position to model positions. + + The fit performs the following steps: + + 1) Determine the source-plane centre of the point source, which could be a free model parameter or computed + as the barycenter of ray-traced positions in the source-plane, using name pairing (see below). + + 2) Determine the image-plane model positions using the `PointSolver` and the source-plane centre of the point + source (e.g. ray tracing triangles to and from the image and source planes), including accounting for + multi-plane ray-tracing. + + 3) Pair each model position with the closest observed position, allowing for repeated pairings of the same + observed position to model positions, to compute the `residual_map`. + + 5) Compute the chi-squared of each position as the square of the residual divided by the RMS noise-map value. + + 6) Sum the chi-squared values to compute the overall log likelihood of the fit. + + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. + + This fit object is used in the `FitPointDataset` to perform position based fitting of a `PointDataset`, + which may also fit other components of the point dataset like fluxes or time delays. + + When performing a `model-fit`via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. Parameters ---------- - data : Grid2DIrregular - The (y,x) arc-second coordinates of positions which the maximum distance and log_likelihood is computed using. - noise_value - The noise-value assumed when computing the log likelihood. + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. """ @property diff --git a/autolens/point/fit/positions/source/separations.py b/autolens/point/fit/positions/source/separations.py index 0dbc88bbd..2c35269b8 100644 --- a/autolens/point/fit/positions/source/separations.py +++ b/autolens/point/fit/positions/source/separations.py @@ -1,3 +1,5 @@ +from autoarray.numpy_wrapper import numpy as npw +import numpy as np from typing import Optional import autoarray as aa @@ -19,15 +21,55 @@ def __init__( profile: Optional[ag.ps.Point] = None, ): """ - A lens position fitter, which takes a set of positions (e.g. from a plane in the tracer) and computes \ - their maximum separation, such that points which tracer closer to one another have a higher log_likelihood. + Fits the positions of a a point source dataset using a `Tracer` object with a source-plane chi-squared based on + the separation of image-plane positions ray-traced to the source-plane compared to the centre of the source + galaxy. + + The fit performs the following steps: + + 1) Determine the source-plane centre of the source-galaxy, which could be a free model parameter or computed + as the barycenter of ray-traced positions in the source-plane, using name pairing (see below). + + 2) Ray-trace the positions in the point source to the source-plane via the `Tracer`, including accounting for + multi-plane ray-tracing. + + 3) Compute the distance of each ray-traced position to the source-plane centre and compute the residuals, + + 4) Compute the magnification of each image-plane position via the Hessian of the tracer's deflection angles. + + 5) Compute the residuals of each position as the difference between the source-plane centre and each + ray-traced position. + + 6) Compute the chi-squared of each position as the square of the residual multiplied by the magnification and + divided by the RMS noise-map value. + + 7) Sum the chi-squared values to compute the overall log likelihood of the fit. + + Point source fitting uses name pairing, whereby the `name` of the `Point` object is paired to the name of the + point source dataset to ensure that point source datasets are fitted to the correct point source. + + This fit object is used in the `FitPointDataset` to perform position based fitting of a `PointDataset`, + which may also fit other components of the point dataset like fluxes or time delays. + + When performing a `model-fit`via an `AnalysisPoint` object the `figure_of_merit` of this object + is called and returned in the `log_likelihood_function`. Parameters ---------- - data : Grid2DIrregular - The (y,x) arc-second coordinates of positions which the maximum distance and log_likelihood is computed using. - noise_value - The noise-value assumed when computing the log likelihood. + name + The name of the point source dataset which is paired to a `Point` profile. + data + The positions of the point source in the image-plane which are fitted. + noise_map + The noise-map of the positions which are used to compute the log likelihood of the positions. + tracer + The tracer of galaxies whose point source profile are used to fit the positions. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. This is not used in this source-plane point source fit. + profile + Manually input the profile of the point source, which is used instead of the one extracted from the + tracer via name pairing if that profile is not found. """ super().__init__( @@ -42,10 +84,12 @@ def __init__( @property def model_data(self) -> aa.Grid2DIrregular: """ - Returns the model positions, which are computed via the point solver. + Returns the source-plane model positions of the point source, which are the positions of the image-plane + positions ray-traced to the source-plane. - It if common for many more image-plane positions to be computed than actual positions in the dataset. In this - case, each data point is paired with its closest model position. + This calculation accounts for multi-plane ray-tracing, whereby if the tracer has more than 2 planees the + redshift of the point source galaxy is extracted and the deflections between the image-plane and source-plane + at its specific redshift are used. """ if len(self.tracer.planes) <= 2: deflections = self.tracer.deflections_yx_2d_from(grid=self.data) @@ -58,6 +102,41 @@ def model_data(self) -> aa.Grid2DIrregular: @property def residual_map(self) -> aa.ArrayIrregular: + """ + Returns the residuals of the point-source source-plane fit, which are the distances of each source-plane + position from the source-plane centre. + """ return self.model_data.distances_to_coordinate_from( coordinate=self.source_plane_coordinate ) + + @property + def chi_squared_map(self) -> float: + """ + Returns the chi-squared of the point-source source-plane fit, which is the sum of the squared residuals + multiplied by the magnifications squared, divided by the noise-map values squared. + """ + + return self.residual_map**2.0 / ( + self.magnifications_at_positions**-2.0 * self.noise_map**2.0 + ) + + @property + def noise_normalization(self) -> float: + """ + Returns the normalization of the noise-map, which is the sum of the noise-map values squared. + """ + return npw.sum( + npw.log( + 2 + * np.pi + * (self.magnifications_at_positions**-2.0 * self.noise_map**2.0) + ) + ) + + @property + def log_likelihood(self) -> float: + """ + Returns the log likelihood of the point-source source-plane fit, which is the sum of the chi-squared values. + """ + return -0.5 * (sum(self.chi_squared_map) + self.noise_normalization) diff --git a/autolens/point/fit/positions/source/max_separation.py b/autolens/point/max_separation.py similarity index 95% rename from autolens/point/fit/positions/source/max_separation.py rename to autolens/point/max_separation.py index 2af62a178..2b60f0968 100644 --- a/autolens/point/fit/positions/source/max_separation.py +++ b/autolens/point/max_separation.py @@ -5,7 +5,7 @@ from autolens.lens.tracer import Tracer -class FitPositionsSourceMaxSeparation: +class SourceMaxSeparation: def __init__( self, data: aa.Grid2DIrregular, diff --git a/autolens/point/model/analysis.py b/autolens/point/model/analysis.py index f41db7a75..aa4e56cca 100644 --- a/autolens/point/model/analysis.py +++ b/autolens/point/model/analysis.py @@ -6,11 +6,11 @@ from autogalaxy.analysis.analysis.analysis import Analysis as AgAnalysis from autolens.analysis.analysis.lens import AnalysisLens -from autolens.analysis.plotter_interface import PlotterInterface from autolens.point.fit.positions.image.pair_repeat import FitPositionsImagePairRepeat from autolens.point.fit.dataset import FitPointDataset from autolens.point.dataset import PointDataset from autolens.point.model.result import ResultPoint +from autolens.point.model.visualizer import VisualizerPoint from autolens.point.solver import PointSolver from autolens import exc @@ -24,6 +24,7 @@ class AnalysisPoint(AgAnalysis, AnalysisLens): + Visualizer = VisualizerPoint Result = ResultPoint def __init__( @@ -36,29 +37,38 @@ def __init__( title_prefix: str = None, ): """ - The analysis performed for model-fitting a point-source dataset, for example fitting the point-sources of a - multiply imaged lensed quasar or supernovae of many source galaxies of a galaxy cluster. + Fits a lens model to a point source dataset (e.g. positions, fluxes, time delays) via a non-linear search. - The analysis brings together the data, model and non-linear search in the classes `log_likelihood_function`, - which is called by every iteration of the non-linear search to compute a likelihood value which samples - parameter space. + The `Analysis` class defines the `log_likelihood_function` which fits the model to the dataset and returns the + log likelihood value defining how well the model fitted the data. + + It handles many other tasks, such as visualization, outputting results to hard-disk and storing results in + a format that can be loaded after the model-fit is complete. + + This class is used for model-fits which fit lens models to point datasets, which may include some combination + of positions, fluxes and time-delays. + + This class stores the settings used to perform the model-fit for certain components of the model (e.g. a + pixelization or inversion), the Cosmology used for the analysis and adapt images used for certain model + classes. Parameters ---------- - point_dict - A dictionary containing the full point source dictionary that is used for model-fitting. - solver - The object which is used to determine the image-plane of source-plane positions of a model (via a `Tracer`). dataset - The imaging of the point-source dataset, which is not used for model-fitting but can be used for - visualization. + The `PointDataset` that is fitted by the model, which contains a combination of positions, fluxes and + time-delays. + solver + Solves the lens equation in order to determine the image-plane positions of a point source by ray-tracing + triangles to and from the source-plane. + fit_positions_cls + The class used to fit the positions of the point source dataset, which could be an image-plane or + source-plane chi-squared. cosmology - The cosmology of the ray-tracing calculation. + The Cosmology assumed for this analysis. title_prefix A string that is added before the title of all figures output by visualization, for example to put the name of the dataset and galaxy in the title. """ - super().__init__(cosmology=cosmology) AnalysisLens.__init__(self=self, cosmology=cosmology) @@ -71,17 +81,45 @@ def __init__( def log_likelihood_function(self, instance): """ - Determine the fit of the strong lens system of lens galaxies and source galaxies to the point source data. + Given an instance of the model, where the model parameters are set via a non-linear search, fit the model + instance to the point source dataset. + + This function returns a log likelihood which is used by the non-linear search to guide the model-fit. + + For this analysis class, this function performs the following steps: + + 1) Extracts all galaxies from the model instance and set up a `Tracer`, which includes ordering the galaxies + by redshift to set up each `Plane`. + + 2) Use the `Tracer` and other attributes to create a `FitPointDataset` object, which performs the steps + below to fit different components of the point source dataset. + + 3) If the point source dataset has positions and model fits positions, perform this fit and compute the + log likelihood. This calculation uses the `fit_positions_cls` object, which may be an image-plane or + source-plane chi-squared. + + 4) If the point source dataset has fluxes and model fits fluxes, perform this fit and compute the log likelihood. + + 5) If the point source dataset has time-delays and model fits time-delays, perform this fit and compute the + log likelihood [NOT SUPPORTED YET]. + + 6) Sum the log likelihoods of the positions, fluxes and time-delays (if they are fitted) to get the overall + log likelihood of the model. + + Certain models will fail to fit the dataset and raise an exception. For example for ill defined mass models + the `PointSolver` may find no solution. In such circumstances the model is discarded and its likelihood value + is passed to the non-linear search in a way that it ignores it (for example, using a value of -1.0e99). Parameters ---------- instance - A model instance with attributes + 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 ------- - fit : Fit - A fractional value indicating how well this model fit and the model masked_dataset itself + float + The log likelihood indicating how well this model instance fitted the imaging data. """ try: fit = self.fit_from(instance=instance) @@ -95,7 +133,24 @@ def fit_from( tracer = self.tracer_via_instance_from( instance=instance, run_time_dict=run_time_dict ) + """ + Given a model instance create a `FitPointDataset` object. + This function is used in the `log_likelihood_function` to fit the model to the imaging data and compute the + log likelihood. + + 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). + run_time_dict + A dictionary which times functions called to fit the model to data, for profiling. + + Returns + ------- + The fit of the lens model to the point source dataset. + """ return FitPointDataset( dataset=self.dataset, tracer=tracer, @@ -104,12 +159,26 @@ def fit_from( run_time_dict=run_time_dict, ) - def visualize(self, paths, instance, during_analysis): - tracer = self.tracer_via_instance_from(instance=instance) + def save_attributes(self, paths: af.DirectoryPaths): + """ + Before the non-linear search begins, 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. - plotter_interface = PlotterInterface(image_path=paths.image_path) + For this analysis, it uses the `AnalysisDataset` object's method to output the following: - def save_attributes(self, paths: af.DirectoryPaths): + - The dataset's point source dataset as a readable .json file. + + It is common for these attributes to be loaded by many of the template aggregator functions given in the + `aggregator` modules. For example, when using the database tools to perform a fit, the default behaviour is for + the dataset, settings and other attributes necessary to perform the fit to be loaded via the pickle files + output by this function. + + Parameters + ---------- + paths + The 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. + """ ag.output_to_json( obj=self.dataset, file_path=paths._files_path / "dataset.json", diff --git a/autolens/point/model/plotter_interface.py b/autolens/point/model/plotter_interface.py new file mode 100644 index 000000000..8d4d8e04e --- /dev/null +++ b/autolens/point/model/plotter_interface.py @@ -0,0 +1,77 @@ +from os import path + +from autolens.analysis.plotter_interface import PlotterInterface + +from autolens.point.fit.dataset import FitPointDataset +from autolens.point.plot.fit_point_plotters import FitPointDatasetPlotter +from autolens.point.dataset import PointDataset +from autolens.point.plot.point_dataset_plotters import PointDatasetPlotter + +from autolens.analysis.plotter_interface import plot_setting + + +class PlotterInterfacePoint(PlotterInterface): + def dataset_point(self, dataset: PointDataset): + """ + Output visualization of an `PointDataset` dataset, typically before a model-fit is performed. + + Images are output to the `image` folder of the `image_path`. When used with a non-linear search the `image_path` + is the output folder of the non-linear search. + + Visualization includes individual images of the different points of the dataset (e.g. the positions and fluxes) + + The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under + the `point_dataset` header. + + Parameters + ---------- + dataset + The imaging dataset which is visualized. + """ + + def should_plot(name): + return plot_setting(section=["point_dataset"], name=name) + + mat_plot_2d = self.mat_plot_2d_from() + + dataset_plotter = PointDatasetPlotter( + dataset=dataset, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d + ) + + if should_plot("subplot_dataset"): + dataset_plotter.subplot_dataset() + + def fit_point( + self, + fit: FitPointDataset, + ): + """ + Visualizes a `FitPointDataset` object, which fits an imaging dataset. + + Images are output to the `image` folder of the `image_path` in a subfolder called `fit`. When + used with a non-linear search the `image_path` points to the search's results folder and this function + visualizes the maximum log likelihood `FitImaging` inferred by the search so far. + + Visualization includes a subplot of individual images of attributes of the `FitPointDataset` (e.g. the model + data and data) and .fits files containing its attributes grouped together. + + The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under + the `fit` and `fit_point_dataset` headers. + + Parameters + ---------- + fit + The maximum log likelihood `FitPointDataset` of the non-linear search which is used to plot the fit. + """ + + def should_plot(name): + return plot_setting(section=["fit", "fit_point_dataset"], name=name) + + mat_plot_2d = self.mat_plot_2d_from() + + fit_plotter = FitPointDatasetPlotter( + fit=fit, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d + ) + + if should_plot("subplot_fit"): + fit_plotter.subplot_fit() diff --git a/autolens/point/model/visualizer.py b/autolens/point/model/visualizer.py new file mode 100644 index 000000000..216421629 --- /dev/null +++ b/autolens/point/model/visualizer.py @@ -0,0 +1,86 @@ +import autofit as af +import autogalaxy as ag + +from autolens.point.model.plotter_interface import PlotterInterfacePoint + + +class VisualizerPoint(af.Visualizer): + @staticmethod + def visualize_before_fit( + analysis, + paths: af.AbstractPaths, + model: af.AbstractPriorModel, + ): + """ + PyAutoFit calls this function immediately before the non-linear search begins. + + It visualizes objects which do not change throughout the model fit like the dataset. + + Parameters + ---------- + paths + The 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 model object, which includes model components representing the galaxies that are fitted to + the imaging data. + """ + + plotter_interface = PlotterInterfacePoint( + image_path=paths.image_path, title_prefix=analysis.title_prefix + ) + + plotter_interface.dataset_point(dataset=analysis.dataset) + + @staticmethod + def visualize( + analysis, + paths: af.DirectoryPaths, + instance: af.ModelInstance, + during_analysis: bool, + ): + """ + Output images of the maximum log likelihood model inferred by the model-fit. This function is called throughout + the non-linear search at regular intervals, and therefore provides on-the-fly visualization of how well the + model-fit is going. + + The visualization performed by this function includes: + + - Images of the best-fit `Tracer`, including the images of each of its galaxies. + + - Images of the best-fit `FitPointDataset`, including the model-image, residuals and chi-squared of its fit to + the imaging data. + + The images output by this function are customized using the file `config/visualize/plots.yaml`. + + Parameters + ---------- + paths + The 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. + 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). + """ + fit = analysis.fit_from(instance=instance) + + plotter_interface = PlotterInterfacePoint( + image_path=paths.image_path, title_prefix=analysis.title_prefix + ) + + plotter_interface.fit_point(fit=fit) + + tracer = fit.tracer + + grid = ag.Grid2D.from_extent( + extent=fit.dataset.extent_from(), shape_native=(100, 100) + ) + + plotter_interface.tracer( + tracer=tracer, + grid=grid, + ) + plotter_interface.galaxies( + galaxies=tracer.galaxies, + grid=grid, + ) diff --git a/autolens/point/plot/fit_point_plotters.py b/autolens/point/plot/fit_point_plotters.py index c70a49fb5..d3b65c657 100644 --- a/autolens/point/plot/fit_point_plotters.py +++ b/autolens/point/plot/fit_point_plotters.py @@ -1,11 +1,10 @@ -from autoarray.plot.abstract_plotters import AbstractPlotter - import autogalaxy.plot as aplt +from autolens.plot.abstract_plotters import Plotter from autolens.point.fit.dataset import FitPointDataset -class FitPointDatasetPlotter(AbstractPlotter): +class FitPointDatasetPlotter(Plotter): def __init__( self, fit: FitPointDataset, @@ -16,6 +15,30 @@ def __init__( visuals_2d: aplt.Visuals2D = aplt.Visuals2D(), include_2d: aplt.Include2D = aplt.Include2D(), ): + """ + Plots the attributes of `FitPointDataset` objects using matplotlib methods and functions which customize the + plot's appearance. + + The `mat_plot_2d` attribute wraps 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 `Visuals2D` object. Attributes may be extracted from + the `FitImaging` and plotted via the visuals object, if the corresponding entry is `True` in the `Include2D` + object or the `config/visualize/include.ini` file. + + Parameters + ---------- + fit + The fit to a point source dataset, which includes the data, model positions and other quantities which can + be plotted like the residual_map and chi-squared map. + mat_plot_2d + Contains objects which wrap the matplotlib function calls that make the plot. + visuals_2d + Contains visuals that can be overlaid on the plot. + include_2d + Specifies which attributes of the `Array2D` are extracted and plotted as visuals. + """ super().__init__( mat_plot_1d=mat_plot_1d, visuals_1d=visuals_1d, @@ -34,10 +57,61 @@ def get_visuals_2d(self) -> aplt.Visuals2D: return self.visuals_2d def figures_2d(self, positions: bool = False, fluxes: bool = False): + """ + Plots the individual attributes of the plotter's `FitPointDataset` object in 2D. + + The API is such that every plottable attribute of the `FitPointDataset` object is an input parameter of type + bool of the function, which if switched to `True` means that it is plotted. + + Parameters + ---------- + positions + If `True`, the dataset's positions are plotted on the figure compared to the model positions. + fluxes + If `True`, the dataset's fluxes are plotted on the figure compared to the model fluxes. + """ if positions: visuals_2d = self.get_visuals_2d() - visuals_2d += visuals_2d.__class__(positions=self.fit.positions.model_data) + visuals_2d += visuals_2d.__class__( + multiple_images=self.fit.positions.model_data + ) + + if self.mat_plot_2d.axis.kwargs.get("extent") is None: + buffer = 0.1 + + y_max = ( + max( + max(self.fit.dataset.positions[:, 0]), + max(self.fit.positions.model_data[:, 0]), + ) + + buffer + ) + y_min = ( + min( + min(self.fit.dataset.positions[:, 0]), + min(self.fit.positions.model_data[:, 0]), + ) + - buffer + ) + x_max = ( + max( + max(self.fit.dataset.positions[:, 1]), + max(self.fit.positions.model_data[:, 1]), + ) + + buffer + ) + x_min = ( + min( + min(self.fit.dataset.positions[:, 1]), + min(self.fit.positions.model_data[:, 1]), + ) + - buffer + ) + + extent = [y_min, y_max, x_min, x_max] + + self.mat_plot_2d.axis.kwargs["extent"] = extent self.mat_plot_2d.plot_grid( grid=self.fit.dataset.positions, @@ -46,7 +120,7 @@ def figures_2d(self, positions: bool = False, fluxes: bool = False): visuals_2d=visuals_2d, auto_labels=aplt.AutoLabels( title=f"{self.fit.dataset.name} Fit Positions", - filename="fit_point_dataset_positions", + filename="fit_point_positions", ), buffer=0.1, ) @@ -66,9 +140,14 @@ def figures_2d(self, positions: bool = False, fluxes: bool = False): if self.fit.dataset.fluxes is not None: visuals_1d = self.get_visuals_1d() - visuals_1d += visuals_1d.__class__( - model_fluxes=self.fit.flux.model_fluxes - ) + # Dataset may have flux but model may not + + try: + visuals_1d += visuals_1d.__class__( + model_fluxes=self.fit.flux.model_fluxes + ) + except AttributeError: + pass self.mat_plot_1d.plot_yx( y=self.fit.dataset.fluxes, @@ -76,7 +155,7 @@ def figures_2d(self, positions: bool = False, fluxes: bool = False): visuals_1d=visuals_1d, auto_labels=aplt.AutoLabels( title=f" {self.fit.dataset.name} Fit Fluxes", - filename="fit_point_dataset_fluxes", + filename="fit_point_fluxes", xlabel="Point Number", ), plot_axis_type_override="errorbar", diff --git a/autolens/point/plot/point_dataset_plotters.py b/autolens/point/plot/point_dataset_plotters.py index a47eae87e..692479147 100644 --- a/autolens/point/plot/point_dataset_plotters.py +++ b/autolens/point/plot/point_dataset_plotters.py @@ -15,6 +15,31 @@ def __init__( visuals_2d: aplt.Visuals2D = aplt.Visuals2D(), include_2d: aplt.Include2D = aplt.Include2D(), ): + """ + Plots the attributes of `PointDataset` objects using the matplotlib methods and functions functions which + customize the plot's appearance. + + The `mat_plot_2d` attribute wraps 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 `Visuals2D` object. Attributes may be extracted from + the `Imaging` and plotted via the visuals object, if the corresponding entry is `True` in the `Include2D` + object or the `config/visualize/include.ini` file. + + Parameters + ---------- + dataset + The imaging dataset the plotter plots. + get_visuals_2d + A function which extracts from the `Imaging` the 2D visuals which are plotted on figures. + 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 `Imaging` are extracted and plotted as visuals for 2D plots. + """ super().__init__( mat_plot_1d=mat_plot_1d, visuals_1d=visuals_1d, @@ -33,6 +58,19 @@ def get_visuals_2d(self) -> aplt.Visuals2D: return self.visuals_2d def figures_2d(self, positions: bool = False, fluxes: bool = False): + """ + Plots the individual attributes of the plotter's `PointDataset` object in 2D. + + The API is such that every plottable attribute of the `Imaging` object is an input parameter of type bool of + the function, which if switched to `True` means that it is plotted. + + Parameters + ---------- + positions + If `True`, the dataset's positions are plotted on the figure compared to the model positions. + fluxes + If `True`, the dataset's fluxes are plotted on the figure compared to the model fluxes. + """ if positions: self.mat_plot_2d.plot_grid( grid=self.dataset.positions, diff --git a/autolens/point/solver/__init__.py b/autolens/point/solver/__init__.py index 2751beef9..d9cf14301 100644 --- a/autolens/point/solver/__init__.py +++ b/autolens/point/solver/__init__.py @@ -1 +1 @@ -from .point_solver import PointSolver +from .point_solver import PointSolver diff --git a/autolens/point/solver/point_solver.py b/autolens/point/solver/point_solver.py index 9cdecfd00..2d160d583 100644 --- a/autolens/point/solver/point_solver.py +++ b/autolens/point/solver/point_solver.py @@ -1,88 +1,78 @@ -import logging -from typing import Tuple, Optional - -from autoarray.numpy_wrapper import np - -import autoarray as aa -from autoarray.numpy_wrapper import use_jax -from autoarray.structures.triangles.shape import Point - -from autofit.jax_wrapper import jit, register_pytree_node_class -from autogalaxy import OperateDeflections -from .shape_solver import AbstractSolver - - -logger = logging.getLogger(__name__) - - -@register_pytree_node_class -class PointSolver(AbstractSolver): - @jit - def solve( - self, - tracer: OperateDeflections, - source_plane_coordinate: Tuple[float, float], - source_plane_redshift: Optional[float] = None, - ) -> aa.Grid2DIrregular: - """ - Solve for the image plane coordinates that are traced to the source plane coordinate. - - This is done by tiling the image plane with triangles and checking if the source plane coordinate is contained - within the triangle. The triangles are subsampled to increase the resolution with only the triangles that - contain the source plane coordinate and their neighbours being kept. - - The means of the triangles are then filtered to keep only those with an absolute magnification above the - threshold. - - Parameters - ---------- - source_plane_coordinate - The source plane coordinate to trace to the image plane. - tracer - The tracer that traces the image plane coordinates to the source plane - source_plane_redshift - The redshift of the source plane coordinate. - - Returns - ------- - A list of image plane coordinates that are traced to the source plane coordinate. - """ - kept_triangles = super().solve_triangles( - tracer=tracer, - shape=Point(*source_plane_coordinate), - source_plane_redshift=source_plane_redshift, - ) - filtered_means = self._filter_low_magnification( - tracer=tracer, points=kept_triangles.means - ) - if use_jax: - return aa.Grid2DIrregular([pair for pair in filtered_means]) - - filtered_means = [ - pair for pair in filtered_means if not np.any(np.isnan(pair)).all() - ] - - difference = len(kept_triangles.means) - len(filtered_means) - if difference > 0: - logger.debug( - f"Filtered one multiple-image with magnification below threshold." - ) - elif difference > 1: - logger.warning( - f"Filtered {difference} multiple-images with magnification below threshold." - ) - - filtered_close = [] - - for mean in filtered_means: - if any( - np.linalg.norm(np.array(mean) - np.array(other)) - <= self.pixel_scale_precision - for other in filtered_close - ): - continue - filtered_close.append(mean) - - return aa.Grid2DIrregular( - [pair for pair in filtered_close if not np.isnan(pair).all()] - ) +import logging +from typing import Tuple, Optional + +from autoarray.numpy_wrapper import np + +import autoarray as aa +from autoarray.numpy_wrapper import use_jax +from autoarray.structures.triangles.shape import Point + +from autofit.jax_wrapper import jit, register_pytree_node_class +from autogalaxy import OperateDeflections +from .shape_solver import AbstractSolver + + +logger = logging.getLogger(__name__) + + +@register_pytree_node_class +class PointSolver(AbstractSolver): + @jit + def solve( + self, + tracer: OperateDeflections, + source_plane_coordinate: Tuple[float, float], + source_plane_redshift: Optional[float] = None, + ) -> aa.Grid2DIrregular: + """ + Solve for the image plane coordinates that are traced to the source plane coordinate. + + This is done by tiling the image plane with triangles and checking if the source plane coordinate is contained + within the triangle. The triangles are sub-sampled to increase the resolution with only the triangles that + contain the source plane coordinate and their neighbours being kept. + + The means of the triangles are then filtered to keep only those with an absolute magnification above the + threshold. + + Parameters + ---------- + source_plane_coordinate + The source plane coordinate to trace to the image plane. + tracer + The tracer that traces the image plane coordinates to the source plane + source_plane_redshift + The redshift of the source plane coordinate. + + Returns + ------- + A list of image plane coordinates that are traced to the source plane coordinate. + """ + kept_triangles = super().solve_triangles( + tracer=tracer, + shape=Point(*source_plane_coordinate), + source_plane_redshift=source_plane_redshift, + ) + + filtered_means = self._filter_low_magnification( + tracer=tracer, points=kept_triangles.means + ) + if use_jax: + return aa.Grid2DIrregular([pair for pair in filtered_means]) + + filtered_means = [ + pair for pair in filtered_means if not np.any(np.isnan(pair)).all() + ] + + difference = len(kept_triangles.means) - len(filtered_means) + if difference > 0: + logger.debug( + f"Filtered one multiple-image with magnification below threshold." + ) + elif difference > 1: + logger.warning( + f"Filtered {difference} multiple-images with magnification below threshold." + ) + + return aa.Grid2DIrregular( + [pair for pair in filtered_means if not np.isnan(pair).all()] + ) diff --git a/autolens/point/solver/shape_solver.py b/autolens/point/solver/shape_solver.py index 2a8dd3afe..f398f41cd 100644 --- a/autolens/point/solver/shape_solver.py +++ b/autolens/point/solver/shape_solver.py @@ -10,19 +10,18 @@ try: if use_jax: - from autoarray.structures.triangles.jax_array import ( - ArrayTriangles, - MAX_CONTAINING_SIZE, + from autoarray.structures.triangles.coordinate_array.jax_coordinate_array import ( + CoordinateArrayTriangles, ) else: - from autoarray.structures.triangles.array import ArrayTriangles - - MAX_CONTAINING_SIZE = None + from autoarray.structures.triangles.coordinate_array.coordinate_array import ( + CoordinateArrayTriangles, + ) except ImportError: - from autoarray.structures.triangles.array import ArrayTriangles - - MAX_CONTAINING_SIZE = None + from autoarray.structures.triangles.coordinate_array.coordinate_array import ( + CoordinateArrayTriangles, + ) from autoarray.structures.triangles.abstract import AbstractTriangles @@ -40,6 +39,7 @@ def __init__( initial_triangles: AbstractTriangles, pixel_scale_precision: float, magnification_threshold=0.1, + neighbor_degree: int = 1, ): """ Determine the image plane coordinates that are traced to be a source plane coordinate. @@ -50,12 +50,16 @@ def __init__( Parameters ---------- + neighbor_degree + The number of times recursively add neighbors for the triangles that contain + the source plane coordinate. pixel_scale_precision The target pixel scale of the image grid. """ self.scale = scale self.pixel_scale_precision = pixel_scale_precision self.magnification_threshold = magnification_threshold + self.neighbor_degree = neighbor_degree self.initial_triangles = initial_triangles @@ -66,8 +70,8 @@ def for_grid( grid: aa.Grid2D, pixel_scale_precision: float, magnification_threshold=0.1, - array_triangles_cls: Type[AbstractTriangles] = ArrayTriangles, - max_containing_size=MAX_CONTAINING_SIZE, + array_triangles_cls: Type[AbstractTriangles] = CoordinateArrayTriangles, + neighbor_degree: int = 1, ): """ Create a solver for a given grid. @@ -88,6 +92,8 @@ def for_grid( max_containing_size Only applies to JAX. This is the maximum number of multiple images expected. We need to know this in advance to allocate memory for the JAX array. + neighbor_degree + The number of times recursively add neighbors for the triangles that contain Returns ------- @@ -103,13 +109,65 @@ def for_grid( x_min = x.min() x_max = x.max() + return cls.for_limits_and_scale( + y_min=y_min, + y_max=y_max, + x_min=x_min, + x_max=x_max, + scale=scale, + pixel_scale_precision=pixel_scale_precision, + magnification_threshold=magnification_threshold, + array_triangles_cls=array_triangles_cls, + neighbor_degree=neighbor_degree, + ) + + @classmethod + def for_limits_and_scale( + cls, + y_min=-1.0, + y_max=1.0, + x_min=-1.0, + x_max=1.0, + scale=0.1, + pixel_scale_precision: float = 0.001, + magnification_threshold=0.1, + array_triangles_cls: Type[AbstractTriangles] = CoordinateArrayTriangles, + neighbor_degree: int = 1, + ): + """ + Create a solver for a given grid. + + The grid defines the limits of the image plane and the pixel scale. + + Parameters + ---------- + y_min + y_max + x_min + x_max + The limits of the image plane in pixels. + scale + The pixel scale of the image plane. The initial triangles have this side length. + pixel_scale_precision + The precision to which the triangles should be subdivided. + magnification_threshold + The threshold for the magnification under which multiple images are filtered. + array_triangles_cls + The class to use for the triangles. JAX is used implicitly if USE_JAX=1 and + jax is installed. + neighbor_degree + The number of times recursively add neighbors for the triangles that contain + + Returns + ------- + The solver. + """ initial_triangles = array_triangles_cls.for_limits_and_scale( y_min=y_min, y_max=y_max, x_min=x_min, x_max=x_max, scale=scale, - max_containing_size=max_containing_size, ) return cls( @@ -117,6 +175,7 @@ def for_grid( initial_triangles=initial_triangles, pixel_scale_precision=pixel_scale_precision, magnification_threshold=magnification_threshold, + neighbor_degree=neighbor_degree, ) @property @@ -227,12 +286,11 @@ def _filter_low_magnification( mask = np.abs(magnifications.array) > self.magnification_threshold return np.where(mask[:, None], points, np.nan) - def _filtered_triangles( + def _source_triangles( self, tracer: OperateDeflections, triangles: aa.AbstractTriangles, source_plane_redshift, - shape: Shape, ): """ Filter the triangles to keep only those that meet the solver condition @@ -242,11 +300,7 @@ def _filtered_triangles( grid=aa.Grid2DIrregular(triangles.vertices), source_plane_redshift=source_plane_redshift, ) - source_triangles = triangles.with_vertices(source_plane_grid.array) - - indexes = source_triangles.containing_indices(shape=shape) - - return triangles.for_indexes(indexes=indexes) + return triangles.with_vertices(source_plane_grid.array) def steps( self, @@ -272,13 +326,19 @@ def steps( """ initial_triangles = self.initial_triangles for number in range(self.n_steps): - kept_triangles = self._filtered_triangles( + source_triangles = self._source_triangles( tracer=tracer, triangles=initial_triangles, source_plane_redshift=source_plane_redshift, - shape=shape, ) - neighbourhood = kept_triangles.neighborhood() + + indexes = source_triangles.containing_indices(shape=shape) + kept_triangles = initial_triangles.for_indexes(indexes=indexes) + + neighbourhood = kept_triangles + for _ in range(self.neighbor_degree): + neighbourhood = neighbourhood.neighborhood() + up_sampled = neighbourhood.up_sample() yield Step( @@ -287,6 +347,7 @@ def steps( filtered_triangles=kept_triangles, neighbourhood=neighbourhood, up_sampled=up_sampled, + source_triangles=source_triangles, ) initial_triangles = up_sampled diff --git a/autolens/point/solver/step.py b/autolens/point/solver/step.py index 0f06aa568..c25909677 100644 --- a/autolens/point/solver/step.py +++ b/autolens/point/solver/step.py @@ -5,7 +5,7 @@ from autoarray.numpy_wrapper import register_pytree_node_class try: - from autoarray.structures.triangles.jax_array import ArrayTriangles + from autoarray.structures.triangles.array.jax_array import ArrayTriangles except ImportError: from autoarray.structures.triangles.array import ArrayTriangles @@ -38,6 +38,7 @@ class Step: filtered_triangles: aa.AbstractTriangles neighbourhood: aa.AbstractTriangles up_sampled: aa.AbstractTriangles + source_triangles: aa.AbstractTriangles def tree_flatten(self): return ( diff --git a/autolens/point/visualise.py b/autolens/point/visualise.py index 26e2cd1f1..2bf253701 100644 --- a/autolens/point/visualise.py +++ b/autolens/point/visualise.py @@ -22,3 +22,36 @@ def visualise(step: Step): plt.title(f"Step {step.number}") plt.gca().set_aspect("equal", adjustable="box") plt.show() + + +def plot_triangles(triangles, color="black", title="Triangles", point=None): + plt.figure(figsize=(8, 8)) + for triangle in triangles: + triangle = np.append(triangle, [triangle[0]], axis=0) + plt.plot(triangle[:, 0], triangle[:, 1], "o-", color=color) + + if point: + plt.plot(point[0], point[1], "x", color="red") + + plt.xlabel("X") + plt.ylabel("Y") + plt.title(title) + plt.gca().set_aspect("equal", adjustable="box") + plt.show() + + +def plot_triangles_compare(triangles_a, triangles_b, number=None): + plt.figure(figsize=(8, 8)) + for triangle in triangles_a: + triangle = np.append(triangle, [triangle[0]], axis=0) + plt.plot(triangle[:, 0], triangle[:, 1], "o-", color="red") + + for triangle in triangles_b: + triangle = np.append(triangle, [triangle[0]], axis=0) + plt.plot(triangle[:, 0], triangle[:, 1], "o-", color="blue") + + plt.xlabel("X") + plt.ylabel("Y") + plt.title("Triangles" + f" {number}" if number is not None else "") + plt.gca().set_aspect("equal", adjustable="box") + plt.show() diff --git a/autolens/quantity/model/result.py b/autolens/quantity/model/result.py index 1d1a1b468..4aa09ff11 100644 --- a/autolens/quantity/model/result.py +++ b/autolens/quantity/model/result.py @@ -43,7 +43,7 @@ def grid(self) -> aa.Grid2D: """ The masked 2D grid used by the dataset in the model-fit. """ - return self.analysis.dataset.grids.uniform + return self.analysis.dataset.grids.lp @property def max_log_likelihood_tracer(self) -> Tracer: diff --git a/autolens/quantity/model/visualizer.py b/autolens/quantity/model/visualizer.py index 62095641e..70aa13e49 100644 --- a/autolens/quantity/model/visualizer.py +++ b/autolens/quantity/model/visualizer.py @@ -38,9 +38,6 @@ def visualize( 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). - during_analysis - If True the visualization is being performed midway through the non-linear search before it is finished, - which may change which images are output. """ if os.environ.get("PYAUTOFIT_TEST_MODE") == "1": @@ -56,6 +53,5 @@ def visualize( plotter_interface = PlotterInterface(image_path=paths.image_path) plotter_interface.tracer( tracer=fit.tracer, - grid=analysis.dataset.grids.uniform, - during_analysis=during_analysis, + grid=analysis.dataset.grids.lp, ) diff --git a/autolens/util/__init__.py b/autolens/util/__init__.py index 5ab119fcb..4f84e5e2c 100644 --- a/autolens/util/__init__.py +++ b/autolens/util/__init__.py @@ -1,6 +1,7 @@ from autoarray.geometry import geometry_util as geometry from autoarray.mask import mask_1d_util as mask_1d from autoarray.mask import mask_2d_util as mask_2d +from autoarray.operators.over_sampling import over_sample_util as over_sample from autoarray.structures.arrays import array_1d_util as array_1d from autoarray.structures.arrays import array_2d_util as array_2d from autoarray.structures.grids import grid_1d_util as grid_1d diff --git a/docs/api/data.rst b/docs/api/data.rst index 609382907..ddda52490 100644 --- a/docs/api/data.rst +++ b/docs/api/data.rst @@ -67,8 +67,7 @@ applied to datasets to apply over sampling to their fit. :template: custom-class-template.rst :recursive: - OverSamplingUniform - OverSamplingIterate + OverSampling 1D Data Structures diff --git a/docs/howtolens/chapter_2_lens_modeling.rst b/docs/howtolens/chapter_2_lens_modeling.rst index 814b76647..59c2c7754 100644 --- a/docs/howtolens/chapter_2_lens_modeling.rst +++ b/docs/howtolens/chapter_2_lens_modeling.rst @@ -5,32 +5,26 @@ In chapter 2, we'll take you through how to model strong lenses using a non-line The chapter contains the following tutorials: -`Tutorial 1: Data `_ -- Loading and inspecting telescope imaging data of a strong lens. +`Tutorial 1: Non-linear Search `_ +- How a non-linear search is used to fit a lens model and the concepts of a parameter space and priors. -`Tutorial 2: Fitting `_ -- Fitting data with a strong lens model. +`Tutorial 2: Practicalities `_ +- Practicalities of performing model-fitting, like how to inspect the results on your hard-disk. -`Tutorial 3: Non-linear Search `_ -- How a non-linear search is used to fit a lens model. - -`Tutorial 4: Parameter Space And Priors `_ -- The Concepts of a parameter space and priors. - -`Tutorial 5: Realism and Complexity `_ +`Tutorial 3: Realism and Complexity `_ - Finding a balance between realism and complexity when composing and fitting a lens model. -`Tutorial 6: Dealing with Failure `_ +`Tutorial 4: Dealing with Failure `_ - What to do when PyAutoLens finds an inaccurate lens model. -`Tutorial 7: Linear Profiles `_ +`Tutorial 5: Linear Profiles `_ - Light profiles which capture complex morphologies in a reduced number of non-linear parameters. -`Tutorial 8: Masking and Positions `_ +`Tutorial 6: Masking and Positions `_ - How to mask and mark positions on your data to improve the lens model. -`Tutorial 9: Results `_ +`Tutorial 7: Results `_ - Overview of the results available after successfully fitting a lens model. -`Tutorial 10: Need for Speed `_ +`Tutorial 8: Need for Speed `_ - How to fit complex models whilst balancing efficiency and run-time. diff --git a/docs/installation/conda.rst b/docs/installation/conda.rst index 318b15d69..8a765e348 100644 --- a/docs/installation/conda.rst +++ b/docs/installation/conda.rst @@ -46,7 +46,7 @@ You may get warnings which state something like: .. code-block:: bash - ERROR: autoarray 2024.9.21.2 has requirement numpy<=1.22.1, but you'll have numpy 1.22.2 which is incompatible. + ERROR: autoarray 2025.1.18.7 has requirement numpy<=1.22.1, but you'll have numpy 1.22.2 which is incompatible. ERROR: numba 0.53.1 has requirement llvmlite<0.37,>=0.36.0rc1, but you'll have llvmlite 0.38.0 which is incompatible. If you see these messages, they do not mean that the installation has failed and the instructions below will @@ -105,7 +105,7 @@ For interferometer analysis there are two optional dependencies that must be ins .. code-block:: bash pip install pynufft - pip install pylops==1.11.1 + pip install pylops==2.3.1 **PyAutoLens** will run without these libraries and it is recommended that you only install them if you intend to do interferometer analysis. diff --git a/docs/installation/pip.rst b/docs/installation/pip.rst index f9a969825..05a9cf968 100644 --- a/docs/installation/pip.rst +++ b/docs/installation/pip.rst @@ -27,7 +27,7 @@ You may get warnings which state something like: .. code-block:: bash - ERROR: autoarray 2024.9.21.2 has requirement numpy<=1.22.1, but you'll have numpy 1.22.2 which is incompatible. + ERROR: autoarray 2025.1.18.7 has requirement numpy<=1.22.1, but you'll have numpy 1.22.2 which is incompatible. ERROR: numba 0.53.1 has requirement llvmlite<0.37,>=0.36.0rc1, but you'll have llvmlite 0.38.0 which is incompatible. If you see these messages, they do not mean that the installation has failed and the instructions below will @@ -86,7 +86,7 @@ For interferometer analysis there are two optional dependencies that must be ins .. code-block:: bash pip install pynufft - pip install pylops==1.11.1 + pip install pylops==2.3.1 **PyAutoLens** will run without these libraries and it is recommended that you only install them if you intend to do interferometer analysis. diff --git a/docs/installation/source.rst b/docs/installation/source.rst index e84390875..5af7b5692 100644 --- a/docs/installation/source.rst +++ b/docs/installation/source.rst @@ -59,7 +59,7 @@ For unit tests to pass you will also need the following optional requirements: .. code-block:: bash pip install pynufft - pip install pylops==1.11.1 + pip install pylops==2.3.1 If you are using a ``conda`` environment, add the source repository as follows: diff --git a/docs/overview/overview_1_start_here.rst b/docs/overview/overview_1_start_here.rst index d845e8aab..1b8103a56 100644 --- a/docs/overview/overview_1_start_here.rst +++ b/docs/overview/overview_1_start_here.rst @@ -361,7 +361,7 @@ object. exposure_time=300.0, background_sky_level=1.0, psf=al.Kernel2D.from_gaussian(shape_native=(11, 11), sigma=0.1, pixel_scales=0.05), - add_poisson_noise=True, + add_poisson_noise_to_data=True, ) Once we have a simulator, we can use it to create an imaging dataset which consists of an image, noise-map and diff --git a/eden.ini b/eden.ini index b800028e5..cedb5becb 100644 --- a/eden.ini +++ b/eden.ini @@ -1,3 +1,3 @@ -[eden] -name=autolens -prefix=al +[eden] +name=autolens +prefix=al diff --git a/optional_requirements.txt b/optional_requirements.txt index 524e88755..7f924b895 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,8 +1,8 @@ -numba -pylops>=1.10.0,<=2.3.1 -pynufft -zeus-mcmc==2.5.4 -getdist==1.4 -#jax>=0.4.13 -#jaxlib>=0.4.13 +numba +pylops>=1.10.0,<=2.3.1 +pynufft +zeus-mcmc==2.5.4 +getdist==1.4 +#jax>=0.4.13 +#jaxlib>=0.4.13 ultranest==4.3.2 \ No newline at end of file diff --git a/release.sh b/release.sh index fde2f648e..d5a6646d0 100755 --- a/release.sh +++ b/release.sh @@ -1,31 +1,31 @@ -#!/usr/bin/env bash - -export PACKAGE_NAME=autolens - -rm -rf $p/dist -rm -rf $p/build - -set -e - -export VERSION=$1 - -cat $PACKAGE_NAME/__init__.py | grep -v __version__ > temp - -cat temp > $PACKAGE_NAME/__init__.py -rm temp -echo "__version__ = '"$VERSION"'" >> $PACKAGE_NAME/__init__.py - -git add $PACKAGE_NAME/__init__.py - -set +e -git commit -m "Incremented version number" -set -e - -python3 setup.py sdist bdist_wheel -twine upload dist/* --skip-existing --username $PYPI_USERNAME --password $PYPI_PASSWORD - - -git push --tags - -rm -rf $p/dist -rm -rf $p/build +#!/usr/bin/env bash + +export PACKAGE_NAME=autolens + +rm -rf $p/dist +rm -rf $p/build + +set -e + +export VERSION=$1 + +cat $PACKAGE_NAME/__init__.py | grep -v __version__ > temp + +cat temp > $PACKAGE_NAME/__init__.py +rm temp +echo "__version__ = '"$VERSION"'" >> $PACKAGE_NAME/__init__.py + +git add $PACKAGE_NAME/__init__.py + +set +e +git commit -m "Incremented version number" +set -e + +python3 setup.py sdist bdist_wheel +twine upload dist/* --skip-existing --username $PYPI_USERNAME --password $PYPI_PASSWORD + + +git push --tags + +rm -rf $p/dist +rm -rf $p/build diff --git a/test_autolens/analysis/test_analysis.py b/test_autolens/analysis/test_analysis.py index 5444b88a3..93d4f1b2c 100644 --- a/test_autolens/analysis/test_analysis.py +++ b/test_autolens/analysis/test_analysis.py @@ -95,7 +95,7 @@ def test__use_border_relocator__determines_if_border_pixel_relocation_is_used( ) ) - masked_imaging_7x7.grids.uniform[4] = np.array([300.0, 0.0]) + masked_imaging_7x7.grids.lp.over_sampled[4] = np.array([300.0, 0.0]) analysis = al.AnalysisImaging( dataset=masked_imaging_7x7, @@ -105,7 +105,7 @@ def test__use_border_relocator__determines_if_border_pixel_relocation_is_used( instance = model.instance_from_unit_vector([]) fit = analysis.fit_from(instance=instance) - grid = fit.inversion.linear_obj_list[0].source_plane_data_grid + grid = fit.inversion.linear_obj_list[0].source_plane_data_grid.over_sampled assert grid[2] == pytest.approx([-82.99114877, 52.81254922], 1.0e-4) @@ -117,7 +117,7 @@ def test__use_border_relocator__determines_if_border_pixel_relocation_is_used( instance = model.instance_from_unit_vector([]) fit = analysis.fit_from(instance=instance) - grid = fit.inversion.linear_obj_list[0].source_plane_data_grid + grid = fit.inversion.linear_obj_list[0].source_plane_data_grid.over_sampled assert grid[2] == pytest.approx([-82.89544515, 52.7491249], 1.0e-4) diff --git a/test_autolens/analysis/test_plotter_interface.py b/test_autolens/analysis/test_plotter_interface.py index ce1622e89..27506324a 100644 --- a/test_autolens/analysis/test_plotter_interface.py +++ b/test_autolens/analysis/test_plotter_interface.py @@ -24,26 +24,16 @@ def test__tracer( plotter_interface.tracer( tracer=tracer_x2_plane_7x7, - grid=masked_imaging_7x7.grids.uniform, - during_analysis=False, + grid=masked_imaging_7x7.grids.lp, ) - plot_path = path.join(plot_path, "tracer") - assert path.join(plot_path, "subplot_galaxies_images.png") in plot_patch.paths - assert path.join(plot_path, "image_2d.png") in plot_patch.paths - assert path.join(plot_path, "plane_image_of_plane_1.png") in plot_patch.paths - assert path.join(plot_path, "convergence_2d.png") in plot_patch.paths - assert path.join(plot_path, "potential_2d.png") not in plot_patch.paths - assert path.join(plot_path, "deflections_y_2d.png") not in plot_patch.paths - assert path.join(plot_path, "deflections_x_2d.png") not in plot_patch.paths - assert path.join(plot_path, "magnification_2d.png") in plot_patch.paths - - convergence = al.util.array_2d.numpy_array_2d_via_fits_from( - file_path=path.join(plot_path, "fits", "convergence_2d.fits"), hdu=0 + + image = al.util.array_2d.numpy_array_2d_via_fits_from( + file_path=path.join(plot_path, "tracer.fits"), hdu=0 ) - assert convergence.shape == (7, 7) + assert image.shape == (5, 5) def test__image_with_positions( @@ -56,6 +46,4 @@ def test__image_with_positions( plotter_interface.image_with_positions(image=image_7x7, positions=positions_x2) - plot_path = path.join(plot_path, "positions") - assert path.join(plot_path, "image_with_positions.png") in plot_patch.paths diff --git a/test_autolens/analysis/test_preloads.py b/test_autolens/analysis/test_preloads.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_autolens/analysis/test_result.py b/test_autolens/analysis/test_result.py index 50809b671..34917ccfc 100644 --- a/test_autolens/analysis/test_result.py +++ b/test_autolens/analysis/test_result.py @@ -227,7 +227,7 @@ def test__image_plane_multiple_image_positions(analysis_imaging_7x7): multiple_images = result.image_plane_multiple_image_positions - assert pytest.approx((0.968719, 0.366210), 1.0e-4) in multiple_images.in_list + assert pytest.approx((0.968719, 0.366210), 1.0e-2) in multiple_images.in_list def test__positions_threshold_from(analysis_imaging_7x7): @@ -247,9 +247,9 @@ def test__positions_threshold_from(analysis_imaging_7x7): result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) - assert result.positions_threshold_from() == pytest.approx(1.1001488121, 1.0e-4) + assert result.positions_threshold_from() == pytest.approx(0.930414842576, 1.0e-4) assert result.positions_threshold_from(factor=5.0) == pytest.approx( - 5.5007440609, 1.0e-4 + 4.652074212, 1.0e-4 ) assert result.positions_threshold_from(minimum_threshold=10.0) == pytest.approx( 10.0, 1.0e-4 @@ -291,6 +291,39 @@ def test__positions_likelihood_from(analysis_imaging_7x7): assert positions_likelihood.threshold == pytest.approx(0.2, 1.0e-4) +def test__positions_likelihood_from__mass_centre_radial_distance_min( + analysis_imaging_7x7, +): + tracer = al.Tracer( + galaxies=[ + al.Galaxy( + redshift=0.5, + mass=al.mp.Isothermal( + centre=(0.1, 0.0), einstein_radius=1.0, ell_comps=(0.0, 0.0) + ), + ), + al.Galaxy(redshift=1.0, bulge=al.lp.SersicSph(centre=(0.0, 0.0))), + ] + ) + + samples_summary = al.m.MockSamplesSummary(max_log_likelihood_instance=tracer) + + result = res.Result(samples_summary=samples_summary, analysis=analysis_imaging_7x7) + + positions_likelihood = result.positions_likelihood_from( + factor=0.1, minimum_threshold=0.2, mass_centre_radial_distance_min=0.1 + ) + + assert isinstance(positions_likelihood, al.PositionsLHPenalty) + assert len(positions_likelihood.positions) == 2 + assert positions_likelihood.positions[0] == pytest.approx( + (-1.00097656e00, 5.63818622e-04), 1.0e-4 + ) + assert positions_likelihood.positions[1] == pytest.approx( + (1.00097656e00, -5.63818622e-04), 1.0e-4 + ) + + def test__results_include_mask__available_as_property( analysis_imaging_7x7, masked_imaging_7x7, samples_summary_with_result ): diff --git a/test_autolens/config/general.yaml b/test_autolens/config/general.yaml index 58138096d..032245c00 100644 --- a/test_autolens/config/general.yaml +++ b/test_autolens/config/general.yaml @@ -1,40 +1,40 @@ -analysis: - n_cores: 1 -fits: - flip_for_ds9: true -grid: - remove_projected_centre: false -hpc: - hpc_mode: false - iterations_per_update: 5000 -adapt: - adapt_minimum_percent: 0.01 - adapt_noise_limit: 100000000.0 -inversion: - check_reconstruction: false # If True, the inversion's reconstruction is checked to ensure the solution of a meshs's mapper is not an invalid solution where the values are all the same. - use_positive_only_solver: false # If True, inversion's use a positive-only linear algebra solver by default, which is slower but prevents unphysical negative values in the reconstructed solutuion. - no_regularization_add_to_curvature_diag_value: 1.0e-8 # The default value added to the curvature matrix's diagonal when regularization is not applied to a linear object, which prevents inversion's failing due to the matrix being singular. - positive_only_uses_p_initial: false # If True, the positive-only solver of an inversion's uses an initial guess of the reconstructed data's values as which values should be positive, speeding up the solver. -model: - ignore_prior_limits: true -numba: - cache: true - nopython: true - parallel: false - use_numba: true -output: - force_pickle_overwrite: false - info_whitespace_length: 80 - log_file: output.log - log_level: INFO - log_to_file: false - model_results_decimal_places: 3 - remove_files: false - samples_to_csv: false -profiling: - perform: true - repeats: 1 -test: - 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. - exception_override: false - disable_positions_lh_inversion_check: false +analysis: + n_cores: 1 +fits: + flip_for_ds9: true +grid: + remove_projected_centre: false +hpc: + hpc_mode: false + iterations_per_update: 5000 +adapt: + adapt_minimum_percent: 0.01 + adapt_noise_limit: 100000000.0 +inversion: + check_reconstruction: false # If True, the inversion's reconstruction is checked to ensure the solution of a meshs's mapper is not an invalid solution where the values are all the same. + use_positive_only_solver: false # If True, inversion's use a positive-only linear algebra solver by default, which is slower but prevents unphysical negative values in the reconstructed solutuion. + no_regularization_add_to_curvature_diag_value: 1.0e-8 # The default value added to the curvature matrix's diagonal when regularization is not applied to a linear object, which prevents inversion's failing due to the matrix being singular. + positive_only_uses_p_initial: false # If True, the positive-only solver of an inversion's uses an initial guess of the reconstructed data's values as which values should be positive, speeding up the solver. +model: + ignore_prior_limits: true +numba: + cache: true + nopython: true + parallel: false + use_numba: true +output: + force_pickle_overwrite: false + info_whitespace_length: 80 + log_file: output.log + log_level: INFO + log_to_file: false + model_results_decimal_places: 3 + remove_files: false + samples_to_csv: false +profiling: + perform: true + repeats: 1 +test: + 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. + exception_override: false + disable_positions_lh_inversion_check: false diff --git a/test_autolens/config/grids.yaml b/test_autolens/config/grids.yaml index 83db40ff5..06d81535e 100644 --- a/test_autolens/config/grids.yaml +++ b/test_autolens/config/grids.yaml @@ -1,217 +1,65 @@ -interpolate: - convergence_2d_from: - Isothermal: false - IsothermalInitialize: false - IsothermalSph: true - deflections_yx_2d_from: - Isothermal: false - IsothermalInitialize: false - IsothermalSph: true - image_2d_from: - Sersic: false - SersicInitialize: false - SersicSph: true - potential_2d_from: - Isothermal: false - IsothermalInitialize: false - IsothermalSph: true -# Certain light and mass profile calculations become ill defined at (0.0, 0.0) or close to this value. This can lead -# to numerical issues in the calculation of the profile, for example a np.nan may arise, crashing the code. - -# To avoid this, we set a minimum value for the radial coordinate of the profile. If the radial coordinate is below -# this value, it is rounded up to this value. This ensures that the profile cannot receive a radial coordinate of 0.0. - -# For example, if an input grid coordinate has a radial coordinate of 1e-12, for most profiles this will be rounded up -# to radial_minimum=1e-08. This is a small enough value that it should not impact the results of the profile calculation. - -radial_minimum: - radial_minimum: - DevVaucouleurs: 1.0e-08 - DevVaucouleursSph: 1.0e-08 - EllMassProfile: 1.0e-08 - EllProfile: 1.0e-08 - Exponential: 1.0e-08 - ExponentialSph: 1.0e-08 - ExternalShear: 1.0e-08 - Gaussian: 1.0e-08 - GaussianGradient: 1.0e-08 - GaussianSph: 1.0e-08 - Isothermal: 1.0e-08 - IsothermalCore: 1.0e-08 - IsothermalCoreSph: 1.0e-08 - IsothermalInitialize: 1.0e-08 - IsothermalSph: 1.0e-08 - MassSheet: 1.0e-08 - MockGridRadialMinimum: 2.5 - NFW: 1.0e-08 - NFWSph: 1.0e-08 - NFWTruncatedSph: 1.0e-08 - PointMass: 0.0 - PowerLaw: 1.0e-08 - PowerLawBroken: 1.0e-08 - PowerLawBrokenSph: 1.0e-08 - PowerLawCore: 1.0e-08 - PowerLawCoreSph: 1.0e-08 - PowerLawSph: 1.0e-08 - Sersic: 1.0e-08 - SersicCore: 1.0e-08 - SersicCoreSph: 1.0e-08 - SersicGradient: 1.0e-08 - SersicGradientSph: 1.0e-08 - SersicSph: 1.0e-08 - SphNFWTruncatedMCR: 1.0e-08 - gNFW: 1.0e-08 - gNFWSph: 1.0e-08 - - -# Over sampling is an important numerical technique, whereby light profiles images are evaluated on a higher resolution -# grid than the image data to ensure the calculation is accurate. - -# By default, a user does not specify the over sampling factor, and a default over sampling scheme is used for each -# profile. This scheme first goes to the centre of the profile, and computes circles with certain radial values -# (e.g. radii). It then assigns an over sampling `sub_size` to each circle, where the central circles have the highest -# over sampling factor and the outer circles have the lowest. - -# The size of the circles that are appropriate for determining the over sampling factor are dependent on the resolution -# of the grid. For a high resolution grid (e.g. low pixel scale), a smaller circle central circle is necessary to -# over sample the profile accurately. The config file below therefore specifies the "radial factors" used for -# automatically determining the over sampling factors for each profile, which is the factor the pixel scale is multiplied -# by to determine the circle size. - -# The config entry below defines the default over sampling factor for each profile, where: - -# radial_factor_list: The factors that are multiplied by the pixel scale to determine the circle size that is used. -# sub_size_list: The over sampling factor that is used for each circle size. - -# For the default entries below, oversampling of degree 32 x 32 is used within a circle of radius 3.01 x pixel scale, -# 4 x 4 within a circle of radius 10.01 x pixel scale and 2 x 2 for all pixels outside of this radius. - -over_sampling: - radial_factor_list: - Chameleon: [1.0] - ChameleonSph: [1.0] - DevVaucouleurs: [1.0] - DevVaucouleursSph: [1.0] - dPIE: [1.0] - dPIESph: [1.0] - ExponentialGradient: [1.0] - ExponentialGradientSph: [1.0] - ElsonFreeFall: [1.0] - ElsonFreeFallSph: [1.0] - Exponential: [1.0] - ExponentialCore: [1.0] - ExponentialCoreSph: [1.0] - ExponentialSph: [1.0] - ExternalShear: [1.0] - Gaussian: [1.0] - GaussianSph: [1.0] - gNFW: [1.0] - gNFWMCRLudlow: [1.0] - gNFWVirialMassConcSph: [1.0] - gNFWSph: [1.0] - Isothermal: [1.0] - IsothermalCore: [1.0] - IsothermalCoreSph: [1.0] - IsothermalSph: [1.0] - MassSheet: [1.0] - Moffat: [1.0] - MoffatSph: [1.0] - PowerLawMultipole: [1.0] - NFW: [1.0] - NFWMCRDuffySph: [1.0] - NFWMCRLudlow: [1.0] - NFWMCRLudlowSph: [1.0] - NFWMCRScatterLudlow: [1.0] - NFWMCRScatterLudlowSph: [1.0] - NFWVirialMassConcSph: [1.0] - NFWSph: [1.0] - NFWTruncatedMCRDuffySph: [1.0] - NFWTruncatedMCRLudlowSph: [1.0] - NFWTruncatedMCRScatterLudlowSph: [1.0] - NFWTruncatedSph: [1.0] - PointMass: [1.0] - PowerLaw: [1.0] - PowerLawBroken: [1.0] - PowerLawBrokenSph: [1.0] - PowerLawCore: [1.0] - PowerLawCoreSph: [1.0] - PowerLawSph: [1.0] - Sersic: [1.0] - SersicCore: [1.0] - SersicCoreSph: [1.0] - SersicGradient: [1.0] - SersicSph: [1.0] - SersicGradientSph: [1.0] - ShapeletCartesianSph: [1.0] - ShapeletCartesian: [1.0] - ShapeletPolarSph: [1.0] - ShapeletPolar: [1.0] - ShapeletExponentialSph: [1.0] - ShapeletExponential: [1.0] - SMBH: [1.0] - SMBHBinary: [1.0] - EllProfile: [1.0] - sub_size_list: - Chameleon: [1, 1] - ChameleonSph: [1, 1] - DevVaucouleurs: [1, 1] - DevVaucouleursSph: [1, 1] - dPIE: [1, 1] - dPIESph: [1, 1] - ExponentialGradient: [1, 1] - ExponentialGradientSph: [1, 1] - ElsonFreeFall: [1, 1] - ElsonFreeFallSph: [1, 1] - Exponential: [1, 1] - ExponentialCore: [1, 1] - ExponentialCoreSph: [1, 1] - ExponentialSph: [1, 1] - ExternalShear: [1, 1] - Gaussian: [1, 1] - GaussianSph: [1, 1] - gNFW: [1, 1] - gNFWMCRLudlow: [1, 1] - gNFWVirialMassConcSph: [1, 1] - gNFWSph: [1, 1] - Isothermal: [1, 1] - IsothermalCore: [1, 1] - IsothermalCoreSph: [1, 1] - IsothermalSph: [1, 1] - MassSheet: [1, 1] - Moffat: [1, 1] - MoffatSph: [1, 1] - PowerLawMultipole: [1, 1] - NFW: [1, 1] - NFWMCRDuffySph: [1, 1] - NFWMCRLudlow: [1, 1] - NFWMCRLudlowSph: [1, 1] - NFWMCRScatterLudlow: [1, 1] - NFWMCRScatterLudlowSph: [1, 1] - NFWVirialMassConcSph : [1, 1] - NFWSph: [1, 1] - NFWTruncatedMCRDuffySph: [1, 1] - NFWTruncatedMCRLudlowSph: [1, 1] - NFWTruncatedMCRScatterLudlowSph: [1, 1] - NFWTruncatedSph: [1, 1] - PointMass: [1, 1] - PowerLaw: [1, 1] - PowerLawBroken: [1, 1] - PowerLawBrokenSph: [1, 1] - PowerLawCore: [1, 1] - PowerLawCoreSph: [1, 1] - PowerLawSph: [1, 1] - Sersic: [1, 1] - SersicCore: [1, 1] - SersicCoreSph: [1, 1] - SersicGradient: [1, 1] - SersicSph: [1, 1] - SersicGradientSph: [1, 1] - ShapeletCartesianSph: [1, 1] - ShapeletCartesian: [1, 1] - ShapeletPolarSph: [1, 1] - ShapeletPolar: [1, 1] - ShapeletExponentialSph: [1, 1] - ShapeletExponential: [1, 1] - SMBH: [1, 1] - SMBHBinary: [1, 1] - EllProfile: [1, 1] \ No newline at end of file +interpolate: + convergence_2d_from: + Isothermal: false + IsothermalInitialize: false + IsothermalSph: true + deflections_yx_2d_from: + Isothermal: false + IsothermalInitialize: false + IsothermalSph: true + image_2d_from: + Sersic: false + SersicInitialize: false + SersicSph: true + potential_2d_from: + Isothermal: false + IsothermalInitialize: false + IsothermalSph: true +# Certain light and mass profile calculations become ill defined at (0.0, 0.0) or close to this value. This can lead +# to numerical issues in the calculation of the profile, for example a np.nan may arise, crashing the code. + +# To avoid this, we set a minimum value for the radial coordinate of the profile. If the radial coordinate is below +# this value, it is rounded up to this value. This ensures that the profile cannot receive a radial coordinate of 0.0. + +# For example, if an input grid coordinate has a radial coordinate of 1e-12, for most profiles this will be rounded up +# to radial_minimum=1e-08. This is a small enough value that it should not impact the results of the profile calculation. + +radial_minimum: + radial_minimum: + DevVaucouleurs: 1.0e-08 + DevVaucouleursSph: 1.0e-08 + EllMassProfile: 1.0e-08 + EllProfile: 1.0e-08 + Exponential: 1.0e-08 + ExponentialSph: 1.0e-08 + ExternalShear: 1.0e-08 + Gaussian: 1.0e-08 + GaussianGradient: 1.0e-08 + GaussianSph: 1.0e-08 + Isothermal: 1.0e-08 + IsothermalCore: 1.0e-08 + IsothermalCoreSph: 1.0e-08 + IsothermalInitialize: 1.0e-08 + IsothermalSph: 1.0e-08 + MassSheet: 1.0e-08 + MockGridRadialMinimum: 2.5 + NFW: 1.0e-08 + NFWSph: 1.0e-08 + NFWTruncatedSph: 1.0e-08 + PointMass: 0.0 + PowerLaw: 1.0e-08 + PowerLawBroken: 1.0e-08 + PowerLawBrokenSph: 1.0e-08 + PowerLawCore: 1.0e-08 + PowerLawCoreSph: 1.0e-08 + PowerLawSph: 1.0e-08 + Sersic: 1.0e-08 + SersicCore: 1.0e-08 + SersicCoreSph: 1.0e-08 + SersicGradient: 1.0e-08 + SersicGradientSph: 1.0e-08 + SersicSph: 1.0e-08 + SphNFWTruncatedMCR: 1.0e-08 + gNFW: 1.0e-08 + gNFWSph: 1.0e-08 + diff --git a/test_autolens/config/non_linear.yaml b/test_autolens/config/non_linear.yaml index 2dcf909de..3fb607c33 100644 --- a/test_autolens/config/non_linear.yaml +++ b/test_autolens/config/non_linear.yaml @@ -1,83 +1,83 @@ -mock: - MockOptimizer: - initialize: - method: prior - printing: - silence: false - updates: - iterations_per_update: 2500 - remove_state_files_at_end: true - MockSearch: - initialize: - method: prior - printing: - silence: false - search: {} - updates: - iterations_per_update: 2500 - remove_state_files_at_end: true -nest: - DynestyDynamic: - general: - acceptance_ratio_threshold: 0.1 - bootstrap: null - bound: multi - enlarge: null - first_update: null - fmove: 0.9 - max_move: 100 - sample: auto - sampling_efficiency: 0.5 - slices: 5 - terminate_at_acceptance_ratio: false - update_interval: null - walks: 25 - initialize: - method: prior - parallel: - force_x1_cpu: false - number_of_cores: 1 - printing: - silence: false - updates: - iterations_per_update: 2500 - remove_state_files_at_end: true - DynestyStatic: - parallel: - number_of_cores: 1 - initialize: - method: prior - inversion: - acceptance_ratio_threshold: 0.05 - const_efficiency_mode: true - evidence_tolerance: 100.0 - multimodal: false - n_live_points: 50 - sampling_efficiency: 0.3 - terminate_at_acceptance_ratio: false - printing: - silence: false - search: - const_efficiency_mode: false - evidence_tolerance: 0.5 - importance_nested_sampling: false - max_iter: 0 - max_modes: 100 - mode_tolerance: -1.0e+90 - multimodal: false - n_live_points: 50 - sampling_efficiency: 0.5 - settings: - context: 0 - init_MPI: false - log_zero: -1.0e+100 - n_iter_before_update: 5 - null_log_evidence: -1.0e+90 - resume: true - seed: -1.0 - stagger_resampling_likelihood: true - verbose: false - write_output: true - updates: - iterations_per_update: 2500 - remove_state_files_at_end: true +mock: + MockOptimizer: + initialize: + method: prior + printing: + silence: false + updates: + iterations_per_update: 2500 + remove_state_files_at_end: true + MockSearch: + initialize: + method: prior + printing: + silence: false + search: {} + updates: + iterations_per_update: 2500 + remove_state_files_at_end: true +nest: + DynestyDynamic: + general: + acceptance_ratio_threshold: 0.1 + bootstrap: null + bound: multi + enlarge: null + first_update: null + fmove: 0.9 + max_move: 100 + sample: auto + sampling_efficiency: 0.5 + slices: 5 + terminate_at_acceptance_ratio: false + update_interval: null + walks: 25 + initialize: + method: prior + parallel: + force_x1_cpu: false + number_of_cores: 1 + printing: + silence: false + updates: + iterations_per_update: 2500 + remove_state_files_at_end: true + DynestyStatic: + parallel: + number_of_cores: 1 + initialize: + method: prior + inversion: + acceptance_ratio_threshold: 0.05 + const_efficiency_mode: true + evidence_tolerance: 100.0 + multimodal: false + n_live_points: 50 + sampling_efficiency: 0.3 + terminate_at_acceptance_ratio: false + printing: + silence: false + search: + const_efficiency_mode: false + evidence_tolerance: 0.5 + importance_nested_sampling: false + max_iter: 0 + max_modes: 100 + mode_tolerance: -1.0e+90 + multimodal: false + n_live_points: 50 + sampling_efficiency: 0.5 + settings: + context: 0 + init_MPI: false + log_zero: -1.0e+100 + n_iter_before_update: 5 + null_log_evidence: -1.0e+90 + resume: true + seed: -1.0 + stagger_resampling_likelihood: true + verbose: false + write_output: true + updates: + iterations_per_update: 2500 + remove_state_files_at_end: true diff --git a/test_autolens/config/notation.yaml b/test_autolens/config/notation.yaml index 8cc3102ef..2d19ca16d 100644 --- a/test_autolens/config/notation.yaml +++ b/test_autolens/config/notation.yaml @@ -1,84 +1,84 @@ -label: - label: - alpha: \alpha - angle_binary: \theta - beta: \beta - break_radius: \theta_{\rm B} - centre_0: y - centre_1: x - coefficient: \lambda - contribution_factor: \omega_{\rm 0} - core_radius: C_{\rm r} - core_radius_0: C_{rm r0} - core_radius_1: C_{\rm r1} - effective_radius: R_{\rm eff} - einstein_radius: \theta_{\rm Ein} - ell_comps_0: \epsilon_{\rm 1} - ell_comps_1: \epsilon_{\rm 2} - multipole_comps_0: M_{\rm 1} - multipole_comps_1: M_{\rm 2} - flux: F - gamma: \gamma - gamma_1: \gamma - gamma_2: \gamma - inner_coefficient: \lambda_{\rm 1} - inner_slope: t_{\rm 1} - intensity: I_{\rm b} - kappa: \kappa - kappa_s: \kappa_{\rm s} - log10m_vir: log_{\rm 10}(m_{vir}) - m: m - mass: M - mass_at_200: M_{\rm 200} - mass_ratio: M_{\rm ratio} - mass_to_light_gradient: \Gamma - mass_to_light_ratio: \Psi - mass_to_light_ratio_base: \Psi_{\rm base} - mass_to_light_radius: R_{\rm ref} - noise_factor: \omega_{\rm 1} - noise_power: \omega{\rm 2} - noise_scale: \sigma_{\rm 1} - normalization_scale: n - outer_coefficient: \lambda_{\rm 2} - outer_slope: t_{\rm 2} - overdens: \Delta_{\rm vir} - pixels: N_{\rm pix} - radius_break: R_{\rm b} - redshift: z - redshift_object: z_{\rm obj} - redshift_source: z_{\rm src} - scale_radius: R_{\rm s} - scatter: \sigma - separation: s - sersic_index: n - shape_0: y_{\rm pix} - shape_1: x_{\rm pix} - sigma: \sigma - signal_scale: V - sky_scale: \sigma_{\rm 0} - slope: \gamma - truncation_radius: R_{\rm t} - weight_floor: W_{\rm f} - weight_power: W_{\rm p} - superscript: - ExternalShear: ext - InputDeflections: defl - Pixelization: pix - Point: point - Redshift: '' - Regularization: reg -label_format: - format: - angular_diameter_distance_to_earth: '{:.2f}' - concentration: '{:.2f}' - einstein_mass: '{:.4e}' - einstein_radius: '{:.2f}' - kpc_per_arcsec: '{:.2f}' - luminosity: '{:.4e}' - m: '{:.1f}' - mass: '{:.4e}' - mass_at_truncation_radius: '{:.4e}' - radius: '{:.2f}' - redshift: '{:.2f}' - rho: '{:.2f}' - sersic_luminosity: '{:.4e}' +label: + label: + alpha: \alpha + angle_binary: \theta + beta: \beta + break_radius: \theta_{\rm B} + centre_0: y + centre_1: x + coefficient: \lambda + contribution_factor: \omega_{\rm 0} + core_radius: C_{\rm r} + core_radius_0: C_{rm r0} + core_radius_1: C_{\rm r1} + effective_radius: R_{\rm eff} + einstein_radius: \theta_{\rm Ein} + ell_comps_0: \epsilon_{\rm 1} + ell_comps_1: \epsilon_{\rm 2} + multipole_comps_0: M_{\rm 1} + multipole_comps_1: M_{\rm 2} + flux: F + gamma: \gamma + gamma_1: \gamma + gamma_2: \gamma + inner_coefficient: \lambda_{\rm 1} + inner_slope: t_{\rm 1} + intensity: I_{\rm b} + kappa: \kappa + kappa_s: \kappa_{\rm s} + log10m_vir: log_{\rm 10}(m_{vir}) + m: m + mass: M + mass_at_200: M_{\rm 200} + mass_ratio: M_{\rm ratio} + mass_to_light_gradient: \Gamma + mass_to_light_ratio: \Psi + mass_to_light_ratio_base: \Psi_{\rm base} + mass_to_light_radius: R_{\rm ref} + noise_factor: \omega_{\rm 1} + noise_power: \omega{\rm 2} + noise_scale: \sigma_{\rm 1} + normalization_scale: n + outer_coefficient: \lambda_{\rm 2} + outer_slope: t_{\rm 2} + overdens: \Delta_{\rm vir} + pixels: N_{\rm pix} + radius_break: R_{\rm b} + redshift: z + redshift_object: z_{\rm obj} + redshift_source: z_{\rm src} + scale_radius: R_{\rm s} + scatter: \sigma + separation: s + sersic_index: n + shape_0: y_{\rm pix} + shape_1: x_{\rm pix} + sigma: \sigma + signal_scale: V + sky_scale: \sigma_{\rm 0} + slope: \gamma + truncation_radius: R_{\rm t} + weight_floor: W_{\rm f} + weight_power: W_{\rm p} + superscript: + ExternalShear: ext + InputDeflections: defl + Pixelization: pix + Point: point + Redshift: '' + Regularization: reg +label_format: + format: + angular_diameter_distance_to_earth: '{:.2f}' + concentration: '{:.2f}' + einstein_mass: '{:.4e}' + einstein_radius: '{:.2f}' + kpc_per_arcsec: '{:.2f}' + luminosity: '{:.4e}' + m: '{:.1f}' + mass: '{:.4e}' + mass_at_truncation_radius: '{:.4e}' + radius: '{:.2f}' + redshift: '{:.2f}' + rho: '{:.2f}' + sersic_luminosity: '{:.4e}' diff --git a/test_autolens/config/output.yaml b/test_autolens/config/output.yaml index 5cb8812dc..4a44b384b 100644 --- a/test_autolens/config/output.yaml +++ b/test_autolens/config/output.yaml @@ -1,63 +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. - +# 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/config/priors/dark_mass_profiles.yaml b/test_autolens/config/priors/dark_mass_profiles.yaml index 713a4856d..61480a243 100644 --- a/test_autolens/config/priors/dark_mass_profiles.yaml +++ b/test_autolens/config/priors/dark_mass_profiles.yaml @@ -1,338 +1,338 @@ -NFW: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - kappa_s: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Relative - value: 0.2 - scale_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 0.2 -NFWSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - kappa_s: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Relative - value: 0.2 - scale_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 0.2 -NFWTruncatedSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - kappa_s: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Relative - value: 0.2 - scale_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 0.2 - truncation_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 0.2 -SphNFWTruncatedMCR: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - mass_at_200: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 100000000.0 - type: LogUniform - upper_limit: 1000000000000000.0 - width_modifier: - type: Relative - value: 0.5 -gNFW: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - inner_slope: - gaussian_limits: - lower: -1.0 - upper: 3.0 - lower_limit: 0.0 - type: Uniform - upper_limit: 2.0 - width_modifier: - type: Absolute - value: 0.3 - kappa_s: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Relative - value: 0.2 - scale_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 0.2 -gNFWSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - inner_slope: - gaussian_limits: - lower: -1.0 - upper: 3.0 - lower_limit: 0.0 - type: Uniform - upper_limit: 2.0 - width_modifier: - type: Absolute - value: 0.3 - kappa_s: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Relative - value: 0.2 - scale_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 0.2 +NFW: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + kappa_s: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Relative + value: 0.2 + scale_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 0.2 +NFWSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + kappa_s: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Relative + value: 0.2 + scale_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 0.2 +NFWTruncatedSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + kappa_s: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Relative + value: 0.2 + scale_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 0.2 + truncation_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 0.2 +SphNFWTruncatedMCR: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + mass_at_200: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 100000000.0 + type: LogUniform + upper_limit: 1000000000000000.0 + width_modifier: + type: Relative + value: 0.5 +gNFW: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + inner_slope: + gaussian_limits: + lower: -1.0 + upper: 3.0 + lower_limit: 0.0 + type: Uniform + upper_limit: 2.0 + width_modifier: + type: Absolute + value: 0.3 + kappa_s: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Relative + value: 0.2 + scale_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 0.2 +gNFWSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + inner_slope: + gaussian_limits: + lower: -1.0 + upper: 3.0 + lower_limit: 0.0 + type: Uniform + upper_limit: 2.0 + width_modifier: + type: Absolute + value: 0.3 + kappa_s: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Relative + value: 0.2 + scale_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 0.2 diff --git a/test_autolens/config/priors/galaxy.yaml b/test_autolens/config/priors/galaxy.yaml index a0af83928..37d54b710 100644 --- a/test_autolens/config/priors/galaxy.yaml +++ b/test_autolens/config/priors/galaxy.yaml @@ -1,17 +1,17 @@ -Galaxy: - redshift: - lower_limit: 0.0 - type: Uniform - upper_limit: 3.0 - -Redshift: - redshift: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 3.0 - width_modifier: - type: Absolute - value: 1.0 +Galaxy: + redshift: + lower_limit: 0.0 + type: Uniform + upper_limit: 3.0 + +Redshift: + redshift: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 3.0 + width_modifier: + type: Absolute + value: 1.0 diff --git a/test_autolens/config/priors/geometry_profiles.yaml b/test_autolens/config/priors/geometry_profiles.yaml index b55820bc4..2cc1e251b 100644 --- a/test_autolens/config/priors/geometry_profiles.yaml +++ b/test_autolens/config/priors/geometry_profiles.yaml @@ -1,48 +1,48 @@ -AbstractSersic: - angle: - lower_limit: 0.0 - type: Uniform - upper_limit: 180.0 - axis_ratio: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - centre_0: - lower_limit: -1.0 - type: Uniform - upper_limit: 1.0 - centre_1: - lower_limit: -1.0 - type: Uniform - upper_limit: 1.0 - effective_radius: - lower_limit: 0.0 - type: Uniform - upper_limit: 3.0 - intensity: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - sersic_index: - lower_limit: 0.6 - type: Uniform - upper_limit: 8.0 -EllProfile: - angle: - lower_limit: 0.0 - type: Uniform - upper_limit: 2.0 - axis_ratio: - lower_limit: 0.0 - type: Uniform - upper_limit: 2.0 -GeometryProfile: - centre_0: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - centre_1: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 -SphlProfile: {} +AbstractSersic: + angle: + lower_limit: 0.0 + type: Uniform + upper_limit: 180.0 + axis_ratio: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + centre_0: + lower_limit: -1.0 + type: Uniform + upper_limit: 1.0 + centre_1: + lower_limit: -1.0 + type: Uniform + upper_limit: 1.0 + effective_radius: + lower_limit: 0.0 + type: Uniform + upper_limit: 3.0 + intensity: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + sersic_index: + lower_limit: 0.6 + type: Uniform + upper_limit: 8.0 +EllProfile: + angle: + lower_limit: 0.0 + type: Uniform + upper_limit: 2.0 + axis_ratio: + lower_limit: 0.0 + type: Uniform + upper_limit: 2.0 +GeometryProfile: + centre_0: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + centre_1: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 +SphlProfile: {} diff --git a/test_autolens/config/priors/light_profiles.yaml b/test_autolens/config/priors/light_profiles.yaml index ee1f07831..830e1fc8f 100644 --- a/test_autolens/config/priors/light_profiles.yaml +++ b/test_autolens/config/priors/light_profiles.yaml @@ -1,266 +1,266 @@ -EllLightProfile: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 0.5 - width_modifier: - type: Absolute - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - lower_limit: 0.0 - mean: 0.0 - sigma: 0.5 - type: Gaussian - upper_limit: inf - width_modifier: - type: Relative - value: 0.5 -Exponential: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 0.5 - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 2.0 - width_modifier: - type: Absolute - value: 2.0 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - lower_limit: 0.0 - mean: 0.0 - sigma: 0.5 - type: Gaussian - upper_limit: inf - width_modifier: - type: Relative - value: 0.5 -Gaussian: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000000.0 - width_modifier: - type: Relative - value: 0.5 - sigma: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 25.0 - width_modifier: - type: Relative - value: 0.5 -Sersic: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000000.0 - width_modifier: - type: Relative - value: 0.5 - sersic_index: - gaussian_limits: - lower: 0.5 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 8.0 - width_modifier: - type: Absolute - value: 1.5 +EllLightProfile: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 0.5 + width_modifier: + type: Absolute + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + lower_limit: 0.0 + mean: 0.0 + sigma: 0.5 + type: Gaussian + upper_limit: inf + width_modifier: + type: Relative + value: 0.5 +Exponential: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 0.5 + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 2.0 + width_modifier: + type: Absolute + value: 2.0 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + lower_limit: 0.0 + mean: 0.0 + sigma: 0.5 + type: Gaussian + upper_limit: inf + width_modifier: + type: Relative + value: 0.5 +Gaussian: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000000.0 + width_modifier: + type: Relative + value: 0.5 + sigma: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 25.0 + width_modifier: + type: Relative + value: 0.5 +Sersic: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000000.0 + width_modifier: + type: Relative + value: 0.5 + sersic_index: + gaussian_limits: + lower: 0.5 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 8.0 + width_modifier: + type: Absolute + value: 1.5 diff --git a/test_autolens/config/priors/mass_sheets.yaml b/test_autolens/config/priors/mass_sheets.yaml index 82551b7cb..6efda8397 100644 --- a/test_autolens/config/priors/mass_sheets.yaml +++ b/test_autolens/config/priors/mass_sheets.yaml @@ -1,21 +1,21 @@ -ExternalShear: - gamma_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -0.2 - type: Uniform - upper_limit: 0.2 - width_modifier: - type: Absolute - value: 0.05 - gamma_2: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -0.2 - type: Uniform - upper_limit: 0.2 - width_modifier: - type: Absolute - value: 0.05 +ExternalShear: + gamma_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -0.2 + type: Uniform + upper_limit: 0.2 + width_modifier: + type: Absolute + value: 0.05 + gamma_2: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -0.2 + type: Uniform + upper_limit: 0.2 + width_modifier: + type: Absolute + value: 0.05 diff --git a/test_autolens/config/priors/pixelizations.yaml b/test_autolens/config/priors/pixelizations.yaml index dde440f40..7d8084ed8 100644 --- a/test_autolens/config/priors/pixelizations.yaml +++ b/test_autolens/config/priors/pixelizations.yaml @@ -1,125 +1,125 @@ -delaunay.DelaunayBrightnessImage: - pixels: - gaussian_limits: - lower: 50.0 - upper: inf - lower_limit: 50.0 - type: Uniform - upper_limit: 2500.0 - width_modifier: - type: Absolute - value: 100.0 - weight_floor: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.1 - weight_power: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 20.0 - width_modifier: - type: Absolute - value: 5.0 -delaunay.DelaunayMagnification: - shape_0: - gaussian_limits: - lower: 3.0 - upper: inf - lower_limit: 20.0 - type: Uniform - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 - shape_1: - gaussian_limits: - lower: 3.0 - upper: inf - lower_limit: 20.0 - type: Uniform - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 -rectangular.Rectangular: - shape_0: - gaussian_limits: - lower: 3.0 - upper: inf - lower_limit: 20.0 - type: Uniform - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 - shape_1: - gaussian_limits: - lower: 3.0 - upper: inf - lower_limit: 20.0 - type: Uniform - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 -voronoi.VoronoiBrightnessImage: - pixels: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 50.0 - type: Uniform - upper_limit: 1500.0 - width_modifier: - type: Absolute - value: 400.0 - weight_floor: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.1 - weight_power: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 20.0 - width_modifier: - type: Absolute - value: 5.0 -voronoi.VoronoiMagnification: - shape_0: - gaussian_limits: - lower: 3.0 - upper: inf - lower_limit: 20.0 - type: Uniform - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 - shape_1: - gaussian_limits: - lower: 3.0 - upper: inf - lower_limit: 20.0 - type: Uniform - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 +delaunay.DelaunayBrightnessImage: + pixels: + gaussian_limits: + lower: 50.0 + upper: inf + lower_limit: 50.0 + type: Uniform + upper_limit: 2500.0 + width_modifier: + type: Absolute + value: 100.0 + weight_floor: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.1 + weight_power: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 20.0 + width_modifier: + type: Absolute + value: 5.0 +delaunay.DelaunayMagnification: + shape_0: + gaussian_limits: + lower: 3.0 + upper: inf + lower_limit: 20.0 + type: Uniform + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + shape_1: + gaussian_limits: + lower: 3.0 + upper: inf + lower_limit: 20.0 + type: Uniform + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 +rectangular.Rectangular: + shape_0: + gaussian_limits: + lower: 3.0 + upper: inf + lower_limit: 20.0 + type: Uniform + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + shape_1: + gaussian_limits: + lower: 3.0 + upper: inf + lower_limit: 20.0 + type: Uniform + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 +voronoi.VoronoiBrightnessImage: + pixels: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 50.0 + type: Uniform + upper_limit: 1500.0 + width_modifier: + type: Absolute + value: 400.0 + weight_floor: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.1 + weight_power: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 20.0 + width_modifier: + type: Absolute + value: 5.0 +voronoi.VoronoiMagnification: + shape_0: + gaussian_limits: + lower: 3.0 + upper: inf + lower_limit: 20.0 + type: Uniform + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + shape_1: + gaussian_limits: + lower: 3.0 + upper: inf + lower_limit: 20.0 + type: Uniform + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 diff --git a/test_autolens/config/priors/regularization.yaml b/test_autolens/config/priors/regularization.yaml index c51693dc8..195ba9b2f 100644 --- a/test_autolens/config/priors/regularization.yaml +++ b/test_autolens/config/priors/regularization.yaml @@ -1,39 +1,39 @@ -adaptive_brightness.AdaptiveBrightness: - inner_coefficient: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - outer_coefficient: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 - signal_scale: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 -constant.Constant: - coefficient: - lower_limit: 0.0 - type: Uniform - upper_limit: 1.0 -constant_zeorth.ConstantZeroth: - coefficient_neighbor: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000000.0 - width_modifier: - type: Relative - value: 0.5 - coefficient_zeroth: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000000.0 - width_modifier: - type: Relative - value: 0.5 +adaptive_brightness.AdaptiveBrightness: + inner_coefficient: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + outer_coefficient: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 + signal_scale: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 +constant.Constant: + coefficient: + lower_limit: 0.0 + type: Uniform + upper_limit: 1.0 +constant_zeorth.ConstantZeroth: + coefficient_neighbor: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000000.0 + width_modifier: + type: Relative + value: 0.5 + coefficient_zeroth: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000000.0 + width_modifier: + type: Relative + value: 0.5 diff --git a/test_autolens/config/priors/stellar_mass_profiles.yaml b/test_autolens/config/priors/stellar_mass_profiles.yaml index ce1f7d9ed..cb68d765e 100644 --- a/test_autolens/config/priors/stellar_mass_profiles.yaml +++ b/test_autolens/config/priors/stellar_mass_profiles.yaml @@ -1,596 +1,596 @@ -DevVaucouleurs: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 -DevVaucouleursSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 -Exponential: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 -ExponentialSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 -Sersic: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 - sersic_index: - gaussian_limits: - lower: 0.8 - upper: 5.0 - lower_limit: 0.8 - type: Uniform - upper_limit: 5.0 - width_modifier: - type: Absolute - value: 1.5 -SersicGradient: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_gradient: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: -1.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 - sersic_index: - gaussian_limits: - lower: 0.8 - upper: 5.0 - lower_limit: 0.8 - type: Uniform - upper_limit: 5.0 - width_modifier: - type: Absolute - value: 1.5 -SersicGradientSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_gradient: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: -1.0 - type: Uniform - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 - sersic_index: - gaussian_limits: - lower: 0.8 - upper: 5.0 - lower_limit: 0.8 - type: Uniform - upper_limit: 5.0 - width_modifier: - type: Absolute - value: 1.5 -SersicSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - effective_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 30.0 - width_modifier: - type: Relative - value: 1.0 - intensity: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 10.0 - width_modifier: - type: Relative - value: 0.5 - mass_to_light_ratio: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 1.0e-06 - type: LogUniform - upper_limit: 1000.0 - width_modifier: - type: Relative - value: 0.3 - sersic_index: - gaussian_limits: - lower: 0.8 - upper: 5.0 - lower_limit: 0.8 - type: Uniform - upper_limit: 5.0 - width_modifier: - type: Absolute - value: 1.5 +DevVaucouleurs: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 +DevVaucouleursSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 +Exponential: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 +ExponentialSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 +Sersic: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 + sersic_index: + gaussian_limits: + lower: 0.8 + upper: 5.0 + lower_limit: 0.8 + type: Uniform + upper_limit: 5.0 + width_modifier: + type: Absolute + value: 1.5 +SersicGradient: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_gradient: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: -1.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 + sersic_index: + gaussian_limits: + lower: 0.8 + upper: 5.0 + lower_limit: 0.8 + type: Uniform + upper_limit: 5.0 + width_modifier: + type: Absolute + value: 1.5 +SersicGradientSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_gradient: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: -1.0 + type: Uniform + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 + sersic_index: + gaussian_limits: + lower: 0.8 + upper: 5.0 + lower_limit: 0.8 + type: Uniform + upper_limit: 5.0 + width_modifier: + type: Absolute + value: 1.5 +SersicSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + effective_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 30.0 + width_modifier: + type: Relative + value: 1.0 + intensity: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 10.0 + width_modifier: + type: Relative + value: 0.5 + mass_to_light_ratio: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 1.0e-06 + type: LogUniform + upper_limit: 1000.0 + width_modifier: + type: Relative + value: 0.3 + sersic_index: + gaussian_limits: + lower: 0.8 + upper: 5.0 + lower_limit: 0.8 + type: Uniform + upper_limit: 5.0 + width_modifier: + type: Absolute + value: 1.5 diff --git a/test_autolens/config/priors/total_mass_profiles.yaml b/test_autolens/config/priors/total_mass_profiles.yaml index 9fad7a6ee..c657bf654 100644 --- a/test_autolens/config/priors/total_mass_profiles.yaml +++ b/test_autolens/config/priors/total_mass_profiles.yaml @@ -1,491 +1,491 @@ -Isothermal: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 -IsothermalCore: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - core_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 0.2 - width_modifier: - type: Absolute - value: 0.1 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 -IsothermalCoreSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - core_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 0.2 - width_modifier: - type: Absolute - value: 0.1 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 -IsothermalSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 -PointMass: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 8.0 - width_modifier: - type: Relative - value: 0.25 -PowerLaw: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - slope: - gaussian_limits: - lower: 1.0 - upper: 3.0 - lower_limit: 1.5 - type: Uniform - upper_limit: 3.0 - width_modifier: - type: Absolute - value: 0.2 -PowerLawCore: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - core_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 0.2 - width_modifier: - type: Absolute - value: 0.1 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 - ell_comps_0: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - ell_comps_1: - gaussian_limits: - lower: -1.0 - upper: 1.0 - lower_limit: -1.0 - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: 1.0 - width_modifier: - type: Absolute - value: 0.2 - slope: - gaussian_limits: - lower: 1.0 - upper: 3.0 - lower_limit: 1.5 - type: Uniform - upper_limit: 3.0 - width_modifier: - type: Absolute - value: 0.2 -PowerLawCoreSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - core_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 0.2 - width_modifier: - type: Absolute - value: 0.1 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 - slope: - gaussian_limits: - lower: 1.0 - upper: 3.0 - lower_limit: 1.5 - type: Uniform - upper_limit: 3.0 - width_modifier: - type: Absolute - value: 0.2 -PowerLawSph: - centre_0: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - centre_1: - gaussian_limits: - lower: -inf - upper: inf - lower_limit: -inf - mean: 0.0 - sigma: 0.3 - type: Gaussian - upper_limit: inf - width_modifier: - type: Absolute - value: 0.05 - einstein_radius: - gaussian_limits: - lower: 0.0 - upper: inf - lower_limit: 0.0 - type: Uniform - upper_limit: 4.0 - width_modifier: - type: Relative - value: 0.05 - slope: - gaussian_limits: - lower: 1.0 - upper: 3.0 - lower_limit: 1.5 - type: Uniform - upper_limit: 3.0 - width_modifier: - type: Absolute - value: 0.2 +Isothermal: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 +IsothermalCore: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + core_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 0.2 + width_modifier: + type: Absolute + value: 0.1 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 +IsothermalCoreSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + core_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 0.2 + width_modifier: + type: Absolute + value: 0.1 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 +IsothermalSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 +PointMass: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 8.0 + width_modifier: + type: Relative + value: 0.25 +PowerLaw: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + slope: + gaussian_limits: + lower: 1.0 + upper: 3.0 + lower_limit: 1.5 + type: Uniform + upper_limit: 3.0 + width_modifier: + type: Absolute + value: 0.2 +PowerLawCore: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + core_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 0.2 + width_modifier: + type: Absolute + value: 0.1 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 + ell_comps_0: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + ell_comps_1: + gaussian_limits: + lower: -1.0 + upper: 1.0 + lower_limit: -1.0 + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: 1.0 + width_modifier: + type: Absolute + value: 0.2 + slope: + gaussian_limits: + lower: 1.0 + upper: 3.0 + lower_limit: 1.5 + type: Uniform + upper_limit: 3.0 + width_modifier: + type: Absolute + value: 0.2 +PowerLawCoreSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + core_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 0.2 + width_modifier: + type: Absolute + value: 0.1 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 + slope: + gaussian_limits: + lower: 1.0 + upper: 3.0 + lower_limit: 1.5 + type: Uniform + upper_limit: 3.0 + width_modifier: + type: Absolute + value: 0.2 +PowerLawSph: + centre_0: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + centre_1: + gaussian_limits: + lower: -inf + upper: inf + lower_limit: -inf + mean: 0.0 + sigma: 0.3 + type: Gaussian + upper_limit: inf + width_modifier: + type: Absolute + value: 0.05 + einstein_radius: + gaussian_limits: + lower: 0.0 + upper: inf + lower_limit: 0.0 + type: Uniform + upper_limit: 4.0 + width_modifier: + type: Relative + value: 0.05 + slope: + gaussian_limits: + lower: 1.0 + upper: 3.0 + lower_limit: 1.5 + type: Uniform + upper_limit: 3.0 + width_modifier: + type: Absolute + value: 0.2 diff --git a/test_autolens/config/visualize.yaml b/test_autolens/config/visualize.yaml index 9405d0ced..8e30ff854 100644 --- a/test_autolens/config/visualize.yaml +++ b/test_autolens/config/visualize.yaml @@ -71,84 +71,32 @@ mat_wrap_2d: s: 17 plots: dataset: - data: true - noise_map: false - signal_to_noise_map: false subplot_dataset: true - uv_wavelengths: false fit: - all_at_end_fits: true - all_at_end_png: false - chi_squared_map: true - data: true - model_data: true - model_images_of_planes: false - noise_map: false - normalized_residual_map: true - plane_images_of_planes: true - residual_map: false - signal_to_noise_map: false subplot_fit: true subplots_of_planes: false - subtracted_images_of_planes: true + fits_fit: true # Output a .fits file containing the fit model data, residual map, normalized residual map and chi-squared? + fits_model_galaxy_images : true # Output a .fits file containing the model images of every galaxy? fit_imaging: {} fit_interferometer: subplot_fit_real_space: true subplot_fit_dirty_images: true - amplitudes_vs_uv_distances: false - dirty_chi_squared_map: false - dirty_image: false - dirty_noise_map: false - dirty_normalized_residual_map: false - dirty_residual_map: false - dirty_signal_to_noise_map: false - phases_vs_uv_distances: false - uv_wavelengths: false + fits_dirty_images: true # output dirty_images.fits showing the dirty image, noise-map, model-data, resiual-map, normalized residual map and chi-squared map? + fit_point_dataset: {} fit_quantity: - all_at_end_fits: true - all_at_end_png: true - chi_squared_map: false - image: true - model_image: false - noise_map: false - residual_map: false subplot_fit: true galaxies: - convergence: false - image: false - potential: false + subplot_galaxies: true + subplot_galaxy_images: true + subplot_galaxies_1d: true + subplot_galaxies_1d_decomposed: true adapt: - images_of_galaxies: true - model_image: true - imaging: - psf: true - interferometer: - amplitudes_vs_uv_distances: false - dirty_image: false - dirty_noise_map: false - dirty_signal_to_noise_map: false - phases_vs_uv_distances: false - uv_wavelengths: false + subplot_adapt_images: true inversion: - all_at_end_png: false - chi_squared_map: true - errors: false - normalized_residual_map: false - reconstructed_image: true - reconstruction: true - regularization_weights: false - residual_map: false subplot_inversion: true positions: image_with_positions: true tracer: - all_at_end_fits: true - all_at_end_png: false - convergence: true - deflections: false - image: true - magnification: true - potential: false - source_plane_image: true subplot_tracer: true - subplot_galaxies_images: true \ No newline at end of file + subplot_galaxies_images: true + fits_tracer: true # Output tracer.fits file of tracer's convergence, potential, deflections_y and deflections_x? \ No newline at end of file diff --git a/test_autolens/imaging/model/test_plotter_interface_imaging.py b/test_autolens/imaging/model/test_plotter_interface_imaging.py index ca6e01d20..c3cb38ea4 100644 --- a/test_autolens/imaging/model/test_plotter_interface_imaging.py +++ b/test_autolens/imaging/model/test_plotter_interface_imaging.py @@ -23,28 +23,24 @@ def test__fit_imaging( plotter_interface = PlotterInterfaceImaging(image_path=plot_path) plotter_interface.fit_imaging( - fit=fit_imaging_x2_plane_inversion_7x7, during_analysis=False + fit=fit_imaging_x2_plane_inversion_7x7, ) assert path.join(plot_path, "subplot_tracer.png") in plot_patch.paths assert path.join(plot_path, "subplot_fit.png") in plot_patch.paths assert path.join(plot_path, "subplot_fit_log10.png") in plot_patch.paths - plot_path = path.join(plot_path, "fit_dataset") - - assert path.join(plot_path, "data.png") in plot_patch.paths - assert path.join(plot_path, "noise_map.png") not in plot_patch.paths - - assert path.join(plot_path, "lens_subtracted_image.png") in plot_patch.paths - assert path.join(plot_path, "source_model_image.png") not in plot_patch.paths + image = al.util.array_2d.numpy_array_2d_via_fits_from( + file_path=path.join(plot_path, "fit.fits"), hdu=0 + ) - assert path.join(plot_path, "reconstruction.png") in plot_patch.paths + assert image.shape == (5, 5) image = al.util.array_2d.numpy_array_2d_via_fits_from( - file_path=path.join(plot_path, "fits", "data.fits"), hdu=0 + file_path=path.join(plot_path, "model_galaxy_images.fits"), hdu=0 ) - assert image.shape == (7, 7) + assert image.shape == (5, 5) def test__fit_imaging_combined( fit_imaging_x2_plane_inversion_7x7, plot_path, plot_patch @@ -56,6 +52,4 @@ def test__fit_imaging_combined( visualizer.fit_imaging_combined(fit_list=2 * [fit_imaging_x2_plane_inversion_7x7]) - plot_path = path.join(plot_path, "combined") - - assert path.join(plot_path, "subplot_fit.png") in plot_patch.paths \ No newline at end of file + assert path.join(plot_path, "subplot_fit_combined.png") in plot_patch.paths \ No newline at end of file diff --git a/test_autolens/imaging/plot/test_fit_imaging_plotters.py b/test_autolens/imaging/plot/test_fit_imaging_plotters.py index 3e6b12aea..05fd07087 100644 --- a/test_autolens/imaging/plot/test_fit_imaging_plotters.py +++ b/test_autolens/imaging/plot/test_fit_imaging_plotters.py @@ -88,8 +88,6 @@ def test__figures_of_plane( subtracted_image=True, model_image=True, plane_index=0, plane_image=True ) - print(plot_patch.paths) - assert path.join(plot_path, "source_subtracted_image.png") in plot_patch.paths assert ( path.join(plot_path, "lens_subtracted_image.png") not in plot_patch.paths diff --git a/test_autolens/imaging/test_fit_imaging.py b/test_autolens/imaging/test_fit_imaging.py index 1279198a1..d2ada81de 100644 --- a/test_autolens/imaging/test_fit_imaging.py +++ b/test_autolens/imaging/test_fit_imaging.py @@ -212,7 +212,7 @@ def test__fit_figure_of_merit__sub_2(image_7x7, psf_3x3, noise_map_7x7, mask_2d_ data=image_7x7, psf=psf_3x3, noise_map=noise_map_7x7, - over_sampling=al.OverSamplingDataset(uniform=al.OverSamplingUniform(sub_size=2)), + over_sample_size_lp=2, ) masked_imaging_7x7 = dataset.apply_mask( @@ -479,7 +479,7 @@ def test__galaxy_model_image_dict(masked_imaging_7x7): fit = al.FitImaging(dataset=masked_imaging_7x7, tracer=tracer) blurred_image_2d_list = tracer.blurred_image_2d_list_from( - grid=masked_imaging_7x7.grids.uniform, + grid=masked_imaging_7x7.grids.lp, convolver=masked_imaging_7x7.convolver, blurring_grid=masked_imaging_7x7.grids.blurring, ) @@ -594,19 +594,19 @@ def test__subtracted_image_of_galaxies_dict(masked_imaging_7x7): fit = al.FitImaging(dataset=masked_imaging_7x7, tracer=tracer) g0_image = g0.blurred_image_2d_from( - grid=masked_imaging_7x7.grids.uniform, + grid=masked_imaging_7x7.grids.lp, blurring_grid=masked_imaging_7x7.grids.blurring, convolver=masked_imaging_7x7.convolver ) g1_image = g1.blurred_image_2d_from( - grid=masked_imaging_7x7.grids.uniform, + grid=masked_imaging_7x7.grids.lp, blurring_grid=masked_imaging_7x7.grids.blurring, convolver=masked_imaging_7x7.convolver ) g2_image = g2.blurred_image_2d_from( - grid=masked_imaging_7x7.grids.uniform, + grid=masked_imaging_7x7.grids.lp, blurring_grid=masked_imaging_7x7.grids.blurring, convolver=masked_imaging_7x7.convolver ) @@ -636,7 +636,7 @@ def test__subtracted_image_of_galaxies_dict(masked_imaging_7x7): fit = al.FitImaging(dataset=masked_imaging_7x7, tracer=tracer) blurred_image_2d_list = tracer.blurred_image_2d_list_from( - grid=masked_imaging_7x7.grids.uniform, + grid=masked_imaging_7x7.grids.lp, convolver=masked_imaging_7x7.convolver, blurring_grid=masked_imaging_7x7.grids.blurring, ) @@ -740,19 +740,19 @@ def test___unmasked_blurred_images(masked_imaging_7x7): fit = al.FitImaging(dataset=masked_imaging_7x7, tracer=tracer) blurred_images_of_planes = tracer.blurred_image_2d_list_from( - grid=masked_imaging_7x7.grids.uniform, + grid=masked_imaging_7x7.grids.lp, convolver=masked_imaging_7x7.convolver, blurring_grid=masked_imaging_7x7.grids.blurring, ) unmasked_blurred_image = tracer.unmasked_blurred_image_2d_from( - grid=masked_imaging_7x7.grids.uniform, psf=masked_imaging_7x7.psf + grid=masked_imaging_7x7.grids.lp, psf=masked_imaging_7x7.psf ) assert (fit.unmasked_blurred_image == unmasked_blurred_image).all() unmasked_blurred_image_of_planes_list = tracer.unmasked_blurred_image_2d_list_from( - grid=masked_imaging_7x7.grids.uniform, psf=masked_imaging_7x7.psf + grid=masked_imaging_7x7.grids.lp, psf=masked_imaging_7x7.psf ) assert ( diff --git a/test_autolens/imaging/test_simulate_and_fit_imaging.py b/test_autolens/imaging/test_simulate_and_fit_imaging.py index 8e1f2759f..f1dc8c990 100644 --- a/test_autolens/imaging/test_simulate_and_fit_imaging.py +++ b/test_autolens/imaging/test_simulate_and_fit_imaging.py @@ -9,7 +9,7 @@ def test__perfect_fit__chi_squared_0(): - grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2) + grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2, over_sample_size=1) psf = al.Kernel2D.from_gaussian( shape_native=(3, 3), pixel_scales=0.2, sigma=0.75, normalize=True @@ -25,7 +25,7 @@ def test__perfect_fit__chi_squared_0(): ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) + dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise_to_data=False) dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) dataset.noise_map = al.Array2D.ones( @@ -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, + over_sample_size_lp=1 ) mask = al.Mask2D.circular( @@ -79,83 +80,6 @@ def test__perfect_fit__chi_squared_0(): shutil.rmtree(file_path) -def test__perfect_fit__chi_squared_0__use_grid_iterate_to_simulate_and_fit(): - over_sampling = al.OverSamplingIterate(fractional_accuracy=0.9999, sub_steps=[2, 4, 8]) - - grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2, - over_sampling=over_sampling) - - psf = al.Kernel2D.from_gaussian( - shape_native=(3, 3), pixel_scales=0.2, sigma=0.75, normalize=True - ) - - lens_galaxy = al.Galaxy( - redshift=0.5, - light=al.lp.Sersic(centre=(0.1, 0.1), intensity=0.1), - mass=al.mp.Isothermal(centre=(0.1, 0.1), einstein_radius=1.8), - ) - source_galaxy = al.Galaxy( - redshift=1.0, light=al.lp.Exponential(centre=(0.1, 0.1), intensity=0.5) - ) - tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) - - dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) - dataset.noise_map = al.Array2D.ones( - shape_native=dataset.data.shape_native, pixel_scales=0.2 - ) - - file_path = path.join( - "{}".format(path.dirname(path.realpath(__file__))), - "data_temp", - "simulate_and_fit", - ) - - try: - shutil.rmtree(file_path) - except FileNotFoundError: - pass - - if path.exists(file_path) is False: - os.makedirs(file_path) - - dataset.output_to_fits( - data_path=path.join(file_path, "data.fits"), - noise_map_path=path.join(file_path, "noise_map.fits"), - psf_path=path.join(file_path, "psf.fits"), - ) - - dataset = al.Imaging.from_fits( - data_path=path.join(file_path, "data.fits"), - noise_map_path=path.join(file_path, "noise_map.fits"), - psf_path=path.join(file_path, "psf.fits"), - pixel_scales=0.2, - over_sampling=al.OverSamplingDataset(uniform=over_sampling) - ) - - mask = al.Mask2D.circular( - shape_native=dataset.data.shape_native, pixel_scales=0.2, radius=0.8 - ) - - masked_dataset = dataset.apply_mask(mask=mask) - - tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - - fit = al.FitImaging(dataset=masked_dataset, tracer=tracer) - - # The value is actually not zero before the blurring grid assumes a sub_size=1 - # and does not use the iterative grid, which has a small impact on the chi-squared - - assert fit.chi_squared == pytest.approx(7.451891005452524e-05, 1e-4) - - file_path = path.join( - "{}".format(path.dirname(path.realpath(__file__))), "data_temp" - ) - - if path.exists(file_path) is True: - shutil.rmtree(file_path) - def test__simulate_imaging_data_and_fit__known_likelihood(): @@ -194,12 +118,12 @@ def test__simulate_imaging_data_and_fit__known_likelihood(): fit = al.FitImaging(dataset=masked_dataset, tracer=tracer) - assert fit.figure_of_merit == pytest.approx(526.353910, 1.0e-2) + assert fit.figure_of_merit == pytest.approx(538.796271746575, 1.0e-2) def test__simulate_imaging_data_and_fit__linear_light_profiles_agree_with_standard_light_profiles(): - grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2) + grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2, over_sample_size=1) psf = al.Kernel2D.from_gaussian( shape_native=(3, 3), pixel_scales=0.2, sigma=0.75, normalize=True @@ -217,10 +141,9 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_agree_with_standa ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) + dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise_to_data=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 ) @@ -230,6 +153,9 @@ 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_over_sampling( + over_sample_size_lp=1 + ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) @@ -272,7 +198,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_agree_with_standa assert fit_linear.figure_of_merit == pytest.approx(-45.02798, 1.0e-4) lens_galaxy_image = lens_galaxy.blurred_image_2d_from( - grid=masked_dataset.grids.uniform, + grid=masked_dataset.grids.lp, convolver=masked_dataset.convolver, blurring_grid=masked_dataset.grids.blurring, ) @@ -284,7 +210,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_agree_with_standa lens_galaxy_image, 1.0e-4 ) - traced_grid_2d_list = tracer.traced_grid_2d_list_from(grid=masked_dataset.grids.uniform) + traced_grid_2d_list = tracer.traced_grid_2d_list_from(grid=masked_dataset.grids.lp) traced_blurring_grid_2d_list = tracer.traced_grid_2d_list_from( grid=masked_dataset.grids.blurring ) @@ -306,7 +232,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_agree_with_standa def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization(): - grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2) + grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2, over_sample_size=1) psf = al.Kernel2D.from_gaussian( shape_native=(3, 3), pixel_scales=0.2, sigma=0.75, normalize=True @@ -324,10 +250,9 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization( ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) + dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise_to_data=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 ) @@ -337,6 +262,9 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization( ) masked_dataset = dataset.apply_mask(mask=mask) + masked_dataset = masked_dataset.apply_over_sampling( + over_sample_size_lp=1 + ) lens_galaxy_linear = al.Galaxy( redshift=0.5, @@ -387,7 +315,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization( assert fit_linear.figure_of_merit == pytest.approx(-84.04875317, 1.0e-4) lens_galaxy_image = lens_galaxy.blurred_image_2d_from( - grid=masked_dataset.grids.uniform, + grid=masked_dataset.grids.lp, convolver=masked_dataset.convolver, blurring_grid=masked_dataset.grids.blurring, ) @@ -459,7 +387,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization( def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization__sub_2(): - grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2, over_sampling=al.OverSamplingUniform(sub_size=2)) + grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2, over_sample_size=2) psf = al.Kernel2D.from_gaussian( shape_native=(3, 3), pixel_scales=0.2, sigma=0.75, normalize=True @@ -477,7 +405,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization_ ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) + dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise_to_data=False) dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) dataset.noise_map = al.Array2D.ones( @@ -492,10 +420,8 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization_ data=dataset.data, psf=dataset.psf, noise_map=dataset.noise_map, - over_sampling=al.OverSamplingDataset( - uniform=al.OverSamplingUniform(sub_size=2), - pixelization=al.OverSamplingUniform(sub_size=2) - ) + over_sample_size_lp=2, + over_sample_size_pixelization=2 ) masked_dataset = dataset.apply_mask(mask=mask) @@ -537,7 +463,7 @@ def test__simulate_imaging_data_and_fit__linear_light_profiles_and_pixelization_ assert fit_linear.figure_of_merit == pytest.approx(-84.36224277776512, 1.0e-4) lens_galaxy_image = lens_galaxy.blurred_image_2d_from( - grid=masked_dataset.grids.uniform, + grid=masked_dataset.grids.lp, convolver=masked_dataset.convolver, blurring_grid=masked_dataset.grids.blurring, ) @@ -621,7 +547,7 @@ def test__simulate_imaging_data_and_fit__complex_fit_compare_mapping_matrix_w_ti source_1 = al.Galaxy(redshift=0.5, bulge=al.lp.Sersic(centre=(0.3, 0.3))) tracer = al.Tracer(galaxies=[lens_0, lens_1, lens_2, source_0, source_1]) - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) + dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise_to_data=False) dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) dataset.sub_size = 2 @@ -692,100 +618,11 @@ def test__simulate_imaging_data_and_fit__complex_fit_compare_mapping_matrix_w_ti 1.0e-4, ) -def test__perfect_fit__chi_squared_0__non_uniform_over_sampling(): - - over_sampling = al.OverSamplingUniform(sub_size=8) - - grid = al.Grid2D.uniform( - shape_native=(31, 31), - pixel_scales=0.2, - over_sampling=over_sampling - ) - - psf = al.Kernel2D.from_gaussian( - shape_native=(3, 3), pixel_scales=0.2, sigma=0.75, normalize=True - ) - - lens_galaxy = al.Galaxy( - redshift=0.5, - mass=al.mp.Isothermal(centre=(0.1, 0.1), einstein_radius=0.3), - ) - source_galaxy = al.Galaxy( - redshift=1.0, light=al.lp.Sersic(centre=(0.1, 0.1), intensity=0.5, sersic_index=1.2) - ) - tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) - - dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) - dataset.noise_map = al.Array2D.ones( - shape_native=dataset.data.shape_native, pixel_scales=0.2 - ) - - file_path = path.join( - "{}".format(path.dirname(path.realpath(__file__))), - "data_temp", - "simulate_and_fit", - ) - - try: - shutil.rmtree(file_path) - except FileNotFoundError: - pass - - if path.exists(file_path) is False: - os.makedirs(file_path) - - dataset.output_to_fits( - data_path=path.join(file_path, "data.fits"), - noise_map_path=path.join(file_path, "noise_map.fits"), - psf_path=path.join(file_path, "psf.fits"), - ) - - dataset = al.Imaging.from_fits( - data_path=path.join(file_path, "data.fits"), - noise_map_path=path.join(file_path, "noise_map.fits"), - psf_path=path.join(file_path, "psf.fits"), - pixel_scales=0.2, - ) - - mask = al.Mask2D.circular( - shape_native=dataset.data.shape_native, pixel_scales=0.2, radius=1.5 - ) - - masked_dataset = dataset.apply_mask(mask=mask) - - traced_grid = tracer.traced_grid_2d_list_from( - grid=masked_dataset.grids.uniform, - )[-1] - - masked_dataset = masked_dataset.apply_over_sampling( - over_sampling=al.OverSamplingDataset( - uniform=al.OverSamplingUniform(sub_size=1), - non_uniform=al.OverSamplingUniform.from_radial_bins( - grid=traced_grid, sub_size_list=[8, 2], radial_list=[0.3], centre_list=[source_galaxy.light.centre] - )) - ) - - tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - - fit = al.FitImaging(dataset=masked_dataset, tracer=tracer) - - assert fit.chi_squared < 0.1 - - file_path = path.join( - "{}".format(path.dirname(path.realpath(__file__))), "data_temp" - ) - - if path.exists(file_path) is True: - shutil.rmtree(file_path) - def test__fit_figure_of_merit__mge_mass_model(masked_imaging_7x7, masked_imaging_covariance_7x7): - over_sampling = al.OverSamplingIterate(fractional_accuracy=0.9999, sub_steps=[2, 4, 8]) grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.2, - over_sampling=over_sampling) + over_sample_size=8) psf = al.Kernel2D.from_gaussian( shape_native=(3, 3), pixel_scales=0.2, sigma=0.75, normalize=True @@ -803,7 +640,7 @@ def test__fit_figure_of_merit__mge_mass_model(masked_imaging_7x7, masked_imaging ) tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise=False) + dataset = al.SimulatorImaging(exposure_time=300.0, psf=psf, add_poisson_noise_to_data=False) dataset = dataset.via_tracer_from(tracer=tracer, grid=grid) dataset.noise_map = al.Array2D.ones( @@ -835,7 +672,7 @@ def test__fit_figure_of_merit__mge_mass_model(masked_imaging_7x7, masked_imaging noise_map_path=path.join(file_path, "noise_map.fits"), psf_path=path.join(file_path, "psf.fits"), pixel_scales=0.2, - over_sampling=al.OverSamplingDataset(uniform=over_sampling) + over_sample_size_lp=8 ) mask = al.Mask2D.circular( @@ -864,10 +701,8 @@ def test__fit_figure_of_merit__mge_mass_model(masked_imaging_7x7, masked_imaging assert fit.chi_squared == pytest.approx(5.706423629698664e-05, 1e-4) - over_sampling = al.OverSamplingUniform(sub_size=8) - masked_dataset = masked_dataset.apply_over_sampling( - al.OverSamplingDataset(uniform=over_sampling) + over_sample_size_lp=8 ) basis = al.lp_basis.Basis( diff --git a/test_autolens/interferometer/model/test_plotter_interface_interferometer.py b/test_autolens/interferometer/model/test_plotter_interface_interferometer.py index be0d81fa9..8a883c4fd 100644 --- a/test_autolens/interferometer/model/test_plotter_interface_interferometer.py +++ b/test_autolens/interferometer/model/test_plotter_interface_interferometer.py @@ -1,6 +1,9 @@ from os import path import pytest + +import autolens as al + from autolens.interferometer.model.plotter_interface import ( PlotterInterfaceInterferometer, ) @@ -22,14 +25,27 @@ def test__fit_interferometer( plotter_interface = PlotterInterfaceInterferometer(image_path=plot_path) plotter_interface.fit_interferometer( - fit=fit_interferometer_x2_plane_7x7, during_analysis=True + fit=fit_interferometer_x2_plane_7x7, ) assert path.join(plot_path, "subplot_fit.png") in plot_patch.paths assert path.join(plot_path, "subplot_fit_real_space.png") in plot_patch.paths assert path.join(plot_path, "subplot_fit_dirty_images.png") in plot_patch.paths - plot_path = path.join(plot_path, "fit_dataset") + # visibilities = ag.util.array_2d.numpy_array_2d_via_fits_from( + # file_path=path.join(plot_path, "fit.fits"), hdu=0 + # ) + # + # assert visibilities.shape == (5, 5) + + image = al.util.array_2d.numpy_array_2d_via_fits_from( + file_path=path.join(plot_path, "model_galaxy_images.fits"), hdu=0 + ) + + assert image.shape == (5, 5) + + image = al.util.array_2d.numpy_array_2d_via_fits_from( + file_path=path.join(plot_path, "dirty_images.fits"), hdu=0 + ) - assert path.join(plot_path, "data.png") in plot_patch.paths - assert path.join(plot_path, "noise_map.png") not in plot_patch.paths + assert image.shape == (5, 5) diff --git a/test_autolens/interferometer/test_fit_interferometer.py b/test_autolens/interferometer/test_fit_interferometer.py index 0e6527102..52045349d 100644 --- a/test_autolens/interferometer/test_fit_interferometer.py +++ b/test_autolens/interferometer/test_fit_interferometer.py @@ -176,7 +176,7 @@ def test___galaxy_model_image_dict(interferometer_7, interferometer_7_grid): ) traced_grid_2d_list_from = tracer.traced_grid_2d_list_from( - grid=interferometer_7.grids.uniform + grid=interferometer_7.grids.lp ) g0_image = g0.image_2d_from(grid=traced_grid_2d_list_from[0]) @@ -275,7 +275,7 @@ def test__galaxy_model_visibilities_dict(interferometer_7, interferometer_7_grid fit = al.FitInterferometer(dataset=interferometer_7, tracer=tracer) traced_grid_2d_list_from = tracer.traced_grid_2d_list_from( - grid=interferometer_7.grids.uniform + grid=interferometer_7.grids.lp ) g0_profile_visibilities = g0.visibilities_from( diff --git a/test_autolens/interferometer/test_simulate_and_fit_interferometer.py b/test_autolens/interferometer/test_simulate_and_fit_interferometer.py index d88e23e33..e88f3a7ef 100644 --- a/test_autolens/interferometer/test_simulate_and_fit_interferometer.py +++ b/test_autolens/interferometer/test_simulate_and_fit_interferometer.py @@ -8,7 +8,9 @@ def test__perfect_fit__chi_squared_0(): - grid = al.Grid2D.uniform(shape_native=(51, 51), pixel_scales=0.1) + grid = al.Grid2D.uniform( + shape_native=(51, 51), pixel_scales=0.1, over_sample_size=1 + ) lens_galaxy = al.Galaxy( redshift=0.5, @@ -108,7 +110,7 @@ def test__perfect_fit__chi_squared_0(): def test__simulate_interferometer_data_and_fit__known_likelihood(): mask = al.Mask2D.circular(radius=3.0, shape_native=(31, 31), pixel_scales=0.2) - grid = al.Grid2D.from_mask(mask=mask) + grid = al.Grid2D.from_mask(mask=mask, over_sample_size=1) pixelization = al.Pixelization( mesh=al.mesh.Rectangular(shape=(16, 16)), @@ -143,7 +145,9 @@ def test__simulate_interferometer_data_and_fit__known_likelihood(): def test__simulate_interferometer_data_and_fit__linear_light_profiles_agree_with_standard_light_profiles(): - grid = al.Grid2D.uniform(shape_native=(51, 51), pixel_scales=0.1) + grid = al.Grid2D.uniform( + shape_native=(51, 51), pixel_scales=0.1, over_sample_size=1 + ) lens_galaxy = al.Galaxy( redshift=0.5, @@ -214,13 +218,13 @@ def test__simulate_interferometer_data_and_fit__linear_light_profiles_agree_with ] == pytest.approx(0.2, 1.0e-2) assert fit.log_likelihood == pytest.approx(fit_linear.log_likelihood) - lens_galaxy_image = lens_galaxy.image_2d_from(grid=dataset.grids.uniform) + lens_galaxy_image = lens_galaxy.image_2d_from(grid=dataset.grids.lp) assert fit_linear.galaxy_model_image_dict[lens_galaxy_linear] == pytest.approx( lens_galaxy_image, 1.0e-4 ) - traced_grid_2d_list = tracer.traced_grid_2d_list_from(grid=dataset.grids.uniform) + traced_grid_2d_list = tracer.traced_grid_2d_list_from(grid=dataset.grids.lp) source_galaxy_image = source_galaxy.image_2d_from(grid=traced_grid_2d_list[1]) @@ -229,7 +233,7 @@ def test__simulate_interferometer_data_and_fit__linear_light_profiles_agree_with ) lens_galaxy_visibilities = lens_galaxy.visibilities_from( - grid=dataset.grids.uniform, transformer=dataset.transformer + grid=dataset.grids.lp, transformer=dataset.transformer ) assert fit_linear.galaxy_model_visibilities_dict[ @@ -246,7 +250,9 @@ def test__simulate_interferometer_data_and_fit__linear_light_profiles_agree_with def test__simulate_interferometer_data_and_fit__linear_light_profiles_and_pixelization(): - grid = al.Grid2D.uniform(shape_native=(51, 51), pixel_scales=0.1) + grid = al.Grid2D.uniform( + shape_native=(51, 51), pixel_scales=0.1, over_sample_size=1 + ) lens_galaxy = al.Galaxy( redshift=0.5, @@ -317,13 +323,13 @@ def test__simulate_interferometer_data_and_fit__linear_light_profiles_and_pixeli ) assert fit_linear.figure_of_merit == pytest.approx(-29.20551989, 1.0e-4) - lens_galaxy_image = lens_galaxy.image_2d_from(grid=dataset.grids.uniform) + lens_galaxy_image = lens_galaxy.image_2d_from(grid=dataset.grids.lp) assert fit_linear.galaxy_model_image_dict[lens_galaxy_linear] == pytest.approx( lens_galaxy_image, 1.0e-2 ) - traced_grid_2d_list = tracer.traced_grid_2d_list_from(grid=dataset.grids.uniform) + traced_grid_2d_list = tracer.traced_grid_2d_list_from(grid=dataset.grids.lp) source_galaxy_image = source_galaxy.image_2d_from(grid=traced_grid_2d_list[1]) diff --git a/test_autolens/interferometer/test_simulator.py b/test_autolens/interferometer/test_simulator.py index 38dc14ca8..dd01262b0 100644 --- a/test_autolens/interferometer/test_simulator.py +++ b/test_autolens/interferometer/test_simulator.py @@ -3,108 +3,107 @@ import pytest -class TestSimulatorInterferometer: - def test__from_tracer__same_as_tracer_input(self): - grid = al.Grid2D.uniform(shape_native=(20, 20), pixel_scales=0.05) - - lens_galaxy = al.Galaxy( - redshift=0.5, - light=al.lp.Sersic(intensity=1.0), - mass=al.mp.Isothermal(einstein_radius=1.6), - ) - - source_galaxy = al.Galaxy(redshift=1.0, light=al.lp.Sersic(intensity=0.3)) - - tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - - simulator = al.SimulatorInterferometer( - uv_wavelengths=np.ones(shape=(7, 2)), - exposure_time=10000.0, - noise_sigma=0.1, - noise_seed=1, - ) - - dataset = simulator.via_tracer_from(tracer=tracer, grid=grid) - - interferometer_via_image = simulator.via_image_from( - image=tracer.image_2d_from(grid=grid) - ) - - assert (dataset.data == interferometer_via_image.data).all() - assert (dataset.uv_wavelengths == interferometer_via_image.uv_wavelengths).all() - assert (dataset.noise_map == interferometer_via_image.noise_map).all() - - def test__via_deflections_and_galaxies_from__same_as_calculation_using_tracer(self): - grid = al.Grid2D.uniform(shape_native=(20, 20), pixel_scales=0.05) - - lens_galaxy = al.Galaxy( - redshift=0.5, mass=al.mp.Isothermal(einstein_radius=1.6) - ) - - source_galaxy = al.Galaxy(redshift=1.0, light=al.lp.Sersic(intensity=0.3)) - - tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - - simulator = al.SimulatorInterferometer( - uv_wavelengths=np.ones(shape=(7, 2)), - exposure_time=10000.0, - noise_sigma=0.1, - noise_seed=1, - ) - - dataset = simulator.via_deflections_and_galaxies_from( - deflections=tracer.deflections_yx_2d_from(grid=grid), - galaxies=[source_galaxy], - ) - - interferometer_via_image = simulator.via_image_from( - image=tracer.image_2d_from(grid=grid) - ) - - assert (dataset.data == interferometer_via_image.data).all() - assert (interferometer_via_image.uv_wavelengths == dataset.uv_wavelengths).all() - assert (dataset.noise_map == interferometer_via_image.noise_map).all() - - def test__simulate_interferometer_from_lens__source_galaxy__compare_to_interferometer( - self, - ): - lens_galaxy = al.Galaxy( - redshift=0.5, - mass=al.mp.Isothermal( - centre=(0.0, 0.0), einstein_radius=1.6, ell_comps=(0.17647, 0.0) - ), - ) - - source_galaxy = al.Galaxy( - redshift=0.5, - light=al.lp.Sersic( - centre=(0.1, 0.1), - ell_comps=(0.096225, -0.055555), - intensity=0.3, - effective_radius=1.0, - sersic_index=2.5, - ), - ) - - grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.05) - - simulator = al.SimulatorInterferometer( - uv_wavelengths=np.ones(shape=(7, 2)), - exposure_time=10000.0, - noise_sigma=0.1, - noise_seed=1, - ) - - dataset = simulator.via_galaxies_from( - galaxies=[lens_galaxy, source_galaxy], grid=grid - ) - - tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) - - interferometer_via_image = simulator.via_image_from( - image=tracer.image_2d_from(grid=grid) - ) - - assert dataset.data == pytest.approx(interferometer_via_image.data, 1.0e-4) - assert (dataset.uv_wavelengths == interferometer_via_image.uv_wavelengths).all() - assert (interferometer_via_image.noise_map == dataset.noise_map).all() +def test__from_tracer__same_as_tracer_input(): + grid = al.Grid2D.uniform(shape_native=(20, 20), pixel_scales=0.05) + + lens_galaxy = al.Galaxy( + redshift=0.5, + light=al.lp.Sersic(intensity=1.0), + mass=al.mp.Isothermal(einstein_radius=1.6), + ) + + source_galaxy = al.Galaxy(redshift=1.0, light=al.lp.Sersic(intensity=0.3)) + + tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) + + simulator = al.SimulatorInterferometer( + uv_wavelengths=np.ones(shape=(7, 2)), + exposure_time=10000.0, + noise_sigma=0.1, + noise_seed=1, + ) + + dataset = simulator.via_tracer_from(tracer=tracer, grid=grid) + + interferometer_via_image = simulator.via_image_from( + image=tracer.image_2d_from(grid=grid) + ) + + assert (dataset.data == interferometer_via_image.data).all() + assert (dataset.uv_wavelengths == interferometer_via_image.uv_wavelengths).all() + assert (dataset.noise_map == interferometer_via_image.noise_map).all() + + +def test__via_deflections_and_galaxies_from__same_as_calculation_using_tracer(): + grid = al.Grid2D.uniform( + shape_native=(20, 20), pixel_scales=0.05, over_sample_size=1 + ) + + lens_galaxy = al.Galaxy(redshift=0.5, mass=al.mp.Isothermal(einstein_radius=1.6)) + + source_galaxy = al.Galaxy(redshift=1.0, light=al.lp.Sersic(intensity=0.3)) + + tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) + + simulator = al.SimulatorInterferometer( + uv_wavelengths=np.ones(shape=(7, 2)), + exposure_time=10000.0, + noise_sigma=0.1, + noise_seed=1, + ) + + dataset = simulator.via_deflections_and_galaxies_from( + deflections=tracer.deflections_yx_2d_from(grid=grid), + galaxies=[source_galaxy], + ) + + interferometer_via_image = simulator.via_image_from( + image=tracer.image_2d_from(grid=grid) + ) + + assert (dataset.data == interferometer_via_image.data).all() + assert (interferometer_via_image.uv_wavelengths == dataset.uv_wavelengths).all() + assert (dataset.noise_map == interferometer_via_image.noise_map).all() + + +def test__simulate_interferometer_from_lens__source_galaxy__compare_to_interferometer(): + lens_galaxy = al.Galaxy( + redshift=0.5, + mass=al.mp.Isothermal( + centre=(0.0, 0.0), einstein_radius=1.6, ell_comps=(0.17647, 0.0) + ), + ) + + source_galaxy = al.Galaxy( + redshift=0.5, + light=al.lp.Sersic( + centre=(0.1, 0.1), + ell_comps=(0.096225, -0.055555), + intensity=0.3, + effective_radius=1.0, + sersic_index=2.5, + ), + ) + + grid = al.Grid2D.uniform(shape_native=(11, 11), pixel_scales=0.05) + + simulator = al.SimulatorInterferometer( + uv_wavelengths=np.ones(shape=(7, 2)), + exposure_time=10000.0, + noise_sigma=0.1, + noise_seed=1, + ) + + dataset = simulator.via_galaxies_from( + galaxies=[lens_galaxy, source_galaxy], grid=grid + ) + + tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) + + interferometer_via_image = simulator.via_image_from( + image=tracer.image_2d_from(grid=grid) + ) + + assert dataset.data == pytest.approx(interferometer_via_image.data, 1.0e-4) + assert (dataset.uv_wavelengths == interferometer_via_image.uv_wavelengths).all() + assert (interferometer_via_image.noise_map == dataset.noise_map).all() diff --git a/test_autolens/lens/test_operate.py b/test_autolens/lens/test_operate.py index c95de7893..32268bbc2 100644 --- a/test_autolens/lens/test_operate.py +++ b/test_autolens/lens/test_operate.py @@ -155,13 +155,8 @@ def test__operate_image__galaxy_blurred_image_2d_dict_from( blurring_grid=blurring_grid_2d_7x7, ) - g1_deflections = g1.deflections_yx_2d_from(grid=grid_2d_7x7) - - source_grid_2d_7x7 = grid_2d_7x7 - g1_deflections - - g1_blurring_deflections = g1.deflections_yx_2d_from(grid=blurring_grid_2d_7x7) - - source_blurring_grid_2d_7x7 = blurring_grid_2d_7x7 - g1_blurring_deflections + source_grid_2d_7x7 = g1.traced_grid_2d_from(grid=grid_2d_7x7) + source_blurring_grid_2d_7x7 = g1.traced_grid_2d_from(grid=blurring_grid_2d_7x7) g3_blurred_image = g3.blurred_image_2d_from( grid=source_grid_2d_7x7, @@ -206,9 +201,7 @@ def test__operate_image__galaxy_visibilities_dict_from_grid_and_transformer( grid=grid_2d_7x7, transformer=transformer_7x7_7 ) - g1_deflections = g1.deflections_yx_2d_from(grid=grid_2d_7x7) - - source_grid_2d_7x7 = grid_2d_7x7 - g1_deflections + source_grid_2d_7x7 = g1.traced_grid_2d_from(grid=grid_2d_7x7) g3_visibilities = g3.visibilities_from( grid=source_grid_2d_7x7, transformer=transformer_7x7_7 diff --git a/test_autolens/lens/test_to_inversion.py b/test_autolens/lens/test_to_inversion.py index ea9d6788b..d8a008583 100644 --- a/test_autolens/lens/test_to_inversion.py +++ b/test_autolens/lens/test_to_inversion.py @@ -57,12 +57,10 @@ def test__lp_linear_func_galaxy_dict_from(masked_imaging_7x7): assert lp_linear_func_list[1].light_profile_list[0] == lp_linear_1 assert lp_linear_func_list[2].light_profile_list[0] == lp_linear_2 - traced_grid_list = tracer.traced_grid_2d_list_from( - grid=masked_imaging_7x7.grids.uniform - ) + traced_grid_list = tracer.traced_grid_2d_list_from(grid=masked_imaging_7x7.grids.lp) assert lp_linear_func_list[0].grid == pytest.approx( - masked_imaging_7x7.grids.uniform, 1.0e-4 + masked_imaging_7x7.grids.lp, 1.0e-4 ) assert lp_linear_func_list[1].grid == pytest.approx(traced_grid_list[1], 1.0e-4) assert lp_linear_func_list[2].grid == pytest.approx(traced_grid_list[2], 1.0e-4) @@ -455,7 +453,7 @@ def test__mapper_galaxy_dict(masked_imaging_7x7): def test__inversion_imaging_from(grid_2d_7x7, masked_imaging_7x7): grids = al.GridsInterface( - uniform=masked_imaging_7x7.grids.uniform, + lp=masked_imaging_7x7.grids.lp, pixelization=masked_imaging_7x7.grids.pixelization, blurring=masked_imaging_7x7.grids.blurring, border_relocator=masked_imaging_7x7.grids.border_relocator, @@ -508,7 +506,7 @@ def test__inversion_interferometer_from(grid_2d_7x7, interferometer_7): interferometer_7.data = al.Visibilities.ones(shape_slim=(7,)) grids = al.GridsInterface( - uniform=interferometer_7.grids.uniform, + lp=interferometer_7.grids.lp, pixelization=interferometer_7.grids.pixelization, blurring=interferometer_7.grids.blurring, border_relocator=interferometer_7.grids.border_relocator, diff --git a/test_autolens/lens/test_tracer.py b/test_autolens/lens/test_tracer.py index 018d433b0..23e5bda5f 100644 --- a/test_autolens/lens/test_tracer.py +++ b/test_autolens/lens/test_tracer.py @@ -221,109 +221,6 @@ def test__image_2d_list_from(): assert len(image_list) == 3 -def test__image_2d_of_plane_from(): - g0 = al.Galaxy(redshift=0.5, light_profile=al.lp.Sersic(intensity=1.0)) - g1 = al.Galaxy(redshift=0.5, light_profile=al.lp.Sersic(intensity=2.0)) - g2 = al.Galaxy(redshift=0.5, light_profile=al.lp.Sersic(intensity=3.0)) - - tracer = al.Tracer(galaxies=[g0, g1, g2]) - - image = tracer.image_2d_of_plane_from(grid=grid_simple, plane_index=0) - - assert image == pytest.approx(0.30276535, 1.0e-4) - - g0 = al.Galaxy( - redshift=0.5, - light_profile=al.lp.Sersic(intensity=1.0), - mass_profile=al.mp.IsothermalSph(einstein_radius=1.0), - ) - g1 = al.Galaxy( - redshift=1.0, - light_profile=al.lp.Sersic(intensity=2.0), - mass_profile=al.mp.IsothermalSph(einstein_radius=2.0), - ) - g2 = al.Galaxy(redshift=2.0, light_profile=al.lp.Sersic(intensity=3.0)) - - tracer = al.Tracer(galaxies=[g0, g1, g2]) - - image = tracer.image_2d_of_plane_from(grid=grid_simple, plane_index=0) - - assert image == pytest.approx(0.0504608, 1.0e-4) - - image = tracer.image_2d_of_plane_from(grid=grid_simple, plane_index=1) - - assert image == pytest.approx(0.2517025, 1.0e-4) - - image = tracer.image_2d_of_plane_from(grid=grid_simple, plane_index=2) - - assert image == pytest.approx(1.8611933, 1.0e-4) - - -def test__image_2d_list_from__adaptive_iterate_sub_grid(): - mask = al.Mask2D( - mask=[ - [True, True, True, True, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - ) - - g0 = al.Galaxy( - redshift=0.5, - light_profile=al.lp.Sersic(intensity=1.0), - mass_profile=al.mp.IsothermalSph(einstein_radius=1.0), - ) - g1 = al.Galaxy( - redshift=1.0, - light_profile=al.lp.Sersic(intensity=2.0), - mass_profile=al.mp.IsothermalSph(einstein_radius=2.0), - ) - g2 = al.Galaxy(redshift=2.0, light_profile=al.lp.Sersic(intensity=3.0)) - - tracer = al.Tracer(galaxies=[g0, g1, g2]) - - def image_2d_list_from_sub_size(sub_size): - grid = al.Grid2D.from_mask( - mask=mask, over_sampling=al.OverSamplingUniform(sub_size=sub_size) - ) - - grid_oversampled = grid.over_sampler.over_sampled_grid - traced_grid_list = tracer.traced_grid_2d_list_from(grid=grid_oversampled) - - image_g0 = g0.image_2d_from(grid=grid) - image_g1_oversampled = g1.image_2d_from(grid=traced_grid_list[1]) - image_g1 = grid.over_sampler.binned_array_2d_from(array=image_g1_oversampled) - - image_g2_oversampled = g2.image_2d_from(grid=traced_grid_list[2]) - image_g2 = grid.over_sampler.binned_array_2d_from(array=image_g2_oversampled) - - return [image_g0, image_g1, image_g2] - - # These values are carefully chosen to make it so that the central pixel requires a sub_size of 4 after iterations - # whereas pixel 0 stops iteration at 2. - - image_2d_sub_2_list = image_2d_list_from_sub_size(sub_size=2) - image_2d_sub_4_list = image_2d_list_from_sub_size(sub_size=4) - - grid_iterate = al.Grid2D.from_mask( - mask=mask, - over_sampling=al.OverSamplingIterate(fractional_accuracy=0.7, sub_steps=[2, 4]), - ) - - image_2d_list = tracer.image_2d_list_from(grid=grid_iterate) - - assert image_2d_list[0][4] == pytest.approx(image_2d_sub_4_list[0][4], 1.0e-4) - assert image_2d_list[1][4] == pytest.approx(image_2d_sub_4_list[1][4], 1.0e-4) - assert image_2d_list[2][4] == pytest.approx(image_2d_sub_4_list[2][4], 1.0e-4) - - assert image_2d_list[0][0] == pytest.approx(image_2d_sub_2_list[0][0], 1.0e-4) - assert image_2d_list[1][0] == pytest.approx(image_2d_sub_2_list[1][0], 1.0e-4) - assert image_2d_list[2][0] == pytest.approx(image_2d_sub_2_list[2][0], 1.0e-4) - - def test__image_2d_list_from__plane_without_light_profile_is_zeros( grid_2d_7x7, ): @@ -359,7 +256,9 @@ def test__image_2d_from__operated_only_input(grid_2d_7x7, lp_0, lp_operated_0, m assert image[1] == pytest.approx(2.93152589, 1.0e-4) -def test__image_2d_from__sum_of_individual_images(grid_2d_7x7, grid_2d_7x7_simple): +def test__image_2d_from__sum_of_individual_images(mask_2d_7x7): + grid_2d_7x7 = al.Grid2D.from_mask(mask=mask_2d_7x7, over_sample_size=2) + g0 = al.Galaxy( redshift=0.1, light_profile=al.lp.Sersic(intensity=0.1), @@ -369,12 +268,16 @@ def test__image_2d_from__sum_of_individual_images(grid_2d_7x7, grid_2d_7x7_simpl tracer = al.Tracer(galaxies=[g0, g1], cosmology=al.cosmo.Planck15()) - traced_grid_2d_list_from = tracer.traced_grid_2d_list_from(grid=grid_2d_7x7) + traced_grid_2d_list = tracer.traced_grid_2d_list_from( + grid=al.Grid2DIrregular(grid_2d_7x7.over_sampled) + ) - image = g0.image_2d_from(grid=grid_2d_7x7) + g1.image_2d_from( - grid=traced_grid_2d_list_from[1] + image = g0.image_2d_from(grid=traced_grid_2d_list[0]) + g1.image_2d_from( + grid=traced_grid_2d_list[1] ) + image = grid_2d_7x7.over_sampler.binned_array_2d_from(array=image) + image_tracer = tracer.image_2d_from(grid=grid_2d_7x7) assert image.shape_native == (7, 7) @@ -382,7 +285,9 @@ def test__image_2d_from__sum_of_individual_images(grid_2d_7x7, grid_2d_7x7_simpl def test__image_2d_via_input_plane_image_from__with_foreground_planes(grid_2d_7x7): - plane_grid = al.Grid2D.uniform(shape_native=(40, 40), pixel_scales=0.3) + plane_grid = al.Grid2D.uniform( + shape_native=(40, 40), pixel_scales=0.3, over_sample_size=1 + ) g0 = al.Galaxy( redshift=0.5, @@ -415,7 +320,7 @@ def test__image_2d_via_input_plane_image_from__without_foreground_planes( grid_2d_7x7 = al.Grid2D( values=grid_2d_7x7, mask=grid_2d_7x7.mask, - over_sampling=al.OverSamplingUniform(sub_size=2), + over_sample_size=2, ) g0 = al.Galaxy( @@ -451,8 +356,7 @@ def test__image_2d_via_input_plane_image_from__with_foreground_planes__multi_pla grid_2d_7x7, ): plane_grid = al.Grid2D.uniform( - shape_native=(40, 40), - pixel_scales=0.3, + shape_native=(40, 40), pixel_scales=0.3, over_sample_size=1 ) g0 = al.Galaxy( @@ -558,9 +462,7 @@ def test__light_profile_snr__signal_to_noise_via_simulator_correct(): def test__galaxy_image_2d_dict_from(grid_2d_7x7, mask_2d_7x7): - grid_2d_7x7 = al.Grid2D.from_mask( - mask=mask_2d_7x7, over_sampling=al.OverSamplingUniform(sub_size=2) - ) + grid_2d_7x7 = al.Grid2D.from_mask(mask=mask_2d_7x7, over_sample_size=2) g0 = al.Galaxy(redshift=0.5, light_profile=al.lp.Sersic(intensity=1.0)) g1 = al.Galaxy( @@ -935,136 +837,6 @@ 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.Grid2D.uniform( - shape_native=(7, 7), - pixel_scales=1.0, - over_sampling=al.OverSamplingIterate( - fractional_accuracy=0.99, sub_steps=[2, 4] - ), - ) - - convergence = tracer.convergence_2d_from(grid=grid) - max_indexes = np.unravel_index( - convergence.native.argmax(), convergence.shape_native - ) - assert max_indexes == (1, 4) - - potential = tracer.potential_2d_from(grid=grid) - max_indexes = np.unravel_index(potential.native.argmin(), potential.shape_native) - assert max_indexes == (1, 4) - - deflections = tracer.deflections_yx_2d_from(grid=grid) - assert deflections.native[1, 4, 0] >= -1e-8 - assert deflections.native[2, 4, 0] <= 0 - assert deflections.native[1, 4, 1] >= 0 - assert deflections.native[1, 3, 1] <= 0 - - -def test__decorators__grid_iterate_in__iterates_array_result_correctly(gal_x1_lp): - mask = al.Mask2D( - mask=[ - [True, True, True, True, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - origin=(0.001, 0.001), - ) - - grid = al.Grid2D.from_mask( - mask=mask, - over_sampling=al.OverSamplingIterate(fractional_accuracy=1.0, sub_steps=[2]), - ) - - tracer = al.Tracer(galaxies=[gal_x1_lp]) - - image = tracer.image_2d_from(grid=grid) - - grid_sub_2 = al.Grid2D( - values=grid, mask=mask, over_sampling=al.OverSamplingUniform(sub_size=2) - ) - image_sub_2 = tracer.image_2d_from(grid=grid_sub_2) - - assert (image == image_sub_2).all() - - grid = al.Grid2D.from_mask( - mask=mask, - over_sampling=al.OverSamplingIterate( - fractional_accuracy=0.95, sub_steps=[2, 4, 8] - ), - ) - - galaxy = al.Galaxy( - redshift=0.5, light=al.lp.Sersic(centre=(0.08, 0.08), intensity=1.0) - ) - - tracer = al.Tracer(galaxies=[galaxy]) - - image = tracer.image_2d_from(grid=grid) - - grid_sub_4 = al.Grid2D( - values=grid, mask=mask, over_sampling=al.OverSamplingUniform(sub_size=4) - ) - image_sub_4 = tracer.image_2d_from(grid=grid_sub_4) - - assert image[0] == image_sub_4[0] - - -def test__decorators__grid_iterate_in__method_returns_array_list__uses_highest_sub_size_of_iterate( - gal_x1_lp, -): - mask = al.Mask2D( - mask=[ - [True, True, True, True, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, False, False, False, True], - [True, True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - origin=(0.001, 0.001), - ) - - grid = al.Grid2D.from_mask( - mask=mask, - over_sampling=al.OverSamplingIterate(fractional_accuracy=1.0, sub_steps=[2]), - ) - - tracer = al.Tracer(galaxies=[gal_x1_lp]) - - images = tracer.image_2d_list_from(grid=grid) - - grid_sub_2 = al.Grid2D( - values=grid, mask=mask, over_sampling=al.OverSamplingUniform(sub_size=2) - ) - image_sub_2 = tracer.image_2d_from(grid=grid_sub_2) - - assert (images[0] == image_sub_2).all() - - grid = al.Grid2D.from_mask( - mask=mask, - over_sampling=al.OverSamplingIterate( - fractional_accuracy=0.95, sub_steps=[2, 4, 8] - ), - ) - - galaxy = al.Galaxy( - redshift=0.5, light=al.lp.Sersic(centre=(0.08, 0.08), intensity=1.0) - ) - - tracer = al.Tracer(galaxies=[galaxy]) - - images = tracer.image_2d_list_from(grid=grid) - - grid_sub_8 = al.Grid2D( - values=grid, mask=mask, over_sampling=al.OverSamplingUniform(sub_size=8) - ) - image_sub_8 = tracer.image_2d_from(grid=grid_sub_8) - - assert images[0][4] == image_sub_8[4] - def test__instance_into_tracer__retains_dictionary_access(): model = af.Collection( diff --git a/test_autolens/point/files/point_dict.json b/test_autolens/point/files/point_dict.json index ab232721d..6f3bd0e8e 100644 --- a/test_autolens/point/files/point_dict.json +++ b/test_autolens/point/files/point_dict.json @@ -1,36 +1,36 @@ -[ - { - "name": "source_1", - "positions": [ - [ - 1.0, - 1.0 - ] - ], - "positions_noise_map": [ - 1.0 - ], - "fluxes": null, - "fluxes_noise_map": null - }, - { - "name": "source_2", - "positions": [ - [ - 1.0, - 1.0 - ] - ], - "positions_noise_map": [ - 1.0 - ], - "fluxes": [ - 2.0, - 3.0 - ], - "fluxes_noise_map": [ - 4.0, - 5.0 - ] - } +[ + { + "name": "source_1", + "positions": [ + [ + 1.0, + 1.0 + ] + ], + "positions_noise_map": [ + 1.0 + ], + "fluxes": null, + "fluxes_noise_map": null + }, + { + "name": "source_2", + "positions": [ + [ + 1.0, + 1.0 + ] + ], + "positions_noise_map": [ + 1.0 + ], + "fluxes": [ + 2.0, + 3.0 + ], + "fluxes_noise_map": [ + 4.0, + 5.0 + ] + } ] \ No newline at end of file diff --git a/test_autolens/point/fit/positions/image/test_abstract.py b/test_autolens/point/fit/positions/image/test_abstract.py index f82779813..89cd0c3df 100644 --- a/test_autolens/point/fit/positions/image/test_abstract.py +++ b/test_autolens/point/fit/positions/image/test_abstract.py @@ -77,8 +77,8 @@ def test__multi_plane_position_solving(): ) assert fit_0.model_data[0, 0] == pytest.approx( - scaling_factor * fit_1.model_data[0, 0], 1.0e-1 + scaling_factor * fit_1.model_data[1, 0], 1.0e-1 ) - assert fit_0.model_data[0, 1] == pytest.approx( + assert fit_0.model_data[1, 1] == pytest.approx( scaling_factor * fit_1.model_data[0, 1], 1.0e-1 ) diff --git a/test_autolens/point/fit/positions/image/test_pair_all.py b/test_autolens/point/fit/positions/image/test_pair_all.py index 3f2c5f917..c95af0d02 100644 --- a/test_autolens/point/fit/positions/image/test_pair_all.py +++ b/test_autolens/point/fit/positions/image/test_pair_all.py @@ -1,32 +1,114 @@ +try: + import jax + + JAX_INSTALLED = True +except ImportError: + JAX_INSTALLED = False + import numpy as np import pytest import autolens as al +point = al.ps.Point(centre=(0.1, 0.1)) +galaxy = al.Galaxy(redshift=1.0, point_0=point) +tracer = al.Tracer(galaxies=[al.Galaxy(redshift=0.5), galaxy]) + + +@pytest.fixture +def data(): + return np.array([(0.0, 0.0), (1.0, 0.0)]) -def test__three_sets_of_positions__model_is_repeated__does_not_double_count(): - point = al.ps.Point(centre=(0.1, 0.1)) - galaxy = al.Galaxy(redshift=1.0, point_0=point) - tracer = al.Tracer(galaxies=[al.Galaxy(redshift=0.5), galaxy]) - data = al.Grid2DIrregular([(2.0, 0.0), (1.0, 0.0), (0.0, 0.0)]) - noise_map = al.ArrayIrregular([0.5, 1.0, 2.0]) - model_data = al.Grid2DIrregular([(4.0, 0.0), (3.0, 0.0), (0.0, 0.0)]) +@pytest.fixture +def noise_map(): + return np.array([1.0, 1.0]) - solver = al.m.MockPointSolver(model_positions=model_data) +@pytest.fixture +def fit(data, noise_map): + model_positions = np.array( + [ + (-1.0749, -1.1), + (1.19117, 1.175), + ] + ) + + return al.FitPositionsImagePairAll( + name="point_0", + data=data, + noise_map=noise_map, + tracer=tracer, + solver=al.mock.MockPointSolver(model_positions), + ) + + +def test_andrew_implementation(fit): + assert np.allclose( + fit.all_permutations_log_likelihoods(), + [ + -1.51114426, + -1.50631469, + ], + ) + assert fit.chi_squared == -2.0 * -4.40375330990644 + + +@pytest.mark.skipif(not JAX_INSTALLED, reason="JAX is not installed") +def test_jax(fit): + assert jax.jit(fit.log_likelihood)() == -4.40375330990644 + + +def test_nan_model_positions( + data, + noise_map, +): + model_positions = np.array( + [ + (-1.0749, -1.1), + (1.19117, 1.175), + (np.nan, np.nan), + ] + ) fit = al.FitPositionsImagePairAll( name="point_0", data=data, noise_map=noise_map, tracer=tracer, - solver=solver, + solver=al.mock.MockPointSolver(model_positions), ) - print(fit.residual_map) + assert np.allclose( + fit.all_permutations_log_likelihoods(), + [ + -1.51114426, + -1.50631469, + ], + ) + assert fit.chi_squared == -2.0 * -4.40375330990644 + - assert fit.model_data.in_list == [(4.0, 0.0), (3.0, 0.0), (0.0, 0.0)] - assert fit.residual_map.in_list == [2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 2.0, 1.0, 0.0] +def test_duplicate_model_position( + data, + noise_map, +): + model_positions = np.array( + [ + (-1.0749, -1.1), + (1.19117, 1.175), + (1.19117, 1.175), + ] + ) + fit = al.FitPositionsImagePairAll( + name="point_0", + data=data, + noise_map=noise_map, + tracer=tracer, + solver=al.mock.MockPointSolver(model_positions), + ) - print(fit.noise_map) - print(fit.normalized_residual_map) + assert np.allclose( + fit.all_permutations_log_likelihoods(), + [-1.14237812, -0.87193683], + ) + assert fit.chi_squared == -2.0 * -4.211539531047171 diff --git a/test_autolens/point/fit/positions/source/test_separations.py b/test_autolens/point/fit/positions/source/test_separations.py index 8384950fb..85cdbe550 100644 --- a/test_autolens/point/fit/positions/source/test_separations.py +++ b/test_autolens/point/fit/positions/source/test_separations.py @@ -24,8 +24,11 @@ def test__two_sets_of_positions__residuals_likelihood_correct(): assert fit.noise_normalization == pytest.approx(2.28945, 1.0e-4) assert fit.log_likelihood == pytest.approx(-5.14472988, 1.0e-4) + # Inclusion of mass model below means there are nonzero magnifications at each position, which get factored into + # chi-squared calculation. + galaxy_mass = al.Galaxy( - redshift=0.5, mass=al.mp.IsothermalSph(centre=(0.0, 0.0), einstein_radius=1.0) + redshift=0.5, mass=al.mp.IsothermalSph(centre=(0.0, 0.0), einstein_radius=0.1) ) tracer = al.Tracer(galaxies=[galaxy_mass, galaxy_point_source]) @@ -34,8 +37,13 @@ def test__two_sets_of_positions__residuals_likelihood_correct(): name="point_0", data=positions, noise_map=noise_map, tracer=tracer, solver=None ) - assert fit.model_data.in_list == [(0.0, 0.0), (0.0, 1.0)] - assert fit.log_likelihood == pytest.approx(-1.6447298, 1.0e-4) + assert fit.magnifications_at_positions.in_list == pytest.approx( + [1.1111049387688177, 1.0526308864400329], 1.0e-4 + ) + assert fit.model_data.in_list == [(0.0, 0.9), (0.0, 1.9)] + + assert fit.chi_squared_map.in_list == pytest.approx([3.9999555592589244, 3.9999947369459807], 1.0e-4) + assert fit.log_likelihood == pytest.approx(-4.98805743691215, 1.0e-4) def test__multi_plane_position_solving(): diff --git a/test_autolens/point/fit/test_abstract.py b/test_autolens/point/fit/test_abstract.py new file mode 100644 index 000000000..d8dc63ec7 --- /dev/null +++ b/test_autolens/point/fit/test_abstract.py @@ -0,0 +1,56 @@ +from functools import partial +import pytest + +import autolens as al + + +def test__magnifications_at_positions__multi_plane_calculation(gal_x1_mp): + g0 = al.Galaxy(redshift=0.5, mass=al.mp.IsothermalSph(einstein_radius=1.0)) + g1 = al.Galaxy(redshift=1.0, point_0=al.ps.PointFlux(flux=1.0)) + g2 = al.Galaxy(redshift=2.0, point_1=al.ps.PointFlux(flux=2.0)) + + tracer = al.Tracer(galaxies=[g0, g1, g2]) + + data = al.ArrayIrregular([1.0]) + noise_map = al.ArrayIrregular([3.0]) + positions = al.Grid2DIrregular([(2.0, 0.0)]) + + fit_0 = al.FitFluxes( + name="point_0", + data=data, + noise_map=noise_map, + positions=positions, + tracer=tracer, + ) + + deflections_func = partial( + tracer.deflections_between_planes_from, plane_i=0, plane_j=1 + ) + + magnification_0 = tracer.magnification_2d_via_hessian_from( + grid=positions, deflections_func=deflections_func + ) + + assert fit_0.magnifications_at_positions[0] == magnification_0 + + fit_1 = al.FitFluxes( + name="point_1", + data=data, + noise_map=noise_map, + positions=positions, + tracer=tracer, + ) + + deflections_func = partial( + tracer.deflections_between_planes_from, plane_i=0, plane_j=2 + ) + + magnification_1 = tracer.magnification_2d_via_hessian_from( + grid=positions, deflections_func=deflections_func + ) + + assert fit_1.magnifications_at_positions[0] == magnification_1 + + assert fit_0.magnifications_at_positions[0] != pytest.approx( + fit_1.magnifications_at_positions[0], 1.0e-1 + ) diff --git a/test_autolens/point/fit/test_fit_dataset.py b/test_autolens/point/fit/test_fit_dataset.py index af61c28b0..36bea90db 100644 --- a/test_autolens/point/fit/test_fit_dataset.py +++ b/test_autolens/point/fit/test_fit_dataset.py @@ -76,7 +76,7 @@ def test__fit_dataset__all_positions_classes(): fit_positions_cls=al.FitPositionsImagePairAll, ) - assert fit.positions.log_likelihood == pytest.approx(-47.78945977, 1.0e-4) + assert fit.positions.log_likelihood == pytest.approx(-24.6435280294, 1.0e-4) fit = al.FitPointDataset( dataset=dataset_0, diff --git a/test_autolens/point/fit/test_fluxes.py b/test_autolens/point/fit/test_fluxes.py index bbb030a9f..ef1920cd9 100644 --- a/test_autolens/point/fit/test_fluxes.py +++ b/test_autolens/point/fit/test_fluxes.py @@ -51,53 +51,3 @@ def test__use_real_tracer(gal_x1_mp): assert fit.model_fluxes.in_list[1] == pytest.approx(2.5, 1.0e-4) assert fit.log_likelihood == pytest.approx(-3.11702, 1.0e-4) - - -def test__multi_plane_calculation(gal_x1_mp): - g0 = al.Galaxy(redshift=0.5, mass=al.mp.IsothermalSph(einstein_radius=1.0)) - g1 = al.Galaxy(redshift=1.0, point_0=al.ps.PointFlux(flux=1.0)) - g2 = al.Galaxy(redshift=2.0, point_1=al.ps.PointFlux(flux=2.0)) - - tracer = al.Tracer(galaxies=[g0, g1, g2]) - - data = al.ArrayIrregular([1.0]) - noise_map = al.ArrayIrregular([3.0]) - positions = al.Grid2DIrregular([(2.0, 0.0)]) - - fit_0 = al.FitFluxes( - name="point_0", - data=data, - noise_map=noise_map, - positions=positions, - tracer=tracer, - ) - - deflections_func = partial( - tracer.deflections_between_planes_from, plane_i=0, plane_j=1 - ) - - magnification_0 = tracer.magnification_2d_via_hessian_from( - grid=positions, deflections_func=deflections_func - ) - - assert fit_0.magnifications[0] == magnification_0 - - fit_1 = al.FitFluxes( - name="point_1", - data=data, - noise_map=noise_map, - positions=positions, - tracer=tracer, - ) - - deflections_func = partial( - tracer.deflections_between_planes_from, plane_i=0, plane_j=2 - ) - - magnification_1 = tracer.magnification_2d_via_hessian_from( - grid=positions, deflections_func=deflections_func - ) - - assert fit_1.magnifications[0] == magnification_1 - - assert fit_0.magnifications[0] != pytest.approx(fit_1.magnifications[0], 1.0e-1) diff --git a/test_autolens/point/model/test_analysis_point.py b/test_autolens/point/model/test_analysis_point.py index bcf35aa3b..1d31b02df 100644 --- a/test_autolens/point/model/test_analysis_point.py +++ b/test_autolens/point/model/test_analysis_point.py @@ -8,7 +8,7 @@ directory = path.dirname(path.realpath(__file__)) -def test__make_result__result_imaging_is_returned(point_dataset): +def _test__make_result__result_imaging_is_returned(point_dataset): model = af.Collection( galaxies=af.Collection( lens=al.Galaxy(redshift=0.5, point_0=al.ps.Point(centre=(0.0, 0.0))) diff --git a/test_autolens/point/model/test_plotter_interface_point.py b/test_autolens/point/model/test_plotter_interface_point.py new file mode 100644 index 000000000..ebe613bec --- /dev/null +++ b/test_autolens/point/model/test_plotter_interface_point.py @@ -0,0 +1,24 @@ +import os +import shutil +from os import path + +import pytest +from autolens.point.model.plotter_interface import PlotterInterfacePoint + +directory = path.dirname(path.realpath(__file__)) + + +@pytest.fixture(name="plot_path") +def make_plotter_interface_plotter_setup(): + return path.join("{}".format(directory), "files") + + +def test__fit_point(fit_point_dataset_x2_plane, include_2d_all, plot_path, plot_patch): + if os.path.exists(plot_path): + shutil.rmtree(plot_path) + + plotter_interface = PlotterInterfacePoint(image_path=plot_path) + + plotter_interface.fit_point(fit=fit_point_dataset_x2_plane) + + assert path.join(plot_path, "subplot_fit.png") in plot_patch.paths diff --git a/test_autolens/point/plot/test_fit_point_plotters.py b/test_autolens/point/plot/test_fit_point_plotters.py index 278fe9b8f..d544e8de7 100644 --- a/test_autolens/point/plot/test_fit_point_plotters.py +++ b/test_autolens/point/plot/test_fit_point_plotters.py @@ -29,15 +29,15 @@ def test__fit_point_quantities_are_output( fit_point_plotter.figures_2d(positions=True, fluxes=True) - assert path.join(plot_path, "fit_point_dataset_positions.png") in plot_patch.paths - assert path.join(plot_path, "fit_point_dataset_fluxes.png") in plot_patch.paths + assert path.join(plot_path, "fit_point_positions.png") in plot_patch.paths + assert path.join(plot_path, "fit_point_fluxes.png") in plot_patch.paths plot_patch.paths = [] fit_point_plotter.figures_2d(positions=True, fluxes=False) - assert path.join(plot_path, "fit_point_dataset_positions.png") in plot_patch.paths - assert path.join(plot_path, "fit_point_dataset_fluxes.png") not in plot_patch.paths + assert path.join(plot_path, "fit_point_positions.png") in plot_patch.paths + assert path.join(plot_path, "fit_point_fluxes.png") not in plot_patch.paths plot_patch.paths = [] @@ -51,8 +51,8 @@ def test__fit_point_quantities_are_output( fit_point_plotter.figures_2d(positions=True, fluxes=True) - assert path.join(plot_path, "fit_point_dataset_positions.png") in plot_patch.paths - assert path.join(plot_path, "fit_point_dataset_fluxes.png") not in plot_patch.paths + assert path.join(plot_path, "fit_point_positions.png") in plot_patch.paths + assert path.join(plot_path, "fit_point_fluxes.png") not in plot_patch.paths def test__subplot_fit( diff --git a/test_autolens/point/test_point_source_dataset.py b/test_autolens/point/test_point_source_dataset.py index d0a53325c..2e10fd722 100644 --- a/test_autolens/point/test_point_source_dataset.py +++ b/test_autolens/point/test_point_source_dataset.py @@ -1,14 +1,14 @@ -import autolens as al - - -def test__info(): - dataset = al.PointDataset( - "name", - positions=al.Grid2DIrregular([(1, 2)]), - positions_noise_map=al.ArrayIrregular([1]), - fluxes=al.ArrayIrregular([2]), - fluxes_noise_map=al.ArrayIrregular([3]), - ) - - assert "name" in dataset.info - assert "positions : Grid2DIrregular" in dataset.info +import autolens as al + + +def test__info(): + dataset = al.PointDataset( + "name", + positions=al.Grid2DIrregular([(1, 2)]), + positions_noise_map=al.ArrayIrregular([1]), + fluxes=al.ArrayIrregular([2]), + fluxes_noise_map=al.ArrayIrregular([3]), + ) + + assert "name" in dataset.info + assert "positions : Grid2DIrregular" in dataset.info diff --git a/test_autolens/point/triangles/conftest.py b/test_autolens/point/triangles/conftest.py index 3c3620630..1aec734ea 100644 --- a/test_autolens/point/triangles/conftest.py +++ b/test_autolens/point/triangles/conftest.py @@ -1,30 +1,30 @@ -import pytest -import autolens as al - - -@pytest.fixture -def grid(): - return al.Grid2D.uniform( - shape_native=(10, 10), - pixel_scales=1.0, - ) - - -@pytest.fixture -def tracer(): - isothermal_mass_profile = al.mp.Isothermal( - centre=(0.0, 0.0), - einstein_radius=1.6, - ell_comps=al.convert.ell_comps_from(axis_ratio=0.9, angle=45.0), - ) - - lens_galaxy = al.Galaxy( - redshift=0.5, - mass=isothermal_mass_profile, - ) - - point_source = al.ps.Point(centre=(0.07, 0.07)) - - source_galaxy = al.Galaxy(redshift=1.0, point_0=point_source) - - return al.Tracer(galaxies=[lens_galaxy, source_galaxy]) +import pytest +import autolens as al + + +@pytest.fixture +def grid(): + return al.Grid2D.uniform( + shape_native=(10, 10), + pixel_scales=1.0, + ) + + +@pytest.fixture +def tracer(): + isothermal_mass_profile = al.mp.Isothermal( + centre=(0.0, 0.0), + einstein_radius=1.6, + ell_comps=al.convert.ell_comps_from(axis_ratio=0.9, angle=45.0), + ) + + lens_galaxy = al.Galaxy( + redshift=0.5, + mass=isothermal_mass_profile, + ) + + point_source = al.ps.Point(centre=(0.07, 0.07)) + + source_galaxy = al.Galaxy(redshift=1.0, point_0=point_source) + + return al.Tracer(galaxies=[lens_galaxy, source_galaxy]) diff --git a/test_autolens/point/triangles/test_extended.py b/test_autolens/point/triangles/test_extended.py index 1c07b0772..a0d21666a 100644 --- a/test_autolens/point/triangles/test_extended.py +++ b/test_autolens/point/triangles/test_extended.py @@ -1,5 +1,6 @@ import pytest +from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles from autoarray.structures.triangles.shape import Circle from autolens.mock import NullTracer from autolens.point.solver.shape_solver import ShapeSolver @@ -9,7 +10,8 @@ def solver(grid): return ShapeSolver.for_grid( grid=grid, - pixel_scale_precision=0.01, + pixel_scale_precision=0.001, + array_triangles_cls=CoordinateArrayTriangles, ) @@ -19,6 +21,6 @@ def test_solver_basic(solver): shape=Circle( 0.0, 0.0, - radius=0.01, + radius=0.1, ), - ) == pytest.approx(1.0, abs=0.01) + ) == pytest.approx(1.0, abs=0.1) diff --git a/test_autolens/point/triangles/test_jax.py b/test_autolens/point/triangles/test_jax.py index aac41330b..6f3b30e31 100644 --- a/test_autolens/point/triangles/test_jax.py +++ b/test_autolens/point/triangles/test_jax.py @@ -1,12 +1,12 @@ -import autofit as af -from autogalaxy.profiles.mass import Isothermal - - -def test_isothermal_pytree(): - model = af.Model(Isothermal) - - children, aux = model.instance_flatten(Isothermal()) - instance = model.instance_unflatten(aux, children) - - assert isinstance(instance, Isothermal) - assert instance.centre == (0.0, 0.0) +import autofit as af +from autogalaxy.profiles.mass import Isothermal + + +def test_isothermal_pytree(): + model = af.Model(Isothermal) + + children, aux = model.instance_flatten(Isothermal()) + instance = model.instance_unflatten(aux, children) + + assert isinstance(instance, Isothermal) + assert instance.centre == (0.0, 0.0) diff --git a/test_autolens/point/triangles/test_regressions.py b/test_autolens/point/triangles/test_regressions.py index f4aa1dbab..c1fc9d5db 100644 --- a/test_autolens/point/triangles/test_regressions.py +++ b/test_autolens/point/triangles/test_regressions.py @@ -1,76 +1,76 @@ -from autoconf.dictable import from_dict -import autolens as al -from autolens.point.solver import PointSolver - - -instance_dict = { - "type": "instance", - "class_path": "autofit.mapper.model.ModelInstance", - "arguments": { - "child_items": { - "type": "dict", - "arguments": { - "source_galaxy": { - "type": "instance", - "class_path": "autogalaxy.galaxy.galaxy.Galaxy", - "arguments": { - "redshift": 1.0, - "label": "cls6", - "light": { - "type": "instance", - "class_path": "autogalaxy.profiles.light.standard.exponential.Exponential", - "arguments": { - "effective_radius": 0.1, - "ell_comps": [0.4731722153284571, -0.27306667016189645], - "centre": [-0.04829335038475, 0.02350935356045], - "intensity": 0.1, - }, - }, - "point_0": { - "type": "instance", - "class_path": "autogalaxy.profiles.point_source.Point", - "arguments": { - "centre": [-0.04829335038475, 0.02350935356045] - }, - }, - }, - }, - "lens_galaxy": { - "type": "instance", - "class_path": "autogalaxy.galaxy.galaxy.Galaxy", - "arguments": { - "redshift": 0.5, - "label": "cls6", - "mass": { - "type": "instance", - "class_path": "autogalaxy.profiles.mass.total.isothermal.Isothermal", - "arguments": { - "ell_comps": [ - 0.05263157894736841, - 3.2227547345982974e-18, - ], - "einstein_radius": 1.6, - "centre": [0.0, 0.0], - }, - }, - }, - }, - }, - } - }, -} - - -def test_missing_multiple_image(grid): - instance = from_dict(instance_dict) - - tracer = al.Tracer(galaxies=[instance.lens_galaxy, instance.source_galaxy]) - - solver = PointSolver.for_grid( - grid=grid, - pixel_scale_precision=0.001, - ) - - triangle_positions = solver.solve( - tracer=tracer, source_plane_coordinate=instance.source_galaxy.point_0.centre - ) +from autoconf.dictable import from_dict +import autolens as al +from autolens.point.solver import PointSolver + + +instance_dict = { + "type": "instance", + "class_path": "autofit.mapper.model.ModelInstance", + "arguments": { + "child_items": { + "type": "dict", + "arguments": { + "source_galaxy": { + "type": "instance", + "class_path": "autogalaxy.galaxy.galaxy.Galaxy", + "arguments": { + "redshift": 1.0, + "label": "cls6", + "light": { + "type": "instance", + "class_path": "autogalaxy.profiles.light.standard.exponential.Exponential", + "arguments": { + "effective_radius": 0.1, + "ell_comps": [0.4731722153284571, -0.27306667016189645], + "centre": [-0.04829335038475, 0.02350935356045], + "intensity": 0.1, + }, + }, + "point_0": { + "type": "instance", + "class_path": "autogalaxy.profiles.point_source.Point", + "arguments": { + "centre": [-0.04829335038475, 0.02350935356045] + }, + }, + }, + }, + "lens_galaxy": { + "type": "instance", + "class_path": "autogalaxy.galaxy.galaxy.Galaxy", + "arguments": { + "redshift": 0.5, + "label": "cls6", + "mass": { + "type": "instance", + "class_path": "autogalaxy.profiles.mass.total.isothermal.Isothermal", + "arguments": { + "ell_comps": [ + 0.05263157894736841, + 3.2227547345982974e-18, + ], + "einstein_radius": 1.6, + "centre": [0.0, 0.0], + }, + }, + }, + }, + }, + } + }, +} + + +def test_missing_multiple_image(grid): + instance = from_dict(instance_dict) + + tracer = al.Tracer(galaxies=[instance.lens_galaxy, instance.source_galaxy]) + + solver = PointSolver.for_grid( + grid=grid, + pixel_scale_precision=0.001, + ) + + triangle_positions = solver.solve( + tracer=tracer, source_plane_coordinate=instance.source_galaxy.point_0.centre + ) diff --git a/test_autolens/point/triangles/test_solver.py b/test_autolens/point/triangles/test_solver.py index 6a5334bbe..62b3b35dc 100644 --- a/test_autolens/point/triangles/test_solver.py +++ b/test_autolens/point/triangles/test_solver.py @@ -1,10 +1,11 @@ from typing import Tuple +import numpy as np import pytest import autolens as al import autogalaxy as ag -from autoarray.structures.triangles.jax_coordinate_array import ArrayTriangles +from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles from autolens.mock import NullTracer from autolens.point.solver import PointSolver @@ -62,21 +63,33 @@ def test_trivial( solver = PointSolver.for_grid( grid=grid, pixel_scale_precision=0.01, - array_triangles_cls=ArrayTriangles, ) - (coordinates,) = solver.solve( + coordinates = solver.solve( tracer=NullTracer(), source_plane_coordinate=source_plane_coordinate, ) - assert coordinates == pytest.approx(source_plane_coordinate, abs=1.0e-1) + assert coordinates[0] == pytest.approx(source_plane_coordinate, abs=1.0e-1) -def test_real_example(grid, tracer): - solver = PointSolver.for_grid( + +def triangle_set(triangles): + return { + tuple(sorted([tuple(np.round(pair, 4)) for pair in triangle])) + for triangle in triangles.triangles.tolist() + if not np.isnan(triangle).any() + } + + +def test_real_example_normal(grid, tracer): + jax_solver = PointSolver.for_grid( grid=grid, pixel_scale_precision=0.001, - array_triangles_cls=ArrayTriangles, + array_triangles_cls=CoordinateArrayTriangles, + ) + + result = jax_solver.solve( + tracer=tracer, + source_plane_coordinate=(0.07, 0.07), ) - result = solver.solve(tracer=tracer, source_plane_coordinate=(0.07, 0.07)) assert len(result) == 5 diff --git a/test_autolens/point/triangles/test_solver_jax.py b/test_autolens/point/triangles/test_solver_jax.py index ff01ae9a7..ce9ae5a82 100644 --- a/test_autolens/point/triangles/test_solver_jax.py +++ b/test_autolens/point/triangles/test_solver_jax.py @@ -6,114 +6,136 @@ import autogalaxy as ag import autofit as af import numpy as np -from autolens import PointSolver - -try: - from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles -except ImportError: - from autoarray.structures.triangles.jax_coordinate_array import ( - CoordinateArrayTriangles, - ) - -from autolens.mock import NullTracer - -pytest.importorskip("jax") - - -@pytest.fixture(autouse=True) -def register(tracer): - af.Model.from_instance(tracer) - - -@pytest.fixture -def solver(grid): - return PointSolver.for_grid( - grid=grid, - pixel_scale_precision=0.01, - array_triangles_cls=CoordinateArrayTriangles, - ) - - -def test_solver(solver): - tracer = ag.mp.Isothermal( - centre=(0.0, 0.0), - einstein_radius=1.0, - ) - assert solver.solve( - tracer, - source_plane_coordinate=(0.0, 0.0), - ) - - -@pytest.mark.parametrize( - "source_plane_coordinate", - [ - (0.0, 0.0), - (0.0, 1.0), - (1.0, 0.0), - (1.0, 1.0), - (0.5, 0.5), - (0.1, 0.1), - (-1.0, -1.0), - ], -) -def test_trivial( - source_plane_coordinate: Tuple[float, float], - grid, - solver, -): - coordinates = solver.solve( - NullTracer(), - source_plane_coordinate=source_plane_coordinate, - ) - coordinates = coordinates.array[~np.isnan(coordinates.array).any(axis=1)] - assert coordinates[0] == pytest.approx(source_plane_coordinate, abs=1.0e-1) - - -def test_real_example(grid, tracer): - solver = PointSolver.for_grid( - grid=grid, - pixel_scale_precision=0.001, - array_triangles_cls=CoordinateArrayTriangles, - ) - - result = solver.solve(tracer, (0.07, 0.07)) - assert len(result) == 5 - - -def _test_jax(grid): - sizes = (5, 10, 15, 20, 25, 30, 35, 40, 45, 50) - run_times = [] - init_times = [] - - for size in sizes: - start = time.time() - solver = PointSolver.for_grid( - grid=grid, - pixel_scale_precision=0.001, - array_triangles_cls=CoordinateArrayTriangles, - max_containing_size=size, - ) - - solver.solve(NullTracer(), (0.07, 0.07)) - - repeats = 100 - - done_init_time = time.time() - init_time = done_init_time - start - for _ in range(repeats): - _ = solver.solve(NullTracer(), (0.07, 0.07)) - - # print(result) - - init_times.append(init_time) - - run_time = (time.time() - done_init_time) / repeats - run_times.append(run_time) - - print(f"Time taken for {size}: {run_time} ({init_time} to init)") - - from matplotlib import pyplot as plt - - plt.plot(sizes, run_times) - plt.show() +from autolens import PointSolver, Tracer + +# +# try: +# from autoarray.structures.triangles.coordinate_array.jax_coordinate_array import ( +# CoordinateArrayTriangles, +# ) +# +# except ImportError: +# from autoarray.structures.triangles.coordinate_array import CoordinateArrayTriangles +# +# from autolens.mock import NullTracer +# +# pytest.importorskip("jax") +# +# +# @pytest.fixture(autouse=True) +# def register(tracer): +# af.Model.from_instance(tracer) +# +# +# @pytest.fixture +# def solver(grid): +# return PointSolver.for_grid( +# grid=grid, +# pixel_scale_precision=0.01, +# array_triangles_cls=CoordinateArrayTriangles, +# ) +# +# +# def test_solver(solver): +# mass_profile = ag.mp.Isothermal( +# centre=(0.0, 0.0), +# einstein_radius=1.0, +# ) +# tracer = Tracer( +# galaxies=[ag.Galaxy(redshift=0.5, mass=mass_profile)], +# ) +# result = solver.solve( +# tracer, +# source_plane_coordinate=(0.0, 0.0), +# ) +# print(result) +# assert result +# +# +# @pytest.mark.parametrize( +# "source_plane_coordinate", +# [ +# (0.0, 0.0), +# (0.0, 1.0), +# (1.0, 0.0), +# (1.0, 1.0), +# (0.5, 0.5), +# (0.1, 0.1), +# (-1.0, -1.0), +# ], +# ) +# def test_trivial( +# source_plane_coordinate: Tuple[float, float], +# grid, +# solver, +# ): +# coordinates = solver.solve( +# NullTracer(), +# source_plane_coordinate=source_plane_coordinate, +# ) +# coordinates = coordinates.array[~np.isnan(coordinates.array).any(axis=1)] +# assert coordinates[0] == pytest.approx(source_plane_coordinate, abs=1.0e-1) +# +# +# def test_real_example(grid, tracer): +# solver = PointSolver.for_grid( +# grid=grid, +# pixel_scale_precision=0.001, +# array_triangles_cls=CoordinateArrayTriangles, +# ) +# +# result = solver.solve(tracer, (0.07, 0.07)) +# assert len(result) == 5 +# +# +# def _test_jax(grid): +# sizes = (5, 10, 15, 20, 25, 30, 35, 40, 45, 50) +# run_times = [] +# init_times = [] +# +# for size in sizes: +# start = time.time() +# solver = PointSolver.for_grid( +# grid=grid, +# pixel_scale_precision=0.001, +# array_triangles_cls=CoordinateArrayTriangles, +# max_containing_size=size, +# ) +# +# solver.solve(NullTracer(), (0.07, 0.07)) +# +# repeats = 100 +# +# done_init_time = time.time() +# init_time = done_init_time - start +# for _ in range(repeats): +# _ = solver.solve(NullTracer(), (0.07, 0.07)) +# +# # print(result) +# +# init_times.append(init_time) +# +# run_time = (time.time() - done_init_time) / repeats +# run_times.append(run_time) +# +# print(f"Time taken for {size}: {run_time} ({init_time} to init)") +# +# from matplotlib import pyplot as plt +# +# plt.plot(sizes, run_times) +# plt.show() +# +# +# def test_real_example_jax(grid, tracer): +# jax_solver = PointSolver.for_grid( +# grid=grid, +# pixel_scale_precision=0.001, +# array_triangles_cls=CoordinateArrayTriangles, +# ) +# +# result = jax_solver.solve( +# tracer=tracer, +# source_plane_coordinate=(0.07, 0.07), +# ) +# +# assert len(result) == 5