Exemple #1
0
    def dict(self) -> dict:
        """
        A dictionary representation of this object
        """
        from autofit.mapper.prior_model.abstract import AbstractPriorModel
        from autofit.mapper.prior_model.collection import CollectionPriorModel
        from autofit.mapper.prior_model.prior_model import PriorModel
        from autofit.mapper.prior.tuple_prior import TuplePrior

        if isinstance(self, CollectionPriorModel):
            type_ = "collection"
        elif isinstance(self, AbstractPriorModel) and self.prior_count == 0:
            type_ = "instance"
        elif isinstance(self, PriorModel):
            type_ = "model"
        elif isinstance(self, TuplePrior):
            type_ = "tuple_prior"
        else:
            raise AssertionError(
                f"{self.__class__.__name__} cannot be serialised to dict")

        dict_ = {"type": type_}

        for key, value in self._dict.items():
            try:
                if not isinstance(value, ModelObject):
                    value = AbstractPriorModel.from_instance(value)
                value = value.dict()
            except AttributeError:
                pass
            except TypeError:
                pass
            dict_[key] = value
        return dict_
Exemple #2
0
    def from_results_internal(
        cls,
        results_internal: np.ndarray,
        log_posterior_list: np.ndarray,
        model: AbstractPriorModel,
        total_iterations: int,
        time: Optional[float] = None,
    ):
        """
        The `Samples` classes in **PyAutoFit** provide an interface between the results of a `NonLinearSearch` (e.g.
        as files on your hard-disk) and Python.

        To create a `Samples` object after an `pyswarms` model-fit the results must be converted from the
        native format used by `pyswarms` (which are numpy ndarrays) to lists of values, the format used by
        the **PyAutoFit** `Samples` objects.

        This classmethod performs this conversion before creating a `PySwarmsSamples` object.

        Parameters
        ----------
        results_internal
            The Pyswarms results in their native internal format from which the samples are computed.
        log_posterior_list
            The log posterior of the PySwarms accepted samples.
        model
            Maps input vectors of unit parameter values to physical values and model instances via priors.
        total_iterations
            The total number of PySwarms iterations, which cannot be estimated from the sample list (which contains
            only accepted samples).
        time
            The time taken to perform the model-fit, which is passed around `Samples` objects for outputting
            information on the overall fit.
        """
        parameter_lists = [
            param.tolist() for parameters in results_internal
            for param in parameters
        ]
        log_prior_list = model.log_prior_list_from(
            parameter_lists=parameter_lists)
        log_likelihood_list = [
            lp - prior for lp, prior in zip(log_posterior_list, log_prior_list)
        ]
        weight_list = len(log_likelihood_list) * [1.0]

        sample_list = Sample.from_lists(
            model=model,
            parameter_lists=[
                parameters.tolist()[0] for parameters in results_internal
            ],
            log_likelihood_list=log_likelihood_list,
            log_prior_list=log_prior_list,
            weight_list=weight_list)

        return PySwarmsSamples(
            model=model,
            sample_list=sample_list,
            total_iterations=total_iterations,
            time=time,
            results_internal=results_internal,
        )
Exemple #3
0
 def __setitem__(self, key, value):
     obj = AbstractPriorModel.from_object(value)
     try:
         obj.id = getattr(self, str(key)).id
     except AttributeError:
         pass
     setattr(self, str(key), obj)
Exemple #4
0
    def fit(
            self,
            model: abstract.AbstractPriorModel,
            analysis: abstract_search.Analysis
    ):
        best_likelihood = float("-inf")
        best_instance = None

        likelihoods = list()

        for list_ in make_lists(
                no_dimensions=model.prior_count,
                step_size=self.step_size
        ):
            instance = model.instance_from_unit_vector(
                list_
            )
            likelihood = analysis.log_likelihood_function(
                instance
            )
            likelihoods.append(likelihood)
            if likelihood > best_likelihood:
                best_likelihood = likelihood
                best_instance = instance

        return Result(
            samples=MockSamples(
                max_log_likelihood_instance=best_instance,
                log_likelihood_list=likelihoods,
                gaussian_tuples=None
            ),
            model=model
        )
Exemple #5
0
    def from_results_internal(
            cls,
            results_internal: Results,
            model: AbstractPriorModel,
            number_live_points: int,
            unconverged_sample_size: int = 100,
            time: Optional[float] = None,
    ):
        """
        The `Samples` classes in **PyAutoFit** provide an interface between the results of a `NonLinearSearch` (e.g.
        as files on your hard-disk) and Python.

        To create a `Samples` object after a `dynesty` model-fit the results must be converted from the
        native format used by `dynesty` to lists of values, the format used by the **PyAutoFit** `Samples` objects.

        This classmethod performs this conversion before creating a `DynestySamples` object.

        Parameters
        ----------
        results_internal
            The `dynesty` results in their native internal format from which the samples are computed.
        model
            Maps input vectors of unit parameter values to physical values and model instances via priors.
        number_live_points
            The number of live points used by the `dynesty` search.
        unconverged_sample_size
            If the samples are for a search that is yet to convergence, a reduced set of samples are used to provide
            a rough estimate of the parameters. The number of samples is set by this parameter.
        time
            The time taken to perform the model-fit, which is passed around `Samples` objects for outputting
            information on the overall fit.
        """
        parameter_lists = results_internal.samples.tolist()
        log_prior_list = model.log_prior_list_from(parameter_lists=parameter_lists)
        log_likelihood_list = list(results_internal.logl)

        try:
            weight_list = list(
                np.exp(np.asarray(results_internal.logwt) - results_internal.logz[-1])
            )
        except:
            weight_list = results_internal["weights"]

        sample_list = Sample.from_lists(
            model=model,
            parameter_lists=parameter_lists,
            log_likelihood_list=log_likelihood_list,
            log_prior_list=log_prior_list,
            weight_list=weight_list,
        )

        return DynestySamples(
            model=model,
            sample_list=sample_list,
            number_live_points=number_live_points,
            unconverged_sample_size=unconverged_sample_size,
            time=time,
            results_internal=results_internal,
        )
Exemple #6
0
 def __setattr__(self, key, value):
     if key.startswith("_"):
         super().__setattr__(key, value)
     else:
         try:
             super().__setattr__(key, AbstractPriorModel.from_object(value))
         except AttributeError:
             pass
Exemple #7
0
    def from_dict(d):
        """
        Recursively parse a dictionary returning the model, collection or
        instance that is represents.

        Parameters
        ----------
        d
            A dictionary representation of some object

        Returns
        -------
        An instance
        """
        from autofit.mapper.prior_model.abstract import AbstractPriorModel
        from autofit.mapper.prior_model.collection import CollectionPriorModel
        from autofit.mapper.prior_model.prior_model import PriorModel
        from autofit.mapper.prior.prior import Prior
        from autofit.mapper.prior.prior import TuplePrior

        if not isinstance(
                d, dict
        ):
            return d

        type_ = d["type"]

        if type_ == "model":
            instance = PriorModel(
                get_class(
                    d.pop("class_path")
                )
            )
        elif type_ == "collection":
            instance = CollectionPriorModel()
        elif type_ == "instance":
            cls = get_class(
                d.pop("class_path")
            )
            instance = object.__new__(cls)
        elif type_ == "tuple_prior":
            instance = TuplePrior()
        else:
            return Prior.from_dict(d)

        d.pop("type")

        for key, value in d.items():
            setattr(
                instance,
                key,
                AbstractPriorModel.from_dict(value)
            )
        return instance
Exemple #8
0
    def instance_for_model(self, model: AbstractPriorModel):
        """
        Create an instance from this sample for a model

        Parameters
        ----------
        model
            The model the this sample was taken from

        Returns
        -------
        The instance corresponding to this sample
        """
        try:
            return model.instance_from_vector(
                self.parameter_lists_for_model(model))
        except KeyError:
            paths = model.model_component_and_parameter_names
            return model.instance_from_vector(
                self.parameter_lists_for_model(model, paths))
Exemple #9
0
    def samples_in_test_mode(self, total_points: int,
                             model: AbstractPriorModel):
        """
        Generate the initial points of the non-linear search in test mode. Like normal, test model draws points, by
        randomly drawing unit values from a uniform distribution between the ball_lower_limit and ball_upper_limit
        values.

        However, the log likelihood function is bypassed and all likelihoods are returned with a value -1.0e99. This
        is so that integration testing of large-scale model-fitting projects can be performed efficiently by bypassing
        sampling of points using the `log_likelihood_function`.

        Parameters
        ----------
        total_points
            The number of points in non-linear paramemter space which initial points are created for.
        model
            An object that represents possible instances of some model with a given dimensionality which is the number
            of free dimensions of the model.
        """

        unit_parameter_lists = []
        parameter_lists = []
        figure_of_merit_list = []

        point_index = 0

        while point_index < total_points:

            unit_parameter_list = model.random_unit_vector_within_limits(
                lower_limit=self.lower_limit, upper_limit=self.upper_limit)
            parameter_list = model.vector_from_unit_vector(
                unit_vector=unit_parameter_list)
            unit_parameter_lists.append(unit_parameter_list)
            parameter_lists.append(parameter_list)
            figure_of_merit_list.append(-1.0e99)
            point_index += 1

        return unit_parameter_lists, parameter_lists, figure_of_merit_list
Exemple #10
0
    def instance_for_model(self, model: AbstractPriorModel):
        """
        Create an instance from this sample for a model

        Parameters
        ----------
        model
            The model the this sample was taken from

        Returns
        -------
        The instance corresponding to this sample
        """
        try:
            if self.is_path_kwargs:
                return model.instance_from_path_arguments(self.kwargs)
            else:
                return model.instance_from_prior_name_arguments(self.kwargs)

        except KeyError:
            # TODO: Does this get used? If so, why?
            return model.instance_from_vector(
                self.parameter_lists_for_model(model))
    def _fit(self,
             model: AbstractPriorModel,
             analysis,
             log_likelihood_cap=None):
        if self.fit_fast:
            result = self._fit_fast(model=model, analysis=analysis)
            return result

        if model.prior_count == 0:
            raise AssertionError(
                "There are no priors associated with the model!")
        if model.prior_count != len(model.unique_prior_paths):
            raise AssertionError(
                "Prior count doesn't match number of unique prior paths")
        index = 0
        unit_vector = model.prior_count * [0.5]
        while True:
            try:
                instance = model.instance_from_unit_vector(unit_vector)
                fit = analysis.log_likelihood_function(instance)
                break
            except exc.FitException as e:
                unit_vector[index] += 0.1
                if unit_vector[index] >= 1:
                    raise e
                index = (index + 1) % model.prior_count
        samples = MockSamples(
            samples=samples_with_log_likelihood_list(self.sample_multiplier *
                                                     fit),
            model=model,
            gaussian_tuples=[
                (prior.mean,
                 prior.width if math.isfinite(prior.width) else 1.0)
                for prior in sorted(model.priors, key=lambda prior: prior.id)
            ],
        )

        self.paths.save_samples(samples)

        return MockResult(
            model=model,
            samples=samples,
        )
Exemple #12
0
    def from_results_internal(
        cls,
        results_internal,
        model: AbstractPriorModel,
        auto_correlation_settings: AutoCorrelationsSettings,
        unconverged_sample_size: int = 100,
        time: Optional[float] = None,
    ):
        """
        The `Samples` classes in **PyAutoFit** provide an interface between the results of a `NonLinearSearch` (e.g.
        as files on your hard-disk) and Python.

        To create a `Samples` object after an `Zeus` model-fit the results must be converted from the
        native format used by `Zeus` (which is a HDFBackend) to lists of values, the format used by the **PyAutoFit**
        `Samples` objects.

        This classmethod performs this conversion before creating a `ZeusSamples` object.

        Parameters
        ----------
        results_internal
            The MCMC results in their native internal format from which the samples are computed.
        model
            Maps input vectors of unit parameter values to physical values and model instances via priors.
        auto_correlations_settings
            Customizes and performs auto correlation calculations performed during and after the search.
        unconverged_sample_size
            If the samples are for a search that is yet to convergence, a reduced set of samples are used to provide
            a rough estimate of the parameters. The number of samples is set by this parameter.
        time
            The time taken to perform the model-fit, which is passed around `Samples` objects for outputting
            information on the overall fit.
        """

        parameter_lists = results_internal.get_chain(flat=True).tolist()
        log_posterior_list = results_internal.get_log_prob(flat=True).tolist()
        log_prior_list = model.log_prior_list_from(
            parameter_lists=parameter_lists)

        log_likelihood_list = [
            log_posterior - log_prior for log_posterior, log_prior in zip(
                log_posterior_list, log_prior_list)
        ]

        weight_list = len(log_likelihood_list) * [1.0]

        sample_list = Sample.from_lists(
            model=model,
            parameter_lists=parameter_lists,
            log_likelihood_list=log_likelihood_list,
            log_prior_list=log_prior_list,
            weight_list=weight_list)

        return ZeusSamples(
            model=model,
            sample_list=sample_list,
            auto_correlation_settings=auto_correlation_settings,
            unconverged_sample_size=unconverged_sample_size,
            time=time,
            results_internal=results_internal,
        )
Exemple #13
0
    def _fit(self, model: AbstractPriorModel, analysis, log_likelihood_cap=None):
        """
        Fit a model using PySwarms and the Analysis class which contains the data and returns the log likelihood from
        instances of the model, which the `NonLinearSearch` seeks to maximize.

        Parameters
        ----------
        model : ModelMapper
            The model which generates instances for different points in parameter space.
        analysis : Analysis
            Contains the data and the log likelihood function which fits an instance of the model to the data, returning
            the log likelihood the `NonLinearSearch` maximizes.

        Returns
        -------
        A result object comprising the Samples object that inclues the maximum log likelihood instance and full
        chains used by the fit.
        """

        fitness_function = self.fitness_function_from_model_and_analysis(
            model=model, analysis=analysis
        )

        if self.paths.is_object("points"):

            init_pos = self.paths.load_object("points")[-1]
            total_iterations = self.paths.load_object("total_iterations")

            self.logger.info("Existing PySwarms samples found, resuming non-linear search.")

        else:

            unit_parameter_lists, parameter_lists, log_posterior_list = self.initializer.samples_from_model(
                total_points=self.config_dict_search["n_particles"],
                model=model,
                fitness_function=fitness_function,
            )

            init_pos = np.zeros(shape=(self.config_dict_search["n_particles"], model.prior_count))

            for index, parameters in enumerate(parameter_lists):

                init_pos[index, :] = np.asarray(parameters)

            total_iterations = 0

            self.logger.info("No PySwarms samples found, beginning new non-linear search. ")

        ## TODO : Use actual limits

        vector_lower = model.vector_from_unit_vector(
            unit_vector=[1e-6] * model.prior_count,
            ignore_prior_limits=True
        )
        vector_upper = model.vector_from_unit_vector(
            unit_vector=[0.9999999] * model.prior_count,
            ignore_prior_limits=True
        )

        lower_bounds = [lower for lower in vector_lower]
        upper_bounds = [upper for upper in vector_upper]

        bounds = (np.asarray(lower_bounds), np.asarray(upper_bounds))

        self.logger.info("Running PySwarmsGlobal Optimizer...")

        while total_iterations < self.config_dict_run["iters"]:

            pso = self.sampler_from(
                model=model,
                fitness_function=fitness_function,
                bounds=bounds,
                init_pos=init_pos
            )

            iterations_remaining = self.config_dict_run["iters"] - total_iterations

            iterations = min(self.iterations_per_update, iterations_remaining)

            if iterations > 0:

                pso.optimize(objective_func=fitness_function.__call__, iters=iterations)

                total_iterations += iterations

                self.paths.save_object(
                    "total_iterations",
                    total_iterations
                )
                self.paths.save_object(
                    "points",
                    pso.pos_history
                )
                self.paths.save_object(
                    "log_posterior_list",
                    [-0.5 * cost for cost in pso.cost_history]
                )

                self.perform_update(
                    model=model, analysis=analysis, during_analysis=True
                )

                init_pos = self.paths.load_object("points")[-1]

        self.logger.info("PySwarmsGlobal complete")
Exemple #14
0
    def samples_from_model(self,
                           total_points: int,
                           model: AbstractPriorModel,
                           fitness_function,
                           use_prior_medians: bool = False):
        """
        Generate the initial points of the non-linear search, by randomly drawing unit values from a uniform
        distribution between the ball_lower_limit and ball_upper_limit values.

        Parameters
        ----------
        total_points
            The number of points in non-linear paramemter space which initial points are created for.
        model
            An object that represents possible instances of some model with a given dimensionality which is the number
            of free dimensions of the model.
        """

        if conf.instance["general"]["test"]["test_mode"]:
            return self.samples_in_test_mode(total_points=total_points,
                                             model=model)

        logger.info(
            "Generating initial samples of model, which are subject to prior limits and other constraints."
        )

        unit_parameter_lists = []
        parameter_lists = []
        figures_of_merit_list = []

        point_index = 0

        while point_index < total_points:

            if not use_prior_medians:

                unit_parameter_list = model.random_unit_vector_within_limits(
                    lower_limit=self.lower_limit, upper_limit=self.upper_limit)

                try:
                    parameter_list = model.vector_from_unit_vector(
                        unit_vector=unit_parameter_list)
                except exc.PriorLimitException:
                    continue

            else:

                unit_parameter_list = [0.5] * model.prior_count
                parameter_list = model.vector_from_unit_vector(
                    unit_vector=unit_parameter_list)

            try:
                figure_of_merit = fitness_function.figure_of_merit_from(
                    parameter_list=parameter_list)

                if np.isnan(figure_of_merit):
                    raise exc.FitException

                unit_parameter_lists.append(unit_parameter_list)
                parameter_lists.append(parameter_list)
                figures_of_merit_list.append(figure_of_merit)
                point_index += 1
            except exc.FitException:
                pass

        return unit_parameter_lists, parameter_lists, figures_of_merit_list
Exemple #15
0
 def append(self, item):
     setattr(self, str(self.item_number),
             AbstractPriorModel.from_object(item))
     self.item_number += 1
    def as_model(self, model_classes=tuple()):
        from autofit.mapper.prior_model.abstract import AbstractPriorModel

        return AbstractPriorModel.from_instance(self, model_classes)
Exemple #17
0
 def add_dict_items(self, item_dict):
     for key, value in item_dict.items():
         setattr(self, key, AbstractPriorModel.from_object(value))