Exemplo n.º 1
0
def make_sensitivity(perturbation_model, search):
    # noinspection PyTypeChecker
    instance = af.ModelInstance()
    instance.gaussian = Gaussian()
    return s.Sensitivity(
        simulation_instance=instance,
        base_model=af.Collection(gaussian=af.PriorModel(Gaussian)),
        perturbation_model=perturbation_model,
        simulate_function=image_function,
        analysis_class=Analysis,
        search=search,
        number_of_steps=2,
    )
Exemplo n.º 2
0
the data.

- `number_of_steps`: The number of steps over which the parameters in the `perturbation_model` are iterated. In this 
example, `mass_at_200` has a `LogUniformPrior` with lower limit 1e6 and upper limit 1e13, therefore 
the `number_of_steps` of 2 will simulate and fit just 2 datasets where the `mass_at_200` is between 1e6 and 1e13.

- `number_of_cores`: The number of cores over which the sensitivity mapping is performed, enabling parallel processing
if set above 1.
"""
from autofit.non_linear.grid import sensitivity as s

sensitivity = s.Sensitivity(
    search=search,
    simulation_instance=simulation_instance,
    base_model=base_model,
    perturbation_model=perturbation_model,
    simulate_function=simulate_function,
    analysis_class=AnalysisImagingSensitivity,
    number_of_steps=2,
)

sensitivity_result = sensitivity.run()
"""
You should now look at the results of the sensitivity mapping in the folder `output/features/sensitivity_mapping`. 

You will note the following 4 model-fits have been performed:

 - The `base_model` is fitted to a simulated dataset where a subhalo with `mass_at_200=1e6` is included.

 - The `base_model` + `perturbation_model` is fitted to a simulated dataset where a subhalo with `mass_at_200=1e6` 
 is included.
Exemplo n.º 3
0
def sensitivity_mapping(slam, uv_wavelengths, real_space_mask,
                        visibilities_mask, mass_results, analysis_cls):
    """
    To begin, we define the `base_model` that we use to perform sensitivity mapping. This is the lens model that is f
    itted to every simulated strong lens without a subhalo, giving us the Bayesian evidence which we compare to the model
    which includes one!).
    """

    path_prefix = slam.path_prefix_from(
        slam.path_prefix,
        "sensitivity",
        slam.source_tag,
        slam.mass_tag,
        slam.setup_subhalo.tag,
    )
    """
    We now define the `base_model` that we use to perform sensitivity mapping. This is the lens model that is fitted to 
    every simulated strong lens without a subhalo, giving us the Bayesian evidence which we compare to the model which 
    includes one!). 

    For this model, we can use the result of fitting this model to the dataset before sensitivity mapping via the 
    mass pipeline. This ensures the priors associated with each parameter are initialized so as to speed up
    each non-linear search performed during sensitivity mapping.
    """
    base_model = mass_results.last.model
    """
    We now define the `perturbation_model`, which is the model component whose parameters we iterate over to perform 
    sensitivity mapping. In this case, this model is a `SphericalNFWMCRLudlow` model and we will iterate over its
    `centre` and `mass_at_200`. We set it up as a `GalaxyModel` so it has an associated redshift and can be directly
    passed to the tracer in the simulate function below.

    Many instances of the `perturbation_model` are created and used to simulate the many strong lens datasets that we fit. 
    However, it is only included in half of the model-fits; corresponding to the lens models which include a dark matter 
    subhalo and whose Bayesian evidence we compare to the simpler model-fits consisting of just the `base_model` to 
    determine if the subhalo was detectable.

    By fitting both models to every simulated lens, we therefore infer the Bayesian evidence of every model to every 
    dataset. Sensitivity mapping therefore maps out for what values of `centre` and `mass_at_200` in the dark mattter 
    subhalo the model-fit including a subhalo provide higher values of Bayesian evidence than the simpler model-fit (and
    therefore when it is detectable!).
    """
    perturbation_model = al.GalaxyModel(redshift=0.5,
                                        mass=al.mp.SphericalNFWMCRLudlow)
    """
    Sensitivity mapping is typically performed over a large range of parameters. However, to make this demonstration quick
    and clear we are going to fix the `centre` of the subhalo to a value near the Einstein ring of (1.6, 0.0). We will 
    iterate over just two `mass_at_200` values corresponding to subhalos of mass 1e6 and 1e11, of which only the latter
    will be shown to be detectable.
    """
    perturbation_model.mass.mass_at_200 = af.LogUniformPrior(lower_limit=1e6,
                                                             upper_limit=1e11)
    perturbation_model.mass.centre.centre_0 = af.UniformPrior(
        lower_limit=-slam.setup_subhalo.grid_dimensions_arcsec,
        upper_limit=slam.setup_subhalo.grid_dimensions_arcsec,
    )
    perturbation_model.mass.centre.centre_1 = af.UniformPrior(
        lower_limit=-slam.setup_subhalo.grid_dimensions_arcsec,
        upper_limit=slam.setup_subhalo.grid_dimensions_arcsec,
    )
    perturbation_model.mass.redshift_object = slam.redshift_lens
    perturbation_model.mass.redshift_source = slam.redshift_source
    """
    We are performing sensitivity mapping to determine when a subhalo is detectable. Eery simulated dataset must 
    be simulated with a lens model, called the `simulation_instance`. We use the maximum likelihood model of the mass pipeline
    for this.

    This includes the lens light and mass and source galaxy light.
    """
    simulation_instance = mass_results.last.instance
    """
    We now write the `simulate_function`, which takes the `simulation_instance` of our model (defined above) and uses it to 
    simulate a dataset which is subsequently fitted.

    Note that when this dataset is simulated, the quantity `instance.perturbation` is used in the `simulate_function`.
    This is an instance of the `SphericalNFWMCRLudlow`, and it is different every time the `simulate_function` is called
    based on the value of sensitivity being computed. 

    In this example, this `instance.perturbation` corresponds to two different subhalos with values of `mass_at_200` of 
    1e6 MSun and 1e11 MSun.
    """
    def simulate_function(instance):
        """
        Set up the `Tracer` which is used to simulate the strong lens imaging, which may include the subhalo in
        addition to the lens and source galaxy.
        """
        tracer = al.Tracer.from_galaxies(galaxies=[
            instance.galaxies.lens,
            instance.perturbation,
            instance.galaxies.source,
        ])
        """
        Set up the grid, PSF and simulator settings used to simulate imaging of the strong lens. These should be tuned to
        match the S/N and noise properties of the observed data you are performing sensitivity mapping on.
        """
        grid = al.Grid2DIterate.uniform(
            shape_native=real_space_mask.shape_native,
            pixel_scales=real_space_mask.pixel_scales,
            fractional_accuracy=0.9999,
            sub_steps=[2, 4, 8, 16, 24],
        )

        simulator = al.SimulatorInterferometer(
            uv_wavelengths=uv_wavelengths,
            exposure_time=300.0,
            background_sky_level=0.1,
            noise_sigma=0.1,
            transformer_class=al.TransformerNUFFT,
        )

        simulated_interferometer = simulator.from_tracer_and_grid(
            tracer=tracer, grid=grid)
        """
        The data generated by the simulate function is that which is fitted, so we should apply the mask for the analysis 
        here before we return the simulated data.
        """
        return al.MaskedInterferometer(
            interferometer=simulated_interferometer,
            visibilities_mask=visibilities_mask,
            real_space_mask=real_space_mask,
        )

    """
    We next specify the search used to perform each model fit by the sensitivity mapper.
    """
    search = af.DynestyStatic(path_prefix=path_prefix, n_live_points=50)
    """
    We can now combine all of the objects created above and perform sensitivity mapping. The inputs to the `Sensitivity`
    object below are:

    - `simulation_instance`: This is an instance of the model used to simulate every dataset that is fitted. In this 
    example it is a lens model that does not include a subhalo, which was inferred by fitting the dataset we perform 
    sensitivity mapping on.

    - `base_model`: This is the lens model that is fitted to every simulated dataset, which does not include a subhalo. 
    In this example is composed of an `EllipticalIsothermal` lens and `EllipticalSersic` source.

    - `perturbation_model`: This is the extra model component that alongside the `base_model` is fitted to every 
    simulated dataset. In this example it is a `SphericalNFWMCRLudlow` dark matter subhalo.

    - `simulate_function`: This is the function that uses the `simulation_instance` and many instances of the 
    `perturbation_model` to simulate many datasets that are fitted with the `base_model` 
    and `base_model` + `perturbation_model`.

    - `analysis_class`: The wrapper `Analysis` class that passes each simulated dataset to the `Analysis` class that 
    fits the data.

    - `number_of_steps`: The number of steps over which the parameters in the `perturbation_model` are iterated. In 
    this example, `mass_at_200` has a `LogUniformPrior` with lower limit 1e6 and upper limit 1e11, therefore 
    the `number_of_steps` of 2 will simulate and fit just 2 datasets where the `mass_at_200` is between 1e6 and 1e11.

    - `number_of_cores`: The number of cores over which the sensitivity mapping is performed, enabling parallel 
    processing if set above 1.
    """
    return s.Sensitivity(
        search=search,
        simulation_instance=simulation_instance,
        base_model=base_model,
        perturbation_model=perturbation_model,
        simulate_function=simulate_function,
        analysis_class=analysis_cls,
        number_of_steps=slam.setup_subhalo.number_of_steps,
        number_of_cores=slam.setup_subhalo.number_of_cores,
    )
Exemplo n.º 4
0
def sensitivity_mapping_interferometer(
    path_prefix: str,
    uv_wavelengths: np.ndarray,
    real_space_mask: al.Mask2D,
    mass_results: af.ResultsCollection,
    analysis_cls: ClassVar[al.AnalysisInterferometer],
    subhalo_mass: af.Model(al.mp.MassProfile) = af.Model(
        al.mp.SphNFWMCRLudlow),
    grid_dimension_arcsec: float = 3.0,
    number_of_steps: Union[Tuple[int], int] = 5,
    number_of_cores: int = 1,
    unique_tag: Optional[str] = None,
    session: Optional[bool] = None,
):
    """
    The SLaM SUBHALO PIPELINE for performing sensitivity mapping to imaging data with or without a lens light
    component, which determines what mass subhalos are detected where in the dataset.

    Parameters
    ----------
    path_prefix
        The prefix of folders between the output path and the search folders.
    uv_wavelengths
        The wavelengths of the interferometer baselines used for mapping to Fourier space.
    real_space_mask
        The mask in real space which defines how lensed images are computed.
    mass_results
        The results of the SLaM MASS PIPELINE which ran before this pipeline.
    analysis_cls
        The analysis class which includes the `log_likelihood_function` and can be customized for the SLaM model-fit. A
        new instance of this class is created for every model-fit.
    subhalo_mass
        The `MassProfile` used to fit the subhalo in this pipeline.
    grid_dimension_arcsec
        the arc-second dimensions of the grid in the y and x directions. An input value of 3.0" means the grid in
        all four directions extends to 3.0" giving it dimensions 6.0" x 6.0".
    number_of_steps
        The 2D dimensions of the grid (e.g. number_of_steps x number_of_steps) that the subhalo search is performed for.
    number_of_cores
        The number of cores used to perform the non-linear search grid search. If 1, each model-fit on the grid is
        performed in serial, if > 1 fits are distributed in parallel using the Python multiprocessing module.
    unique_tag
        The unique tag for this model-fit, which will be given a unique entry in the sqlite database and also acts as
        the folder after the path prefix and before the search name. This is typically the name of the dataset.
    """
    """
    To begin, we define the `base_model` that we use to perform sensitivity mapping. This is the lens model that is f
    itted to every simulated strong lens without a subhalo, giving us the Bayesian evidence which we compare to the model
    which includes one!).
    """
    """
    We now define the `base_model` that we use to perform sensitivity mapping. This is the lens model that is fitted to 
    every simulated strong lens without a subhalo, giving us the Bayesian evidence which we compare to the model which 
    includes one!). 

    For this model, we can use the result of fitting this model to the dataset before sensitivity mapping via the 
    mass pipeline. This ensures the priors associated with each parameter are initialized so as to speed up
    each non-linear search performed during sensitivity mapping.
    """
    base_model = mass_results.last.model
    """
    We now define the `perturbation_model`, which is the model component whose parameters we iterate over to perform 
    sensitivity mapping. In this case, this model is a `SphNFWMCRLudlow` model and we will iterate over its
    `centre` and `mass_at_200`. We set it up as a `Model` so it has an associated redshift and can be directly
    passed to the tracer in the simulate function below.

    Many instances of the `perturbation_model` are created and used to simulate the many strong lens datasets that we fit. 
    However, it is only included in half of the model-fits; corresponding to the lens models which include a dark matter 
    subhalo and whose Bayesian evidence we compare to the simpler model-fits consisting of just the `base_model` to 
    determine if the subhalo was detectable.

    By fitting both models to every simulated lens, we therefore infer the Bayesian evidence of every model to every 
    dataset. Sensitivity mapping therefore maps out for what values of `centre` and `mass_at_200` in the dark mattter 
    subhalo the model-fit including a subhalo provide higher values of Bayesian evidence than the simpler model-fit (and
    therefore when it is detectable!).
    """
    perturbation_model = af.Model(al.Galaxy, redshift=0.5, mass=subhalo_mass)
    """
    Sensitivity mapping is typically performed over a large range of parameters. However, to make this demonstration quick
    and clear we are going to fix the `centre` of the subhalo to a value near the Einstein ring of (1.6, 0.0). We will 
    iterate over just two `mass_at_200` values corresponding to subhalos of mass 1e6 and 1e11, of which only the latter
    will be shown to be detectable.
    """
    perturbation_model.mass.mass_at_200 = af.LogUniformPrior(lower_limit=1e6,
                                                             upper_limit=1e11)
    perturbation_model.mass.centre.centre_0 = af.UniformPrior(
        lower_limit=-grid_dimension_arcsec, upper_limit=grid_dimension_arcsec)
    perturbation_model.mass.centre.centre_1 = af.UniformPrior(
        lower_limit=-grid_dimension_arcsec, upper_limit=grid_dimension_arcsec)
    perturbation_model.mass.redshift_object = (
        mass_results.last.model.galaxies.lens.redshift)
    perturbation_model.mass.redshift_source = (
        mass_results.last.model.galaxies.source.redshift)
    """
    We are performing sensitivity mapping to determine when a subhalo is detectable. Eery simulated dataset must 
    be simulated with a lens model, called the `simulation_instance`. We use the maximum likelihood model of the mass pipeline
    for this.

    This includes the lens light and mass and source galaxy light.
    """
    simulation_instance = mass_results.last.instance
    """
    We now write the `simulate_function`, which takes the `simulation_instance` of our model (defined above) and uses it to 
    simulate a dataset which is subsequently fitted.

    Note that when this dataset is simulated, the quantity `instance.perturbation` is used in the `simulate_function`.
    This is an instance of the `SphNFWMCRLudlow`, and it is different every time the `simulate_function` is called
    based on the value of sensitivity being computed. 

    In this example, this `instance.perturbation` corresponds to two different subhalos with values of `mass_at_200` of 
    1e6 MSun and 1e11 MSun.
    """
    def simulate_function(instance):
        """
        Set up the `Tracer` which is used to simulate the strong lens imaging, which may include the subhalo in
        addition to the lens and source galaxy.
        """
        tracer = al.Tracer.from_galaxies(galaxies=[
            instance.galaxies.lens,
            instance.perturbation,
            instance.galaxies.source,
        ])
        """
        Set up the grid, PSF and simulator settings used to simulate imaging of the strong lens. These should be tuned to
        match the S/N and noise properties of the observed data you are performing sensitivity mapping on.
        """
        grid = al.Grid2DIterate.uniform(
            shape_native=real_space_mask.shape_native,
            pixel_scales=real_space_mask.pixel_scales,
            fractional_accuracy=0.9999,
            sub_steps=[2, 4, 8, 16, 24],
        )

        simulator = al.SimulatorInterferometer(
            uv_wavelengths=uv_wavelengths,
            exposure_time=300.0,
            background_sky_level=0.1,
            noise_sigma=0.1,
            transformer_class=al.TransformerNUFFT,
        )

        simulated_interferometer = simulator.from_tracer_and_grid(
            tracer=tracer, grid=grid)
        """
        The data generated by the simulate function is that which is fitted, so we should apply the mask for the analysis 
        here before we return the simulated data.
        """
        return al.Interferometer(
            visibilities=simulated_interferometer.visibilities,
            noise_map=simulated_interferometer.noise_map,
            uv_wavelengths=uv_wavelengths,
            real_space_mask=real_space_mask,
        )

    """
    We next specify the search used to perform each model fit by the sensitivity mapper.
    """
    search = af.DynestyStatic(path_prefix=path_prefix, nlive=50)
    """
    We can now combine all of the objects created above and perform sensitivity mapping. The inputs to the `Sensitivity`
    object below are:

    - `simulation_instance`: This is an instance of the model used to simulate every dataset that is fitted. In this 
    example it is a lens model that does not include a subhalo, which was inferred by fitting the dataset we perform 
    sensitivity mapping on.

    - `base_model`: This is the lens model that is fitted to every simulated dataset, which does not include a subhalo. 
    In this example is composed of an `EllIsothermal` lens and `EllSersic` source.

    - `perturbation_model`: This is the extra model component that alongside the `base_model` is fitted to every 
    simulated dataset. In this example it is a `SphNFWMCRLudlow` dark matter subhalo.

    - `simulate_function`: This is the function that uses the `simulation_instance` and many instances of the 
    `perturbation_model` to simulate many datasets that are fitted with the `base_model` 
    and `base_model` + `perturbation_model`.

    - `analysis_class`: The wrapper `Analysis` class that passes each simulated dataset to the `Analysis` class that 
    fits the data.

    - `number_of_steps`: The number of steps over which the parameters in the `perturbation_model` are iterated. In 
    this example, `mass_at_200` has a `LogUniformPrior` with lower limit 1e6 and upper limit 1e11, therefore 
    the `number_of_steps` of 2 will simulate and fit just 2 datasets where the `mass_at_200` is between 1e6 and 1e11.

    - `number_of_cores`: The number of cores over which the sensitivity mapping is performed, enabling parallel 
    processing if set above 1.
    """
    sensitivity_mapper = s.Sensitivity(
        search=search,
        simulation_instance=simulation_instance,
        base_model=base_model,
        perturbation_model=perturbation_model,
        simulate_function=simulate_function,
        analysis_class=analysis_cls,
        number_of_steps=number_of_steps,
        number_of_cores=number_of_cores,
    )

    return sensitivity_mapper.run()