Exemplo n.º 1
0
class Phase(af.AbstractPhase):

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, profiles, non_linear_class=af.MultiNest):

        super().__init__(paths=paths, non_linear_class=non_linear_class)

        self.profiles = profiles

    def run(self, dataset: Dataset, mask):

        analysis = self.make_analysis(dataset=dataset, mask=mask)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, mask):

        masked_dataset = MaskedDataset(dataset=dataset, mask=mask)

        return Analysis(masked_dataset=masked_dataset,
                        image_path=self.optimizer.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output,
        )
Exemplo n.º 2
0
class Phase(af.AbstractPhase):

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, profiles, non_linear_class=af.MultiNest, transformer_class=al.TransformerFINUFFT):

        super().__init__(paths=paths, non_linear_class=non_linear_class)

        self.profiles = profiles

        self.transformer_class = transformer_class

    def run(self, dataset: Dataset, xy_mask):

        analysis = self.make_analysis(
            dataset=dataset,
            xy_mask=xy_mask
        )

        result = self.run_analysis(analysis=analysis)

        return self.make_result(
            result=result,
            analysis=analysis
        )

    def make_analysis(self, dataset, xy_mask):

        masked_dataset = MaskedDataset(
            dataset=dataset,
            xy_mask=xy_mask
        )

        transformers = []
        for i in range(masked_dataset.uv_wavelengths.shape[0]):
            transformers.append(
                self.transformer_class(
                    uv_wavelengths=masked_dataset.uv_wavelengths[i],
                    grid=masked_dataset.grid_3d.grid_2d.in_radians
                )
            )

        return Analysis(
            masked_dataset=masked_dataset,
            transformers=transformers,
            image_path=self.optimizer.paths.image_path
        )

    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output,
        )
Exemplo n.º 3
0
class Phase(af.AbstractPhase):

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, profiles, non_linear_class=af.MultiNest):

        super().__init__(paths=paths, non_linear_class=non_linear_class)
        self.profiles = profiles

    def run(self, dataset: Spectrum):
        """
        Pass a dataset to the phase, running the phase and non-linear search.

        Parameters
        ----------
        dataset: aa.Imaging
            The dataset fitted by the phase, which in this case is a PyAutoArray imaging object.

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising the best fit model.
        """

        analysis = self.make_analysis(dataset=dataset)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset):
        """
        Create an Analysis object, which creates the dataset and contains the functions which perform the fit.

        Parameters
        ----------
        dataset: aa.Imaging
            The dataset fitted by the phase, which in this case is a PyAutoArray imaging object.

        Returns
        -------
        analysis : Analysis
            An analysis object that the non-linear search calls to determine the fit likelihood for a given model
            instance.
        """
        return Analysis(dataset=dataset, dimensions=self.model.prior_count, visualize_path=self.optimizer.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output
        )
Exemplo n.º 4
0
class Phase(af.AbstractPhase):

    galaxies = af.PhaseProperty("galaxies")

    Result = Result

    @af.convert_paths
    def __init__(self,
                 paths,
                 *,
                 galaxies,
                 self_calibration=False,
                 non_linear_class=af.MultiNest,
                 transformer_class=al.TransformerFINUFFT):

        super().__init__(paths=paths, non_linear_class=non_linear_class)

        self.galaxies = galaxies

        self.transformer_class = transformer_class

        self.self_calibration = self_calibration

    @property
    def phase_folders(self):
        return self.optimizer.phase_folders

    def run(self, dataset: Dataset, xy_mask):

        analysis = self.make_analysis(dataset=dataset, xy_mask=xy_mask)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, xy_mask):

        masked_dataset = MaskedDataset(dataset=dataset, xy_mask=xy_mask)

        transformer = self.transformer_class(
            uv_wavelengths=masked_dataset.uv_wavelengths,
            grid=masked_dataset.grid.in_radians)

        return Analysis(masked_dataset=masked_dataset,
                        transformer=transformer,
                        self_calibration=self.self_calibration,
                        image_path=self.optimizer.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output,
        )
Exemplo n.º 5
0
class PhaseGalaxy(abstract.AbstractPhase):
    galaxies = af.PhaseProperty("galaxies")

    Analysis = Analysis

    def __init__(
        self,
        phase_name,
        search,
        folders=tuple(),
        galaxies=None,
        use_image=False,
        use_convergence=False,
        use_potential=False,
        use_deflections=False,
        sub_size=2,
        pixel_scales_interp=None,
        cosmology=cosmo.Planck15,
    ):
        """
        A phase in an lens pipeline. Uses the set non_linear search to try to fit
        models and hyper_galaxies passed to it.

        Parameters
        ----------
        search: class
            The class of a non_linear search
        sub_size: int
            The side length of the subgrid
        """

        super(PhaseGalaxy, self).__init__(phase_name=phase_name,
                                          folders=folders,
                                          search=search)
        self.cosmology = cosmology
        self.use_image = use_image
        self.use_convergence = use_convergence
        self.use_potential = use_potential
        self.use_deflections = use_deflections
        self.galaxies = galaxies
        self.sub_size = sub_size
        self.pixel_scales_interp = pixel_scales_interp

    def run(self, galaxy_data, mask, info=None, results=None):
        """
        Run this phase.

        Parameters
        ----------
        galaxy_data
        mask: Mask
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            An object describing the results of the last phase or None if no phase has
            been executed

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising the best fit model and other hyper_galaxies.
        """
        analysis = self.make_analysis(galaxy_data=galaxy_data,
                                      results=results,
                                      mask=mask)

        self.save_metadata(galaxy_data.name)
        self.model = self.model.populate(results)

        result = self.run_analysis(analysis=analysis,
                                   info=info,
                                   pickle_files=pickle_files)

        return self.make_result(result, analysis)

    def make_analysis(self, galaxy_data, mask, results=None):
        """
        Create an lens object. Also calls the prior passing and masked_imaging modifying
        functions to allow child classes to change the behaviour of the phase.

        Parameters
        ----------
        galaxy_data
        mask: Mask
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens: Analysis
            An lens object that the non-linear search calls to determine the fit of a
             set of values
        """

        if self.use_image or self.use_convergence or self.use_potential:

            galaxy_data = masked_galaxy_data.MaskedGalaxyDataset(
                galaxy_data=galaxy_data[0],
                mask=mask,
                pixel_scales_interp=self.pixel_scales_interp,
                use_image=self.use_image,
                use_convergence=self.use_convergence,
                use_potential=self.use_potential,
                use_deflections_y=self.use_deflections,
                use_deflections_x=self.use_deflections,
            )

            return AnalysisSingle(
                galaxy_data=galaxy_data,
                cosmology=self.cosmology,
                image_path=self.search.paths.image_path,
                results=results,
            )

        elif self.use_deflections:

            galaxy_data_y = masked_galaxy_data.MaskedGalaxyDataset(
                galaxy_data=galaxy_data[0],
                mask=mask,
                pixel_scales_interp=self.pixel_scales_interp,
                use_image=self.use_image,
                use_convergence=self.use_convergence,
                use_potential=self.use_potential,
                use_deflections_y=self.use_deflections,
                use_deflections_x=False,
            )

            galaxy_data_x = masked_galaxy_data.MaskedGalaxyDataset(
                galaxy_data=galaxy_data[1],
                mask=mask,
                pixel_scales_interp=self.pixel_scales_interp,
                use_image=self.use_image,
                use_convergence=self.use_convergence,
                use_potential=self.use_potential,
                use_deflections_y=False,
                use_deflections_x=self.use_deflections,
            )

            return AnalysisDeflections(
                galaxy_data_y=galaxy_data_y,
                galaxy_data_x=galaxy_data_x,
                cosmology=self.cosmology,
                image_path=self.search.paths.image_path,
                results=results,
            )
Exemplo n.º 6
0
class PhaseImaging(PhaseData):
    galaxies = af.PhaseProperty("galaxies")
    hyper_image_sky = af.PhaseProperty("hyper_image_sky")
    hyper_background_noise = af.PhaseProperty("hyper_background_noise")

    Analysis = Analysis
    Result = Result

    def __init__(
        self,
        phase_name,
        phase_folders=tuple(),
        galaxies=None,
        hyper_image_sky=None,
        hyper_background_noise=None,
        optimizer_class=af.MultiNest,
        cosmology=cosmo.Planck15,
        sub_size=2,
        signal_to_noise_limit=None,
        bin_up_factor=None,
        psf_shape=None,
        positions_threshold=None,
        mask_function=None,
        inner_mask_radii=None,
        pixel_scale_interpolation_grid=None,
        pixel_scale_binned_cluster_grid=None,
        inversion_uses_border=True,
        inversion_pixel_limit=None,
        auto_link_priors=False,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear optimizer to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        optimizer_class: class
            The class of a non_linear optimizer
        sub_size: int
            The side length of the subgrid
        pixel_scale_binned_cluster_grid : float or None
            If *True*, the hyper_galaxies image used to generate the cluster'grids weight map will be binned \
            up to this higher pixel scale to speed up the KMeans clustering algorithm.
        """

        phase_tag = phase_tagging.phase_tag_from_phase_settings(
            sub_size=sub_size,
            signal_to_noise_limit=signal_to_noise_limit,
            bin_up_factor=bin_up_factor,
            psf_shape=psf_shape,
            positions_threshold=positions_threshold,
            inner_mask_radii=inner_mask_radii,
            pixel_scale_interpolation_grid=pixel_scale_interpolation_grid,
            pixel_scale_binned_cluster_grid=pixel_scale_binned_cluster_grid,
        )

        super().__init__(
            phase_name=phase_name,
            phase_tag=phase_tag,
            phase_folders=phase_folders,
            galaxies=galaxies,
            optimizer_class=optimizer_class,
            cosmology=cosmology,
            auto_link_priors=auto_link_priors,
        )

        self.hyper_image_sky = hyper_image_sky
        self.hyper_background_noise = hyper_background_noise

        self.hyper_noise_map_max = af.conf.instance.general.get(
            "hyper", "hyper_noise_map_max", float)

        self.is_hyper_phase = False

        self.meta_data_fit = MetaImagingFit(
            variable=self.variable,
            bin_up_factor=bin_up_factor,
            psf_shape=psf_shape,
            sub_size=sub_size,
            signal_to_noise_limit=signal_to_noise_limit,
            positions_threshold=positions_threshold,
            mask_function=mask_function,
            inner_mask_radii=inner_mask_radii,
            pixel_scale_interpolation_grid=pixel_scale_interpolation_grid,
            pixel_scale_binned_cluster_grid=pixel_scale_binned_cluster_grid,
            inversion_uses_border=inversion_uses_border,
            inversion_pixel_limit=inversion_pixel_limit,
        )

    # noinspection PyMethodMayBeStatic,PyUnusedLocal
    def modify_image(self, image, results):
        """
        Customize an lens_data. e.g. removing lens light.

        Parameters
        ----------
        image: scaled_array.ScaledSquarePixelArray
            An lens_data that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result of the previous lens

        Returns
        -------
        lens_data: scaled_array.ScaledSquarePixelArray
            The modified image (not changed by default)
        """
        return image

    def make_analysis(self, data, results=None, mask=None, positions=None):
        """
        Create an lens object. Also calls the prior passing and lens_data modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        data: im.Imaging
            An lens_data that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the non-linear optimizer calls to determine the fit of a set of values
        """
        self.meta_data_fit.variable = self.variable
        modified_image = self.modify_image(
            image=data.image,
            results=results,
        )

        lens_imaging_data = self.meta_data_fit.data_fit_from(
            data=data,
            mask=mask,
            positions=positions,
            results=results,
            modified_image=modified_image)

        self.output_phase_info()

        analysis = self.Analysis(
            lens_imaging_data=lens_imaging_data,
            cosmology=self.cosmology,
            image_path=self.optimizer.image_path,
            results=results,
        )

        return analysis

    def output_phase_info(self):

        file_phase_info = "{}/{}".format(self.optimizer.phase_output_path,
                                         "phase.info")

        with open(file_phase_info, "w") as phase_info:
            phase_info.write("Optimizer = {} \n".format(
                type(self.optimizer).__name__))
            phase_info.write("Sub-grid size = {} \n".format(
                self.meta_data_fit.sub_size))
            phase_info.write("PSF shape = {} \n".format(
                self.meta_data_fit.psf_shape))
            phase_info.write("Positions Threshold = {} \n".format(
                self.meta_data_fit.positions_threshold))
            phase_info.write("Cosmology = {} \n".format(self.cosmology))
            phase_info.write("Auto Link Priors = {} \n".format(
                self.auto_link_priors))

            phase_info.close()

    def extend_with_multiple_hyper_phases(
        self,
        hyper_galaxy=False,
        inversion=False,
        include_background_sky=False,
        include_background_noise=False,
    ):
        hyper_phase_classes = []

        if hyper_galaxy:
            if not include_background_sky and not include_background_noise:
                hyper_phase_classes.append(
                    phase_extensions.hyper_galaxy_phase.HyperGalaxyPhase)
            elif include_background_sky and not include_background_noise:
                hyper_phase_classes.append(phase_extensions.hyper_galaxy_phase.
                                           HyperGalaxyBackgroundSkyPhase)
            elif not include_background_sky and include_background_noise:
                hyper_phase_classes.append(phase_extensions.hyper_galaxy_phase.
                                           HyperGalaxyBackgroundNoisePhase)
            else:
                hyper_phase_classes.append(phase_extensions.hyper_galaxy_phase.
                                           HyperGalaxyBackgroundBothPhase)

        if inversion:
            if not include_background_sky and not include_background_noise:
                hyper_phase_classes.append(phase_extensions.InversionPhase)
            elif include_background_sky and not include_background_noise:
                hyper_phase_classes.append(
                    phase_extensions.InversionBackgroundSkyPhase)
            elif not include_background_sky and include_background_noise:
                hyper_phase_classes.append(
                    phase_extensions.InversionBackgroundNoisePhase)
            else:
                hyper_phase_classes.append(
                    phase_extensions.InversionBackgroundBothPhase)

        if len(hyper_phase_classes) == 0:
            return self
        else:
            return phase_extensions.CombinedHyperPhase(
                phase=self, hyper_phase_classes=hyper_phase_classes)
Exemplo n.º 7
0
class PhaseDataset(abstract.AbstractPhase):
    gaussians = af.PhaseProperty("gaussians")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, gaussians=None, optimizer_class=af.MultiNest):
        """

        A phase in an lens pipeline. Uses the set non_linear optimizer to try to fit models and hyper_gaussians
        passed to it.

        Parameters
        ----------
        optimizer_class: class
            The class of a non_linear optimizer
        """

        super(PhaseDataset, self).__init__(paths,
                                           optimizer_class=optimizer_class)
        self.gaussians = gaussians or []

    def run(self, dataset: Dataset, mask, results=None):
        """
        Run this phase.

        Parameters
        ----------
        mask: Mask
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            An object describing the results of the last phase or None if no phase has been executed
        dataset: scaled_array.ScaledSquarePixelArray
            An masked_imaging that has been masked

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising the best fit model and other hyper_gaussians.
        """
        dataset.save(self.paths.phase_output_path)
        self.model = self.model.populate(results)

        analysis = self.make_analysis(dataset=dataset,
                                      mask=mask,
                                      results=results)

        self.customize_priors(results)
        self.assert_and_save_pickle()

        result = self.run_analysis(analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, results=None, mask=None):
        """
        Create an lens object. Also calls the prior passing and masked_imaging modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        mask: Mask
            The default masks passed in by the pipeline
        dataset: im.Imaging
            An masked_imaging that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the non-linear optimizer calls to determine the fit of a set of values
        """
        raise NotImplementedError()
Exemplo n.º 8
0
class Phase(af.AbstractPhase):

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self,
                 paths,
                 *,
                 profiles,
                 lens_redshift,
                 source_redshift,
                 regions=[],
                 non_linear_class=af.MultiNest,
                 transformer_class=al.TransformerFINUFFT):

        super().__init__(paths=paths, non_linear_class=non_linear_class)

        self.profiles = profiles

        if lens_redshift < source_redshift:
            self.lens_redshift = lens_redshift
            self.source_redshift = source_redshift
        else:
            raise ValueError(
                "The len's z={} must be lower than the source's z={}",
                format(lens_redshift, source_redshift))

        self.transformer_class = transformer_class

        if not isinstance(regions, list):
            raise ValueError("""The variable "regions" must be a list.""")
        else:
            self.regions = regions

    @property
    def phase_folders(self):
        return self.optimizer.phase_folders

    def run(self, dataset: Dataset, xy_mask):

        analysis = self.make_analysis(dataset=dataset, xy_mask=xy_mask)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, xy_mask):

        masked_dataset = MaskedDataset(dataset=dataset, xy_mask=xy_mask)

        transformers = []
        for i in range(masked_dataset.uv_wavelengths.shape[0]):
            transformers.append(
                self.transformer_class(
                    uv_wavelengths=masked_dataset.uv_wavelengths[i],
                    grid=masked_dataset.grid_3d.grid_2d.in_radians))
        """
        def get_continuum(masked_dataset, regions):
            pass

        continuum = np.zeros(
            shape=(dataset.visibilities.shape[0], ),
            dtype=bool
        )
        for region in self.regions:
            continuum += region


        def func(masked_dataset, continuum):

            argspec = inspect.getargspec(MaskedDatasetLite.__init__)
            args = {}
            for argname in argspec.args:
                if argname not in ["self"]:
                    if hasattr(masked_dataset, argname):
                        array = getattr(masked_dataset, argname)
                        args[argname] = reshape_array(
                            array=array[~continuum]
                        )

            return MaskedDatasetLite(**args)

        masked_dataset_continuum = func(masked_dataset, continuum)
        #print(masked_dataset_continuum.uv_wavelengths.shape)

        transformer_continuum = self.transformer_class(
            uv_wavelengths=masked_dataset_continuum.uv_wavelengths,
            grid=masked_dataset.grid_3d.grid_2d.in_radians
        )

        # dirty_image = transformer_continuum.image_from_visibilities(
        #     visibilities=masked_dataset_continuum.visibilities
        # )
        # plt.figure()
        # plt.imshow(dirty_image[::-1], cmap="jet")
        # plt.xticks([])
        # plt.yticks([])
        # plt.show()
        """

        return Analysis(masked_dataset=masked_dataset,
                        transformers=transformers,
                        transformer_continuum=None,
                        lens_redshift=self.lens_redshift,
                        source_redshift=self.source_redshift,
                        image_path=self.optimizer.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output,
        )
Exemplo n.º 9
0
class Phase(af.AbstractPhase):

    gaussian = af.PhaseProperty("gaussian")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, gaussian, search):
        """
        A phase which fits a Gaussian model using a non-linear search.

        Parameters
        ----------
        paths : af.Paths
            Handles the output directory structure.
        gaussian : gaussians.Gaussian
            The model component Gaussian class fitted by this phase.
        search: class
            The class of a non_linear search
        """

        super().__init__(paths=paths, search=search)

        self.gaussian = gaussian

    """
    The run method is slightly different, as it now passed a mask in addition to the dataset. These are used to set up
    the masked-dataset in the 'analysis.py' module.
    """

    def run(self, dataset: Dataset, mask):
        """
        Pass a dataset to the phase, running the phase and non-linear search.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.
        mask: Mask
            The mask used for the analysis.

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising information on the non-linear search and the maximum likelihood model.
        """

        analysis = self.make_analysis(dataset=dataset, mask=mask)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, mask):
        """
        Create an Analysis object, which creates the dataset and contains the functions which perform the fit.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.

        Returns
        -------
        analysis : Analysis
            An analysis object that the non-linear search calls to determine the fit log_likelihood for a given model
            instance.
        """
        """To mask the dataset we simply pass both to the MaskedDataset class."""

        masked_dataset = MaskedDataset(dataset=dataset, mask=mask)
        """
        The 'image_path' is where visualizatiion of the model fit is output. Below, we direct it to the same path as
        the non-linear search output, but with an additional folder 'image' at the end. This path should be used
        for pretty much any project.
        """

        return Analysis(masked_dataset=masked_dataset,
                        image_path=self.search.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            samples=result.samples,
            previous_model=self.model,
            search=self.search,
            analysis=analysis,
        )
Exemplo n.º 10
0
class PhaseImaging(dataset.PhaseDataset):
    galaxies = af.PhaseProperty("galaxies")
    hyper_image_sky = af.PhaseProperty("hyper_image_sky")
    hyper_background_noise = af.PhaseProperty("hyper_background_noise")

    Analysis = Analysis
    Result = Result

    @af.convert_paths
    def __init__(
        self,
        paths,
        *,
        galaxies=None,
        hyper_image_sky=None,
        hyper_background_noise=None,
        optimizer_class=af.MultiNest,
        cosmology=cosmo.Planck15,
        sub_size=2,
        signal_to_noise_limit=None,
        bin_up_factor=None,
        psf_shape_2d=None,
        positions_threshold=None,
        pixel_scale_interpolation_grid=None,
        inversion_uses_border=True,
        inversion_pixel_limit=None,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear optimizer to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        optimizer_class: class
            The class of a non_linear optimizer
        sub_size: int
            The side length of the subgrid
        """

        phase_tag = tagging.phase_tag_from_phase_settings(
            sub_size=sub_size,
            signal_to_noise_limit=signal_to_noise_limit,
            bin_up_factor=bin_up_factor,
            psf_shape_2d=psf_shape_2d,
            positions_threshold=positions_threshold,
            pixel_scale_interpolation_grid=pixel_scale_interpolation_grid,
        )
        paths.phase_tag = phase_tag

        super().__init__(
            paths,
            galaxies=galaxies,
            optimizer_class=optimizer_class,
            cosmology=cosmology,
        )

        self.hyper_image_sky = hyper_image_sky
        self.hyper_background_noise = hyper_background_noise

        self.is_hyper_phase = False

        self.meta_dataset = MetaImaging(
            model=self.model,
            bin_up_factor=bin_up_factor,
            psf_shape_2d=psf_shape_2d,
            sub_size=sub_size,
            signal_to_noise_limit=signal_to_noise_limit,
            positions_threshold=positions_threshold,
            pixel_scale_interpolation_grid=pixel_scale_interpolation_grid,
            inversion_uses_border=inversion_uses_border,
            inversion_pixel_limit=inversion_pixel_limit,
        )

    # noinspection PyMethodMayBeStatic,PyUnusedLocal
    def modify_image(self, image, results):
        """
        Customize an masked_imaging. e.g. removing lens light.

        Parameters
        ----------
        image: scaled_array.ScaledSquarePixelArray
            An masked_imaging that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result of the previous lens

        Returns
        -------
        masked_imaging: scaled_array.ScaledSquarePixelArray
            The modified image (not changed by default)
        """
        return image

    def make_analysis(self, dataset, mask, results=None, positions=None):
        """
        Create an lens object. Also calls the prior passing and masked_imaging modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        dataset: im.Imaging
            An masked_imaging that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the non-linear optimizer calls to determine the fit of a set of values
        """
        self.meta_dataset.model = self.model
        modified_image = self.modify_image(image=dataset.image,
                                           results=results)

        masked_imaging = self.meta_dataset.masked_dataset_from(
            dataset=dataset,
            mask=mask,
            positions=positions,
            results=results,
            modified_image=modified_image,
        )

        self.output_phase_info()

        analysis = self.Analysis(
            masked_imaging=masked_imaging,
            cosmology=self.cosmology,
            image_path=self.optimizer.paths.image_path,
            results=results,
        )

        return analysis

    def output_phase_info(self):

        file_phase_info = "{}/{}".format(
            self.optimizer.paths.phase_output_path, "phase.info")

        with open(file_phase_info, "w") as phase_info:
            phase_info.write("Optimizer = {} \n".format(
                type(self.optimizer).__name__))
            phase_info.write("Sub-grid size = {} \n".format(
                self.meta_dataset.sub_size))
            phase_info.write("PSF shape = {} \n".format(
                self.meta_dataset.psf_shape_2d))
            phase_info.write("Positions Threshold = {} \n".format(
                self.meta_dataset.positions_threshold))
            phase_info.write("Cosmology = {} \n".format(self.cosmology))

            phase_info.close()
Exemplo n.º 11
0
class Phase(af.AbstractPhase):

    """
    This tells the phase that the input parameter 'gaussian' is a model component that is fitted for by the phase's
    non-linear search.

    In 'analysis.py', the function 'fit' has an input parameter called 'instance' which is the gaussian mapped from
    this model via a unit vector and the model priors (as described in tutorial 1).

    For your model-fitting problem, this will be replaced by the modules in your 'model' package.
    """

    gaussian = af.PhaseProperty("gaussian")

    Result = Result  # Set the result to the Result class in 'result.py'

    @af.convert_paths  # <- This handles setting up output paths.
    def __init__(
        self,
        paths,
        gaussian,  # <- The user inputs a model -> gaussian.py -> Gaussian class here.
        search,  # <- This specifies the default non-linear search used by the phase.
    ):
        """
        A phase which fits a Gaussian model using a non-linear search.

        Parameters
        ----------
        paths : af.Paths
            Handles the output directory structure.
        gaussian : model.gaussians.Gaussian
            The model component Gaussian class fitted by this phase.
        search: class
            The class of a non_linear search
        """
        super().__init__(paths=paths, search=search)
        self.gaussian = gaussian

    def run(self, dataset: Dataset):
        """ Pass a dataset to the phase, running the phase and non-linear search.

        Parameters
        ----------
        dataset : dataset.Dataset
            The dataset fitted by the phase, which is specified in the module 'dataset/dataset.py'

        Returns
        -------
        result: result.Result
            A result object comprising information on the non-linear search and the maximum likelihood model.
        """

        """
        These functions create instances of the Analysis class (in 'analysis.py'), runs the analysis (which performs
        the non-linear search ) and returns an instance of the Result class (in 'result.py').

        Once you've looked through this module, check those modules out to see exactly what these classes do!
        """

        analysis = self.make_analysis(dataset=dataset)

        """
        'run_analysis' is not located in analysis.py, instead it is an inherited method from the parent class
        'af.AbstractPhase'. Essentially, all this function does is begin the non-linear search, using the analysis
        created above.
        """

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset):
        """
        Create an Analysis object, which uses the dataset with functions to perform a fit.

        Parameters
        ----------
        dataset : dataset.Dataset
            The dataset fitted by the phase, which is specified in the module 'dataset/dataset.py'

        Returns
        -------
        analysis : Analysis
            An analysis object that the non-linear search calls to determine the fit log_likelihood for a given model
            instance.
        """
        return Analysis(dataset=dataset)

    def make_result(self, result, analysis):
        return self.Result(
            samples=result.samples,
            previous_model=self.model,
            search=self.search,
            analysis=analysis,
        )
Exemplo n.º 12
0
class Phase(af.AbstractPhase):
    """
    Because we now have multiple profiles in our model, we have renamed 'gaussian' to 'profiles'. As before,
    PyAutoFit uses this information to map the input Profile classes to a model instance when performing a fit.
    """

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, profiles, settings, search):
        """
        A phase which fits a model composed of multiple profiles (Gaussian, Exponential) using a non-linear search.

        Parameters
        ----------
        paths : af.Paths
            Handles the output directory structure.
        profiles : [profiles.Profile]
            The model components (e.g. Gaussian, Exponenial) fitted by this phase.
        search: class
            The class of a non_linear search
        settings : PhaseSettings
            The collection of settings of the phase used to augment the data that is fitted and tag the output path.
        """
        """
        Here, we create a 'tag' for our phase. If we use an optional phase setting to alter the dataset we fit (here,
        a data_trim_ variable), we want to 'tag' the phase such that results are output to a unique
        directory whose names makes it explicit how the dataset was changed.

        If this setting is off, the tag is an empty string and thus the directory structure is not changed.
        """

        paths.tag = settings.tag  # The phase_tag must be manually added to the phase.

        super().__init__(paths=paths, search=search)

        self.profiles = profiles
        """
        Phase settings alter the dataset that is fitted, however a phase does not have access to the dataset until it
        is run (e.g. the run method below is passed the dataset). In order for a phase to use its input phase
        settings to create the dataset it fits, these settings are stored in the 'meta_dataset' attribute and used
        when the 'run' and 'make_analysis' methods are called.
        """

        self.meta_dataset = MetaDataset(settings=settings)

    def run(self, dataset: Dataset, mask):
        """
        Pass a dataset to the phase, running the phase and non-linear search.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.
        mask: Mask
            The mask used for the analysis.

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising information on the non-linear search and the maximum likelihood model.
        """

        analysis = self.make_analysis(dataset=dataset, mask=mask)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, mask):
        """
        Create an Analysis object, which creates the dataset and contains the functions which perform the fit.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.

        Returns
        -------
        analysis : Analysis
            An analysis object that the non-linear search calls to determine the fit log_likelihood for a given model
            instance.
        """
        """
        Here, the meta_dataset is used to create the masked dataset that is fitted. If the data_trim_left and / or
        data_trim_right settings are passed into the phase, the function below uses them to alter the masked dataset.

        Checkout 'meta_dataset.py' for more details.
        """

        masked_dataset = self.meta_dataset.masked_dataset_from_dataset_and_mask(
            dataset=dataset, mask=mask)

        return Analysis(masked_dataset=masked_dataset,
                        image_path=self.search.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            samples=result.samples,
            previous_model=self.model,
            search=self.search,
            analysis=analysis,
        )
Exemplo n.º 13
0
class PhasePointSource(abstract.AbstractPhase):

    galaxies = af.PhaseProperty("galaxies")

    Analysis = Analysis
    Result = Result

    def __init__(
            self,
            *,
            search,
            positions_solver,
            galaxies=None,
            settings=SettingsPhasePositions(),
            cosmology=cosmo.Planck15,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear search to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        search: class
            The class of a non_linear search
        sub_size: int
            The side length of the subgrid
        """

        super().__init__(search=search,
                         settings=settings,
                         galaxies=galaxies,
                         cosmology=cosmology)

        self.positions_solver = positions_solver

    def make_analysis(
        self,
        positions,
        positions_noise_map,
        fluxes=None,
        fluxes_noise_map=None,
        imaging=None,
        results=None,
    ):
        """
        Returns an lens object. Also calls the prior passing and masked_imaging modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask2D
            The default masks passed in by the pipeline
        dataset: im.Imaging
            An masked_imaging that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the `NonLinearSearch` calls to determine the fit of a set of values
        """

        self.output_phase_info()

        return self.Analysis(
            positions=positions,
            noise_map=positions_noise_map,
            fluxes=fluxes,
            fluxes_noise_map=fluxes_noise_map,
            solver=self.positions_solver,
            imaging=imaging,
            settings=self.settings,
            cosmology=self.cosmology,
            results=results,
        )

    def run(
        self,
        positions,
        positions_noise_map,
        fluxes=None,
        fluxes_noise_map=None,
        imaging=None,
        results=None,
        info=None,
        pickle_files=None,
    ):
        """
        Run this phase.

        Parameters
        ----------
        mask: Mask2D
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            An object describing the results of the last phase or None if no phase has been executed
        dataset: scaled_array.ScaledSquarePixelArray
            An masked_imaging that has been masked

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising the best fit model and other hyper_galaxies.
        """

        self.model = self.model.populate(results)

        results = results or af.ResultsCollection()

        analysis = self.make_analysis(
            positions=positions,
            positions_noise_map=positions_noise_map,
            fluxes=fluxes,
            fluxes_noise_map=fluxes_noise_map,
            imaging=imaging,
            results=results,
        )

        result = self.run_analysis(analysis=analysis,
                                   info=info,
                                   pickle_files=pickle_files)

        return self.make_result(result=result, analysis=analysis)

    def output_phase_info(self):

        file_phase_info = path.join(self.search.paths.output_path,
                                    "phase.info")

        with open(file_phase_info, "w") as phase_info:
            phase_info.write("Optimizer = {} \n".format(
                type(self.search).__name__))
            phase_info.write("Cosmology = {} \n".format(self.cosmology))
            phase_info.close()
Exemplo n.º 14
0
class PhaseInterferometer(dataset.PhaseDataset):
    galaxies = af.PhaseProperty("galaxies")
    hyper_background_noise = af.PhaseProperty("hyper_background_noise")

    Analysis = Analysis
    Result = Result

    @af.convert_paths
    def __init__(
        self,
        paths,
        *,
        real_space_mask,
        galaxies=None,
        hyper_background_noise=None,
        optimizer_class=af.MultiNest,
        cosmology=cosmo.Planck15,
        sub_size=2,
        primary_beam_shape_2d=None,
        positions_threshold=None,
        pixel_scale_interpolation_grid=None,
        inversion_uses_border=True,
        inversion_pixel_limit=None,
    ):

        """

        A phase in an lens pipeline. Uses the set non_linear optimizer to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        optimizer_class: class
            The class of a non_linear optimizer
        sub_size: int
            The side length of the subgrid
        """

        paths.phase_tag = phase_tagging.phase_tag_from_phase_settings(
            sub_size=sub_size,
            real_space_shape_2d=real_space_mask.shape_2d,
            real_space_pixel_scales=real_space_mask.pixel_scales,
            primary_beam_shape_2d=primary_beam_shape_2d,
            positions_threshold=positions_threshold,
            pixel_scale_interpolation_grid=pixel_scale_interpolation_grid,
        )

        super().__init__(
            paths,
            galaxies=galaxies,
            optimizer_class=optimizer_class,
            cosmology=cosmology,
        )

        self.hyper_background_noise = hyper_background_noise

        self.is_hyper_phase = False

        self.meta_interferometer_fit = MetaInterferometerFit(
            model=self.model,
            sub_size=sub_size,
            real_space_mask=real_space_mask,
            primary_beam_shape_2d=primary_beam_shape_2d,
            positions_threshold=positions_threshold,
            pixel_scale_interpolation_grid=pixel_scale_interpolation_grid,
            inversion_uses_border=inversion_uses_border,
            inversion_pixel_limit=inversion_pixel_limit,
        )

    # noinspection PyMethodMayBeStatic,PyUnusedLocal
    def modify_visibilities(self, visibilities, results):
        """
        Customize an masked_interferometer. e.g. removing lens light.

        Parameters
        ----------
        image: scaled_array.ScaledSquarePixelArray
            An masked_interferometer that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result of the previous lens

        Returns
        -------
        masked_interferometer: scaled_array.ScaledSquarePixelArray
            The modified image (not changed by default)
        """
        return visibilities

    def make_analysis(self, dataset, mask, results=None, positions=None):
        """
        Create an lens object. Also calls the prior passing and masked_interferometer modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        dataset: im.Interferometer
            An masked_interferometer that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the non-linear optimizer calls to determine the fit of a set of values
        """
        self.meta_interferometer_fit.model = self.model
        modified_visibilities = self.modify_visibilities(
            visibilities=dataset.visibilities, results=results
        )

        masked_interferometer = self.meta_interferometer_fit.masked_dataset_from(
            dataset=dataset,
            mask=mask,
            positions=positions,
            results=results,
            modified_visibilities=modified_visibilities,
        )

        self.output_phase_info()

        analysis = self.Analysis(
            masked_interferometer=masked_interferometer,
            cosmology=self.cosmology,
            image_path=self.optimizer.paths.image_path,
            results=results,
        )

        return analysis

    def output_phase_info(self):

        file_phase_info = "{}/{}".format(
            self.optimizer.paths.phase_output_path, "phase.info"
        )

        with open(file_phase_info, "w") as phase_info:
            phase_info.write("Optimizer = {} \n".format(type(self.optimizer).__name__))
            phase_info.write(
                "Sub-grid size = {} \n".format(self.meta_interferometer_fit.sub_size)
            )
            phase_info.write(
                "Primary Beam shape = {} \n".format(
                    self.meta_interferometer_fit.primary_beam_shape_2d
                )
            )
            phase_info.write(
                "Positions Threshold = {} \n".format(
                    self.meta_interferometer_fit.positions_threshold
                )
            )
            phase_info.write("Cosmology = {} \n".format(self.cosmology))

            phase_info.close()

    def extend_with_multiple_hyper_phases(
        self,
        hyper_galaxy=False,
        inversion=False,
        include_background_sky=False,
        include_background_noise=False,
    ):
        hyper_phase_classes = []

        if hyper_galaxy:
            if not include_background_sky and not include_background_noise:
                hyper_phase_classes.append(
                    extensions.hyper_galaxy_phase.HyperGalaxyPhase
                )
            elif include_background_sky and not include_background_noise:
                hyper_phase_classes.append(
                    extensions.hyper_galaxy_phase.HyperGalaxyBackgroundSkyPhase
                )
            elif not include_background_sky and include_background_noise:
                hyper_phase_classes.append(
                    extensions.hyper_galaxy_phase.HyperGalaxyBackgroundNoisePhase
                )
            else:
                hyper_phase_classes.append(
                    extensions.hyper_galaxy_phase.HyperGalaxyBackgroundBothPhase
                )

        if inversion:
            if not include_background_sky and not include_background_noise:
                hyper_phase_classes.append(extensions.InversionPhase)
            elif include_background_sky and not include_background_noise:
                hyper_phase_classes.append(extensions.InversionBackgroundSkyPhase)
            elif not include_background_sky and include_background_noise:
                hyper_phase_classes.append(extensions.InversionBackgroundNoisePhase)
            else:
                hyper_phase_classes.append(extensions.InversionBackgroundBothPhase)

        if len(hyper_phase_classes) == 0:
            return self
        else:
            return extensions.CombinedHyperPhase(
                phase=self, hyper_phase_classes=hyper_phase_classes
            )
Exemplo n.º 15
0
class PhaseImaging(dataset.PhaseDataset):

    galaxies = af.PhaseProperty("galaxies")
    hyper_image_sky = af.PhaseProperty("hyper_image_sky")
    hyper_background_noise = af.PhaseProperty("hyper_background_noise")

    Analysis = Analysis
    Result = Result

    @af.convert_paths
    def __init__(
            self,
            paths,
            *,
            search,
            galaxies=None,
            hyper_image_sky=None,
            hyper_background_noise=None,
            settings=SettingsPhaseImaging(),
            cosmology=cosmo.Planck15,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear search to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        search: class
            The class of a non_linear search
        sub_size: int
            The side length of the subgrid
        """

        super().__init__(
            paths,
            search=search,
            settings=settings,
            galaxies=galaxies,
            cosmology=cosmology,
        )

        self.hyper_image_sky = hyper_image_sky
        self.hyper_background_noise = hyper_background_noise

        self.is_hyper_phase = False

    def make_phase_attributes(self, analysis):
        return PhaseAttributes(
            cosmology=self.cosmology,
            positions=analysis.masked_dataset.positions,
            hyper_model_image=analysis.hyper_model_image,
            hyper_galaxy_image_path_dict=analysis.hyper_galaxy_image_path_dict,
        )

    def make_analysis(self, dataset, mask, results=None):
        """
        Create an lens object. Also calls the prior passing and masked_imaging modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        dataset: im.Imaging
            An masked_imaging that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the non-linear search calls to determine the fit of a set of values
        """

        masked_imaging = imaging.MaskedImaging(
            imaging=dataset,
            mask=mask,
            settings=self.settings.settings_masked_imaging)

        self.output_phase_info()

        analysis = self.Analysis(
            masked_imaging=masked_imaging,
            settings=self.settings,
            cosmology=self.cosmology,
            image_path=self.search.paths.image_path,
            results=results,
            log_likelihood_cap=self.settings.log_likelihood_cap,
        )

        return analysis

    def extend_with_stochastic_phase(
        self,
        stochastic_search=None,
        include_lens_light=False,
        include_pixelization=False,
        include_regularization=False,
        histogram_samples=500,
        histogram_bins=10,
        stochastic_method="gaussian",
        stochastic_sigma=0.0,
    ):

        if stochastic_search is None:
            stochastic_search = self.search.copy_with_name_extension(
                extension="")

        model_classes = [ag.mp.MassProfile]

        if include_lens_light:
            model_classes.append(ag.lp.LightProfile)

        if include_pixelization:
            model_classes.append(ag.pix.Pixelization)

        if include_regularization:
            model_classes.append(ag.reg.Regularization)

        return StochasticPhase(
            phase=self,
            search=stochastic_search,
            model_classes=tuple(model_classes),
            histogram_samples=histogram_samples,
            histogram_bins=histogram_bins,
            stochastic_method=stochastic_method,
            stochastic_sigma=stochastic_sigma,
        )

    def output_phase_info(self):

        file_phase_info = "{}/{}".format(self.search.paths.output_path,
                                         "phase.info")

        with open(file_phase_info, "w") as phase_info:
            phase_info.write("Optimizer = {} \n".format(
                type(self.search).__name__))
            phase_info.write("Sub-grid size = {} \n".format(
                self.settings.settings_masked_imaging.sub_size))
            phase_info.write("PSF shape = {} \n".format(
                self.settings.settings_masked_imaging.psf_shape_2d))
            phase_info.write("Positions Threshold = {} \n".format(
                self.settings.settings_lens.positions_threshold))
            phase_info.write("Cosmology = {} \n".format(self.cosmology))

            phase_info.close()
Exemplo n.º 16
0
class Phase(af.AbstractPhase):

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, *, profiles, settings, search):
        """
        A phase which fits a model composed of multiple profiles (Gaussian, Exponential) using a non-linear search.

        Parameters
        ----------
        paths : af.Paths
            Handles the output directory structure.
        profiles : [profiles.Profile]
            The model components (e.g. Gaussian, Exponenial) fitted by this phase.
        search: class
            The class of a non_linear search
        data_trim_left : int or None
            The number of pixels by which the data is trimmed from the left-hand side.
        data_trim_right : int or None
            The number of pixels by which the data is trimmed from the right-hand side.
        """

        paths.tag = settings.tag

        super().__init__(paths=paths, search=search)

        self.profiles = profiles

        self.meta_dataset = MetaDataset(settings=settings)

    @property
    def folders(self):
        return self.search.folders

    def run(self, dataset: Dataset, mask, info=None, results=None):
        """
        Pass a dataset to the phase, running the phase and non-linear search.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.
        mask: Mask
            The mask used for the analysis.

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising information on the non-linear search and the maximum likelihood model.
        """

        # These functions save the objects we will later access using the aggregator. They are saved via the 'pickle'
        # module in Python, which serializes the data on to the hard-disk.

        # See the 'dataset.py' module for a description of what the metadata is.

        self.save_metadata(dataset=dataset)
        self.save_dataset(dataset=dataset)
        self.save_mask(mask=mask)
        self.save_meta_dataset(meta_dataset=self.meta_dataset)

        self.model = self.model.populate(results)

        results = results or af.ResultsCollection()

        # This saves the search information of the phase, meaning that we can use the search instance
        # (e.g. Emcee) to interpret our results in the aggregator.

        analysis = self.make_analysis(dataset=dataset, mask=mask, results=results)

        result = self.run_analysis(analysis=analysis, info=info)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, mask, results=None):
        """
        Create an Analysis object, which creates the dataset and contains the functions which perform the fit.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.

        Returns
        -------
        analysis : Analysis
            An analysis object that the non-linear search calls to determine the fit log_likelihood for a given model
            instance.
        """
        self.meta_dataset.model = self.model

        masked_dataset = self.meta_dataset.masked_dataset_from_dataset_and_mask(
            dataset=dataset, mask=mask
        )

        return Analysis(
            masked_dataset=masked_dataset, image_path=self.search.paths.image_path
        )

    def make_result(self, result, analysis):
        return self.Result(samples=result.samples, analysis=analysis)
Exemplo n.º 17
0
class PhaseDataset(abstract.AbstractPhase):
    galaxies = af.PhaseProperty("galaxies")

    Result = Result

    @af.convert_paths
    def __init__(
        self,
        paths,
        galaxies=None,
        optimizer_class=af.MultiNest,
        cosmology=cosmo.Planck15,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear optimizer to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        optimizer_class: class
            The class of a non_linear optimizer
        """

        super(PhaseDataset, self).__init__(paths,
                                           optimizer_class=optimizer_class)
        self.galaxies = galaxies or []
        self.cosmology = cosmology

        self.is_hyper_phase = False

    def run(self, dataset: Dataset, mask, results=None, positions=None):
        """
        Run this phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            An object describing the results of the last phase or None if no phase has been executed
        dataset: scaled_array.ScaledSquarePixelArray
            An masked_imaging that has been masked

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising the best fit model and other hyper_galaxies.
        """
        dataset.save(self.paths.phase_output_path)
        self.save_metadata(dataset)
        self.model = self.model.populate(results)

        analysis = self.make_analysis(dataset=dataset,
                                      mask=mask,
                                      results=results,
                                      positions=positions)

        self.customize_priors(results)
        self.assert_and_save_pickle()

        result = self.run_analysis(analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, mask, results=None, positions=None):
        """
        Create an lens object. Also calls the prior passing and masked_imaging modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        dataset: im.Imaging
            An masked_imaging that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the non-linear optimizer calls to determine the fit of a set of values
        """
        raise NotImplementedError()

    def extend_with_inversion_phase(self):
        return extensions.InversionPhase(phase=self)

    def extend_with_multiple_hyper_phases(
        self,
        hyper_galaxy=False,
        inversion=False,
        include_background_sky=False,
        include_background_noise=False,
        hyper_galaxy_phase_first=False,
    ):

        self.use_as_hyper_dataset = True

        hyper_phase_classes = []

        if self.meta_dataset.has_pixelization and inversion:
            if not include_background_sky and not include_background_noise:
                hyper_phase_classes.append(extensions.InversionPhase)
            elif include_background_sky and not include_background_noise:
                hyper_phase_classes.append(
                    extensions.InversionBackgroundSkyPhase)
            elif not include_background_sky and include_background_noise:
                hyper_phase_classes.append(
                    extensions.InversionBackgroundNoisePhase)
            else:
                hyper_phase_classes.append(
                    extensions.InversionBackgroundBothPhase)

        if hyper_galaxy:
            if not include_background_sky and not include_background_noise:
                hyper_phase_classes.append(
                    extensions.hyper_galaxy_phase.HyperGalaxyPhase)
            elif include_background_sky and not include_background_noise:
                hyper_phase_classes.append(extensions.hyper_galaxy_phase.
                                           HyperGalaxyBackgroundSkyPhase)
            elif not include_background_sky and include_background_noise:
                hyper_phase_classes.append(extensions.hyper_galaxy_phase.
                                           HyperGalaxyBackgroundNoisePhase)
            else:
                hyper_phase_classes.append(extensions.hyper_galaxy_phase.
                                           HyperGalaxyBackgroundBothPhase)

        if hyper_galaxy_phase_first:
            if inversion and hyper_galaxy:
                hyper_phase_classes = [
                    cls for cls in reversed(hyper_phase_classes)
                ]

        if len(hyper_phase_classes) == 0:
            return self
        else:
            return extensions.CombinedHyperPhase(
                phase=self, hyper_phase_classes=hyper_phase_classes)
Exemplo n.º 18
0
class PhaseData(phase.AbstractPhase):
    galaxies = af.PhaseProperty("galaxies")

    Result = Result
    Analysis = Analysis

    def __init__(
        self,
        phase_name,
        phase_tag,
        phase_folders=tuple(),
        galaxies=None,
        optimizer_class=af.MultiNest,
        cosmology=cosmo.Planck15,
        auto_link_priors=False,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear optimizer to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        optimizer_class: class
            The class of a non_linear optimizer
        """

        super(PhaseData, self).__init__(
            phase_name=phase_name,
            phase_tag=phase_tag,
            phase_folders=phase_folders,
            optimizer_class=optimizer_class,
            cosmology=cosmology,
            auto_link_priors=auto_link_priors,
        )
        self.galaxies = galaxies or []

        self.is_hyper_phase = False

    def run(self, data, results=None, mask=None, positions=None):
        """
        Run this phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            An object describing the results of the last phase or None if no phase has been executed
        data: scaled_array.ScaledSquarePixelArray
            An lens_data that has been masked

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising the best fit model and other hyper_galaxies.
        """
        self.variable = self.variable.populate(results)

        analysis = self.make_analysis(data=data,
                                      results=results,
                                      mask=mask,
                                      positions=positions)

        self.customize_priors(results)
        self.assert_and_save_pickle()

        result = self.run_analysis(analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, data, results=None, mask=None, positions=None):
        """
        Create an lens object. Also calls the prior passing and lens_data modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask
            The default masks passed in by the pipeline
        data: im.Imaging
            An lens_data that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the non-linear optimizer calls to determine the fit of a set of values
        """
        raise NotImplementedError()

    def extend_with_inversion_phase(self):
        return phase_extensions.InversionPhase(phase=self)
Exemplo n.º 19
0
class Phase(af.AbstractPhase):

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self,
                 paths,
                 profiles,
                 lens_redshift,
                 source_redshift,
                 regions=None,
                 non_linear_class=af.MultiNest,
                 transformer_class=al.TransformerFINUFFT):

        super().__init__(paths=paths, non_linear_class=non_linear_class)

        self.profiles = profiles

        if lens_redshift < source_redshift:
            self.lens_redshift = lens_redshift
            self.source_redshift = source_redshift
        else:
            raise ValueError(
                "The len's z={} must be lower than the source's z={}",
                format(lens_redshift, source_redshift))

        self.transformer_class = transformer_class

        if not isinstance(regions, list):
            raise ValueError("""The variable "regions" must be a list.""")
        else:
            self.regions = regions

        # TODO: Check if there is a clash between regions

        # self.continuum_idx = []
        # for region in self.regions:
        #     print(region)

        #exit()

    def run(self, dataset: Dataset, xy_mask, uv_mask=None):

        analysis = self.make_analysis(dataset=dataset,
                                      xy_mask=xy_mask,
                                      uv_mask=uv_mask)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, xy_mask, uv_mask):

        masked_dataset = MaskedDataset(dataset=dataset,
                                       xy_mask=xy_mask,
                                       uv_mask=uv_mask)

        transformers = []
        for i in range(masked_dataset.uv_wavelengths.shape[0]):
            transformers.append(
                self.transformer_class(
                    uv_wavelengths=masked_dataset.uv_wavelengths[i],
                    grid=masked_dataset.grid_3d.grid_2d.in_radians))

        return Analysis(masked_dataset=masked_dataset,
                        transformers=transformers,
                        lens_redshift=self.lens_redshift,
                        source_redshift=self.source_redshift,
                        image_path=self.optimizer.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output,
        )
Exemplo n.º 20
0
class Phase(af.AbstractPhase):

    galaxies = af.PhaseProperty("galaxies")

    Result = Result

    @af.convert_paths
    def __init__(
        self,
        paths,
        *,
        galaxies,
        regions,
        non_linear_class=af.MultiNest,
        transformer_class=al.TransformerFINUFFT
    ):

        super().__init__(paths=paths, non_linear_class=non_linear_class)

        self.galaxies = galaxies

        if not isinstance(regions, list):
            raise ValueError(
                """The variable "regions" must be a list."""
            )
        else:
            self.regions = regions

        self.transformer_class = transformer_class

    @property
    def phase_folders(self):
        return self.optimizer.phase_folders

    def run(self, dataset: Dataset, xy_mask):

        analysis = self.make_analysis(
            dataset=dataset,
            xy_mask=xy_mask
        )

        result = self.run_analysis(analysis=analysis)

        return self.make_result(
            result=result,
            analysis=analysis
        )

    def make_analysis(self, dataset, xy_mask):

        # NOTE: The masked_dataset is no longer being used, instead each compoent
        # (i.e. continuum + emission line regions) each have their own masked_dataset
        # which is initialized here.
        def masked_datasets_from_regions(masked_dataset, regions):

            args = {
                "continuum":{}
            }

            idx = np.zeros(
                shape=(masked_dataset.visibilities.shape[0], ),
                dtype=bool
            )
            for i, region in enumerate(regions):
                idx += region.idx
                args["region_{}".format(i)] = {}

            if all(idx):
                continuum = False
            else:
                continuum = True

            argspec = inspect.getargspec(MaskedDatasetLite.__init__)

            #args = {}
            for argname in argspec.args:
                if argname not in ["self"]:
                    if hasattr(masked_dataset, argname):
                        array = getattr(masked_dataset, argname)

                        if continuum:
                            args["continuum"][argname] = reshape_array(
                                array=array[~idx]
                            )

                        for i, region in enumerate(regions):
                            args["region_{}".format(i)][argname] = reshape_array(
                                array=array[region.idx]
                            )
                            #args["region_{}".format(i)][argname] = np.average(a=array[region.idx], axis=0)

            masked_datasets = {
                "continuum":MaskedDatasetLite(**args["continuum"]) if continuum else None
            }
            for i, region in enumerate(regions):
                masked_datasets["region_{}".format(i)] = MaskedDatasetLite(**args["region_{}".format(i)])

            return masked_datasets

        # NOTE: Multiple lines can be present in a cube, in which
        # case region will be a list (renamed to regions) - DONE
        # NOTE: Can we skip the initialization of the masked dataset?
        masked_dataset = MaskedDataset(
            dataset=dataset,
            xy_mask=xy_mask,
        )

        masked_datasets = masked_datasets_from_regions(
            masked_dataset=masked_dataset,
            regions=self.regions
        )

        transformers = {}
        for key in masked_datasets.keys():
            if masked_datasets[key] is not None:
                transformers[key] = self.transformer_class(
                    uv_wavelengths=masked_datasets[key].uv_wavelengths,
                    grid=masked_dataset.grid_3d.grid_2d.in_radians
                )
            else:
                transformers[key] = None

        return Analysis(
            masked_datasets=masked_datasets,
            transformers=transformers,
            grid=masked_dataset.grid_3d.grid_2d,
            image_path=self.optimizer.paths.image_path
        )


    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output,
        )
Exemplo n.º 21
0
class PhasePositions(abstract.AbstractPhase):

    galaxies = af.PhaseProperty("galaxies")

    Analysis = Analysis
    Result = Result

    @af.convert_paths
    def __init__(
            self,
            paths,
            *,
            search,
            solver,
            galaxies=None,
            settings=SettingsPhasePositions(),
            cosmology=cosmo.Planck15,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear search to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        search: class
            The class of a non_linear search
        sub_size: int
            The side length of the subgrid
        """

        super().__init__(
            paths,
            search=search,
            settings=settings,
            galaxies=galaxies,
            cosmology=cosmology,
        )

        self.solver = solver

    def make_attributes(self, analysis):
        return Attributes(cosmology=self.cosmology)

    def make_analysis(self, positions, imaging=None, results=None):
        """
        Returns an lens object. Also calls the prior passing and masked_imaging modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask2D
            The default masks passed in by the pipeline
        dataset: im.Imaging
            An masked_imaging that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the `NonLinearSearch` calls to determine the fit of a set of values
        """

        self.output_phase_info()

        analysis = self.Analysis(
            positions=positions,
            solver=self.solver,
            imaging=imaging,
            cosmology=self.cosmology,
            results=results,
        )

        return analysis

    def output_phase_info(self):

        file_phase_info = path.join(self.search.paths.output_path,
                                    "phase.info")

        with open(file_phase_info, "w") as phase_info:
            phase_info.write("Optimizer = {} \n".format(
                type(self.search).__name__))
            phase_info.write("Positions Threshold = {} \n".format(
                self.settings.positions_threshold))
            phase_info.write("Cosmology = {} \n".format(self.cosmology))

            phase_info.close()
Exemplo n.º 22
0
class PhaseGalaxy(af.AbstractPhase):
    galaxies = af.PhaseProperty("galaxies")

    Analysis = Analysis

    def __init__(
        self,
        phase_name,
        phase_folders=tuple(),
        galaxies=None,
        use_image=False,
        use_convergence=False,
        use_potential=False,
        use_deflections=False,
        optimizer_class=af.MultiNest,
        sub_size=2,
        pixel_scale_interpolation_grid=None,
        mask_function=None,
        cosmology=cosmo.Planck15,
    ):
        """
        A phase in an lens pipeline. Uses the set non_linear optimizer to try to fit
        models and hyper_galaxies passed to it.

        Parameters
        ----------
        optimizer_class: class
            The class of a non_linear optimizer
        sub_size: int
            The side length of the subgrid
        """

        super(PhaseGalaxy, self).__init__(
            phase_name=phase_name,
            phase_folders=phase_folders,
            optimizer_class=optimizer_class,
        )
        self.cosmology = cosmology
        self.use_image = use_image
        self.use_convergence = use_convergence
        self.use_potential = use_potential
        self.use_deflections = use_deflections
        self.galaxies = galaxies
        self.sub_size = sub_size
        self.pixel_scale_interpolation_grid = pixel_scale_interpolation_grid
        self.mask_function = mask_function

    def run(self, galaxy_data, results=None, mask=None):
        """
        Run this phase.

        Parameters
        ----------
        galaxy_data
        mask: Mask
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            An object describing the results of the last phase or None if no phase has
            been executed

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising the best fit model and other hyper_galaxies.
        """
        analysis = self.make_analysis(galaxy_data=galaxy_data,
                                      results=results,
                                      mask=mask)

        self.variable = self.variable.populate(results)
        self.customize_priors(results)
        self.assert_and_save_pickle()

        result = self.run_analysis(analysis)

        return self.make_result(result, analysis)

    def make_analysis(self, galaxy_data, results=None, mask=None):
        """
        Create an lens object. Also calls the prior passing and lens_data modifying
        functions to allow child classes to change the behaviour of the phase.

        Parameters
        ----------
        galaxy_data
        mask: Mask
            The default masks passed in by the pipeline
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens: Analysis
            An lens object that the non-linear optimizer calls to determine the fit of a
             set of values
        """

        mask = setup_phase_mask(
            data=galaxy_data[0],
            mask=mask,
            mask_function=self.mask_function,
            inner_mask_radii=None,
        )

        if self.use_image or self.use_convergence or self.use_potential:

            galaxy_data = gd.GalaxyFitData(
                galaxy_data=galaxy_data[0],
                mask=mask,
                pixel_scale_interpolation_grid=self.
                pixel_scale_interpolation_grid,
                use_image=self.use_image,
                use_convergence=self.use_convergence,
                use_potential=self.use_potential,
                use_deflections_y=self.use_deflections,
                use_deflections_x=self.use_deflections,
            )

            return AnalysisSingle(
                galaxy_data=galaxy_data,
                cosmology=self.cosmology,
                image_path=self.optimizer.image_path,
                results=results,
            )

        elif self.use_deflections:

            galaxy_data_y = gd.GalaxyFitData(
                galaxy_data=galaxy_data[0],
                mask=mask,
                pixel_scale_interpolation_grid=self.
                pixel_scale_interpolation_grid,
                use_image=self.use_image,
                use_convergence=self.use_convergence,
                use_potential=self.use_potential,
                use_deflections_y=self.use_deflections,
                use_deflections_x=False,
            )

            galaxy_data_x = gd.GalaxyFitData(
                galaxy_data=galaxy_data[1],
                mask=mask,
                pixel_scale_interpolation_grid=self.
                pixel_scale_interpolation_grid,
                use_image=self.use_image,
                use_convergence=self.use_convergence,
                use_potential=self.use_potential,
                use_deflections_y=False,
                use_deflections_x=self.use_deflections,
            )

            return AnalysisDeflections(
                galaxy_data_y=galaxy_data_y,
                galaxy_data_x=galaxy_data_x,
                cosmology=self.cosmology,
                image_path=self.optimizer.image_path,
                results=results,
            )
Exemplo n.º 23
0
class Phase(af.AbstractPhase):
    """
    Because we now have multiple profiles in our model, we have renamed 'gaussian' to 'profiles'. As before,
    PyAutoFit uses this information to map the input Profile classes to a model instance when performing a fit.

    Whereas the 'gaussian' variable took a single Gaussian object in the previous tutorials, the 'profiles' variable
    is a list of model component objects. The PhaseProperty class below accounts for this, such that the instance
    object passed into the log likelihood function can be iterated over like a list.
    """

    profiles = af.PhaseProperty("profiles")

    Result = Result

    @af.convert_paths
    def __init__(self, paths, profiles, search):
        """
        A phase which fits a model composed of multiple profiles (Gaussian, Exponential) using a non-linear search.

        Parameters
        ----------
        paths : af.Paths
            Handles the output directory structure.
        profiles : [profiles.Profile]
            The model components (e.g. Gaussian, Exponenial) fitted by this phase.
        search: class
            The class of a non_linear search
        """

        super().__init__(paths=paths, search=search)

        self.profiles = profiles

    def run(self, dataset: Dataset, mask):
        """
        Pass a dataset to the phase, running the phase and non-linear search.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.
        mask: Mask
            The mask used for the analysis.

        Returns
        -------
        result: AbstractPhase.Result
            A result object comprising information on the non-linear search and the maximum likelihood model.
        """

        analysis = self.make_analysis(dataset=dataset, mask=mask)

        result = self.run_analysis(analysis=analysis)

        return self.make_result(result=result, analysis=analysis)

    def make_analysis(self, dataset, mask):
        """
        Create an Analysis object, which creates the dataset and contains the functions which perform the fit.

        Parameters
        ----------
        dataset: aa.Dataset
            The dataset fitted by the phase, as defined in the 'dataset.py' module.

        Returns
        -------
        analysis : Analysis
            An analysis object that the non-linear search calls to determine the fit log_likelihood for a given model
            instance.
        """

        masked_dataset = MaskedDataset(dataset=dataset, mask=mask)

        return Analysis(masked_dataset=masked_dataset,
                        image_path=self.search.paths.image_path)

    def make_result(self, result, analysis):
        return self.Result(
            samples=result.samples,
            previous_model=self.model,
            search=self.search,
            analysis=analysis,
        )
Exemplo n.º 24
0
class PhaseInterferometer(dataset.PhaseDataset):
    galaxies = af.PhaseProperty("galaxies")
    hyper_background_noise = af.PhaseProperty("hyper_background_noise")

    Analysis = Analysis
    Result = Result

    def __init__(
        self,
        *,
        search,
        real_space_mask,
        galaxies=None,
        hyper_background_noise=None,
        settings=SettingsPhaseInterferometer(),
        cosmology=cosmo.Planck15,
        use_as_hyper_dataset=False,
    ):
        """

        A phase in an lens pipeline. Uses the set non_linear search to try to fit models and hyper_galaxies
        passed to it.

        Parameters
        ----------
        search: class
            The class of a non_linear search
        sub_size: int
            The side length of the subgrid
        """

        search.paths.tag = settings.phase_tag_with_inversion

        super().__init__(
            search=search,
            galaxies=galaxies,
            settings=settings,
            cosmology=cosmology,
            use_as_hyper_dataset=use_as_hyper_dataset,
        )

        self.hyper_background_noise = hyper_background_noise

        self.is_hyper_phase = False

        self.real_space_mask = real_space_mask

    def make_analysis(self, dataset, mask, results=None):
        """
        Returns an lens object. Also calls the prior passing and masked_interferometer modifying functions to allow child
        classes to change the behaviour of the phase.

        Parameters
        ----------
        positions
        mask: Mask2D
            The default masks passed in by the pipeline
        dataset: im.Interferometer
            An masked_interferometer that has been masked
        results: autofit.tools.pipeline.ResultsCollection
            The result from the previous phase

        Returns
        -------
        lens : Analysis
            An lens object that the `NonLinearSearch` calls to determine the fit of a set of values
        """

        masked_interferometer = interferometer.MaskedInterferometer(
            interferometer=dataset,
            visibilities_mask=mask,
            real_space_mask=self.real_space_mask,
            settings=self.settings.settings_masked_interferometer,
        )

        self.output_phase_info()

        return self.Analysis(
            masked_interferometer=masked_interferometer,
            settings=self.settings,
            cosmology=self.cosmology,
            results=results,
        )

    def output_phase_info(self):

        file_phase_info = path.join(self.search.paths.output_path,
                                    "phase.info")

        with open(file_phase_info, "w") as phase_info:
            phase_info.write("Optimizer = {} \n".format(
                type(self.search).__name__))
            phase_info.write("Sub-grid size = {} \n".format(
                self.settings.settings_masked_interferometer.sub_size))
            phase_info.write("Positions Threshold = {} \n".format(
                self.settings.settings_lens.positions_threshold))
            phase_info.write("Cosmology = {} \n".format(self.cosmology))

            phase_info.close()
Exemplo n.º 25
0
class Phase(af.AbstractPhase):

    galaxies = af.PhaseProperty("galaxies")

    Result = Result

    @af.convert_paths
    def __init__(
        self,
        paths,
        *,
        galaxies,
        region,
        non_linear_class=af.MultiNest,
        transformer_class=al.TransformerFINUFFT
    ):

        super().__init__(paths=paths, non_linear_class=non_linear_class)

        self.galaxies = galaxies
        self.region = region
        self.transformer_class = transformer_class

    def run(self, dataset: Dataset, xy_mask):

        analysis = self.make_analysis(
            dataset=dataset,
            xy_mask=xy_mask
        )

        result = self.run_analysis(analysis=analysis)

        return self.make_result(
            result=result,
            analysis=analysis
        )

    def make_analysis(self, dataset, xy_mask):

        # NOTE: Multiple lines can be present in a cube, in which
        # case region will be a list (renamed to regions)
        masked_dataset = MaskedDataset(
            dataset=dataset,
            xy_mask=xy_mask,
            region=self.region
        )

        masked_dataset_continuum = RegionMaskedDataset(
            dataset=masked_dataset.dataset_outside_region,
            uv_mask=masked_dataset.uv_mask_outside_region,
            continuum=True
        )

        masked_dataset_line = RegionMaskedDataset(
            dataset=masked_dataset.dataset_inside_region,
            uv_mask=masked_dataset.uv_mask_inside_region,
            continuum=False
        )

        transformers = []
        for i in range(masked_dataset.uv_wavelengths.shape[0]):
            transformers.append(
                self.transformer_class(
                    uv_wavelengths=masked_dataset.uv_wavelengths[i],
                    grid=masked_dataset.grid_3d.grid_2d.in_radians
                )
            )

        transformer_continuum = self.transformer_class(
            uv_wavelengths=masked_dataset_continuum.uv_wavelengths,
            grid=masked_dataset.grid_3d.grid_2d.in_radians
        )

        # # NOTE: EXPERIMENTAL
        # holder = RegionMaskedDatasetsHolder(
        #     region_masked_datasets=[
        #         masked_dataset_continuum,
        #         masked_dataset_line
        #     ]
        # )
        # exit()

        # TODO: region_masked_datasets can be a class that holds individual
        # masked datasets and it's only function will be to differentiate between
        # a masked_dataset corresponding to the continuum and the rest.
        return Analysis(
            masked_dataset=masked_dataset,
            region_masked_datasets=[
                masked_dataset_continuum,
                masked_dataset_line
            ],
            transformers=transformers,
            transformer_continuum=transformer_continuum,
            image_path=self.optimizer.paths.image_path
        )

    def make_result(self, result, analysis):
        return self.Result(
            instance=result.instance,
            likelihood=result.likelihood,
            analysis=analysis,
            output=result.output,
        )