Ejemplo n.º 1
0
    def __init__(self,
                 priors,
                 likelihood,
                 global_options={},
                 manual_constraints={},
                 fixed_parameters={}):
        """Constructor."""
        self.init_args = {
            'priors': priors,
            'likelihood': likelihood,
            'global_options': global_options,
            'manual_constraints': manual_constraints,
            'fixed_parameters': fixed_parameters
        }
        self.parameters = eos.Parameters.Defaults()
        self.global_options = eos.Options()
        self.log_likelihood = eos.LogLikelihood(self.parameters)
        self.log_posterior = eos.LogPosterior(self.log_likelihood)
        self.varied_parameters = []
        self.bounds = []

        eos.info(
            'Creating analysis with {nprior} priors, {nconst} EOS-wide constraints, {nopts} global options, {nmanual} manually-entered constraints and {nparams} fixed parameters.'
            .format(nprior=len(priors),
                    nconst=len(likelihood),
                    nopts=len(global_options),
                    nmanual=len(manual_constraints),
                    nparams=len(fixed_parameters)))
        eos.debug('priors:')
        for p in priors:
            eos.debug(' - {name} ({type}) [{min}, {max}]'.format(
                name=p['parameter'],
                type=p['type'],
                min=p['min'],
                max=p['max']))
        eos.debug('constraints:')
        for cn in likelihood:
            eos.debug(' - {name}'.format(name=cn))
        eos.debug('manual_constraints:')
        for cn, ce in manual_constraints.items():
            eos.debug(' - {name}'.format(name=cn))
        eos.debug('fixed_parameters:')
        for pn, pe in fixed_parameters.items():
            eos.debug(' - {name}'.format(name=pn))

        # collect the global options
        for key, value in global_options.items():
            self.global_options.set(key, value)

        # Fix specified parameters
        for param, value in fixed_parameters.items():
            self.parameters.set(param, value)

        # create the priors
        for prior in priors:
            parameter = prior['parameter']
            minv = prior['min']
            maxv = prior['max']
            prior_type = prior['type'] if 'type' in prior else 'uniform'
            if 'uniform' == prior_type or 'flat' == prior_type:
                self.log_posterior.add(
                    eos.LogPrior.Flat(self.parameters, parameter,
                                      eos.ParameterRange(minv, maxv)), False)
            elif 'gauss' == prior_type or 'gaussian' == prior_type:
                central = prior['central']
                sigma = prior['sigma']
                if type(sigma) is list or type(sigma) is tuple:
                    sigma_lo = sigma[0]
                    sigma_hi = sigma[1]
                else:
                    sigma_lo = sigma
                    sigma_hi = sigma
                self.log_posterior.add(
                    eos.LogPrior.Gauss(self.parameters, parameter,
                                       eos.ParameterRange(minv, maxv),
                                       central - sigma_lo, central,
                                       central + sigma_hi), False)
            else:
                raise ValueError(
                    'Unknown prior type \'{}\''.format(prior_type))

            self.bounds.append((minv, maxv))
            p = self.parameters[parameter]
            p.set_min(minv)
            p.set_max(maxv)
            self.varied_parameters.append(p)

        # create the likelihood
        for constraint_name in likelihood:
            constraint = eos.Constraint.make(constraint_name,
                                             self.global_options)
            self.log_likelihood.add(constraint)

        # add manual constraints to the likelihood
        for constraint_name, constraint_data in manual_constraints.items():
            import yaml
            yaml_string = yaml.dump(constraint_data)
            constraint_entry = eos.ConstraintEntry.deserialize(
                constraint_name, yaml_string)
            constraint = constraint_entry.make(constraint_name,
                                               self.global_options)
            self.log_likelihood.add(constraint)

        # perform some sanity checks
        varied_parameter_names = set(
            [p.name() for p in self.varied_parameters])
        used_parameter_names = set()
        for observable in self.log_likelihood.observable_cache():
            for i in observable.used_parameter_ids():
                used_parameter_names.add(self.parameters.by_id(i).name())

        used_but_unvaried = used_parameter_names - varied_parameter_names
        if (len(used_but_unvaried) > 0):
            eos.info(
                'likelihood probably depends on {} parameter(s) that do not appear in the prior; check prior?'
                .format(len(used_but_unvaried)))
        for n in used_but_unvaried:
            eos.debug('used, but not included in any prior: \'{}\''.format(n))
        for n in varied_parameter_names - used_parameter_names:
            eos.warn(
                'likelihood does not depend on parameter \'{}\'; remove from prior or check options!'
                .format(n))
Ejemplo n.º 2
0
    def sample_pmc(self,
                   log_proposal,
                   step_N=1000,
                   steps=10,
                   final_N=5000,
                   rng=np.random.mtrand):
        """
        Return samples of the parameters and log(weights)

        Obtains random samples of the log(posterior) using adaptive importance sampling following
        the Popoulation Monte Carlo approach with PyPMC.

        :param step_N: Number of samples that shall be drawn in each adaptation step.
        :param steps: Number of adaptation steps.
        :param final_N: Number of samples that shall be drawn after all adaptation steps.
        :param rng: Optional random number generator (must be compatible with the requirements of pypmc.sampler.importance_sampler.ImportancSampler)

        :return: A tuple of the parameters as array of length N = pre_N * steps + final_N, the (linear) weights as array of length N, and the
            final proposal function as pypmc.density.mixture.MixtureDensity.

        .. note::
           This method requires the PyPMC python module, which can be installed from PyPI.
        """
        import pypmc
        try:
            from tqdm import tqdm
            progressbar = tqdm
        except ImportError:
            progressbar = lambda x: x

        ind_lower = np.array([bound[0] for bound in self.bounds])
        ind_upper = np.array([bound[1] for bound in self.bounds])
        ind = pypmc.tools.indicator.hyperrectangle(ind_lower, ind_upper)

        log_target = pypmc.tools.indicator.merge_function_with_indicator(
            self.log_pdf, ind, -np.inf)

        # create PMC sampler
        sampler = pypmc.sampler.importance_sampling.ImportanceSampler(
            log_target, log_proposal, save_target_values=True)  #, rng=rng)
        generating_components = []

        # carry out adaptions
        for step in progressbar(range(steps)):
            origins = sampler.run(step_N, trace_sort=True)
            generating_components.append(origins)
            samples = sampler.samples[:]
            weights = sampler.weights[:][:, 0]
            normalized_weights = np.ma.masked_where(weights <= 0,
                                                    weights) / np.sum(weights)
            entropy = -1.0 * np.dot(np.log(normalized_weights),
                                    normalized_weights)
            perplexity = np.exp(entropy) / len(normalized_weights)
            eos.debug('Perplexity after sampling in step {}: {}'.format(
                step, perplexity))
            pypmc.mix_adapt.pmc.gaussian_pmc(samples,
                                             sampler.proposal,
                                             weights,
                                             mincount=0,
                                             rb=True,
                                             copy=False)
            sampler.proposal.normalize()

        # draw final samples
        origins = sampler.run(final_N, trace_sort=True)
        generating_components.append(origins)
        samples = sampler.samples[:]
        weights = sampler.weights[:][:, 0]
        normalized_weights = np.ma.masked_where(weights <= 0,
                                                weights) / np.sum(weights)
        entropy = -1.0 * np.dot(np.log(normalized_weights), normalized_weights)
        perplexity = np.exp(entropy) / len(normalized_weights)
        eos.info('Perplexity after final samples: {}'.format(perplexity))

        return samples, weights, sampler.proposal