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))
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