def set_reference(self, ref_info): """ Sets or updates the reference pdf with the given parameter input info. ``ref_info`` should be a dict ``{parameter_name: [ref definition]}``, not ``{parameter_name: {"ref": [ref definition]}}``. When called after prior initialisation, not mentioning a parameter leaves its reference pdf unchanged, whereas explicitly setting ``ref: None`` sets the prior as the reference pdf. You can set different reference pdf's/values for different MPI processes, e.g. for fixing different starting points for parallel MCMC chains. """ if not hasattr(self, "ref_pdf"): # Initialised with nan's in case ref==None: no ref -> uses prior self.ref_pdf = [np.nan] * self.d() unknown = set(ref_info).difference(self.params) if unknown: raise LoggedError( self.log, f"Cannot set reference pdf for parameter(s) {unknown}: " "not sampled parameters.") for i, p in enumerate(self.params): # The next if ensures correct behavious in "update call", # where not mentioning a parameter and making its ref None are different # (not changing vs setting to prior) if p not in ref_info: continue # init: use prior; update: don't change ref = ref_info[p] # [number, number] interpreted as Gaussian if isinstance(ref, Sequence) and len(ref) == 2 and all( isinstance(n, numbers.Number) for n in ref): ref = {"dist": "norm", "loc": ref[0], "scale": ref[1]} if isinstance(ref, numbers.Real): self.ref_pdf[i] = float(ref) elif isinstance(ref, Mapping): self.ref_pdf[i] = get_scipy_1d_pdf({p: ref}) elif ref is None: # We only get here if explicit `param: None` mention! self.ref_pdf[i] = np.nan else: raise LoggedError( self.log, "'ref' for starting position should be None or a number" ", a list of two numbers for normal mean and deviation," "or a dict with parameters for a scipy distribution.") # Re-set the pointlike-ref property if hasattr(self, "_ref_is_pointlike"): delattr(self, "_ref_is_pointlike") self.reference_is_pointlike
def __init__(self, parameterization: Parameterization, info_prior: Optional[PriorsDict] = None): """ Initializes the prior and reference pdf's from the input information. """ self.set_logger() self._parameterization = parameterization sampled_params_info = parameterization.sampled_params_info() # pdf: a list of independent components # in principle, separable: one per parameter self.params = [] self.pdf = [] self.ref_pdf = [] self._ref_is_pointlike = True self._bounds = np.zeros((len(sampled_params_info), 2)) for i, p in enumerate(sampled_params_info): self.params += [p] prior = sampled_params_info[p].get("prior") self.pdf += [get_scipy_1d_pdf({p: prior})] fast_logpdf = fast_logpdfs.get(self.pdf[-1].dist.name) if fast_logpdf: self.pdf[-1].logpdf = MethodType(fast_logpdf, self.pdf[-1]) # Get the reference (1d) pdf ref = sampled_params_info[p].get("ref") # Cases: number, pdf (something, but not a number), nothing if isinstance(ref, Sequence) and len(ref) == 2 and all( isinstance(n, numbers.Number) for n in ref): ref = {"dist": "norm", "loc": ref[0], "scale": ref[1]} if isinstance(ref, numbers.Real): self.ref_pdf += [float(ref)] elif isinstance(ref, Mapping): self.ref_pdf += [get_scipy_1d_pdf({p: ref})] self._ref_is_pointlike = False elif ref is None: self.ref_pdf += [np.nan] self._ref_is_pointlike = False else: raise LoggedError( self.log, "'ref' for starting position should be None or a number" ", a list of two numbers for normal mean and deviation," "or a dict with parameters for a scipy distribution.") self._bounds[i] = [-np.inf, np.inf] try: self._bounds[i] = self.pdf[-1].interval(1) except AttributeError: raise LoggedError( self.log, "No bounds defined for parameter '%s' " "(maybe not a scipy 1d pdf).", p) self._uniform_indices = np.array([ i for i, pdf in enumerate(self.pdf) if pdf.dist.name == 'uniform' ], dtype=int) self._non_uniform_indices = np.array([ i for i in range(len(self.pdf)) if i not in self._uniform_indices ], dtype=int) self._non_uniform_logpdf = [ self.pdf[i].logpdf for i in self._non_uniform_indices ] self._upper_limits = self._bounds[:, 1].copy() self._lower_limits = self._bounds[:, 0].copy() self._uniform_logp = -np.sum( np.log(self._upper_limits[self._uniform_indices] - self._lower_limits[self._uniform_indices])) # Process the external prior(s): self.external = {} self.external_dependence = set() info_prior = info_prior or {} for name in info_prior: if name == prior_1d_name: raise LoggedError( self.log, "The name '%s' is a reserved prior name. " "Please use a different one.", prior_1d_name) self.log.debug("Loading external prior '%s' from: '%s'", name, info_prior[name]) logp = get_external_function(info_prior[name], name=name) argspec = getfullargspec(logp) known = set(parameterization.input_params()) params = [p for p in argspec.args if p in known] params_without_default = set( argspec.args[:(len(argspec.args) - len(argspec.defaults or []))]) unknown = params_without_default - known if unknown: if unknown.intersection(parameterization.derived_params()): err = ( "External prior '%s' has arguments %s that are output derived " "parameters, Priors must be functions of input parameters. " "Use a separate 'likelihood' for the prior if needed.") else: err = ( "Some of the arguments of the external prior '%s' cannot be " "found and don't have a default value either: %s") raise LoggedError(self.log, err, name, list(unknown)) self.external_dependence.update(params) self.external[name] = ExternalPrior(logp=logp, params=params) self.mpi_warning( "External prior '%s' loaded. " "Mind that it might not be normalized!", name) parameterization.check_dropped(self.external_dependence)
def __init__(self, parameterization, info_prior=None): """ Initializes the prior and reference pdf's from the input information. """ self.set_logger() constant_params_info = parameterization.constant_params() sampled_params_info = parameterization.sampled_params_info() if not sampled_params_info: self.mpi_warning("No sampled parameters requested! " "This will fail for non-mock samplers.") # pdf: a list of independent components # in principle, separable: one per parameter self.params = [] self.pdf = [] self.ref_pdf = [] self._ref_is_pointlike = True self._bounds = np.zeros((len(sampled_params_info), 2)) for i, p in enumerate(sampled_params_info): self.params += [p] prior = sampled_params_info[p].get(_prior) self.pdf += [get_scipy_1d_pdf({p: prior})] fast_logpdf = fast_logpdfs.get(self.pdf[-1].dist.name) if fast_logpdf: self.pdf[-1].logpdf = MethodType(fast_logpdf, self.pdf[-1]) # Get the reference (1d) pdf ref = sampled_params_info[p].get(partag.ref) # Cases: number, pdf (something, but not a number), nothing if isinstance(ref, numbers.Real): self.ref_pdf += [float(ref)] elif ref is not None: self.ref_pdf += [get_scipy_1d_pdf({p: ref})] self._ref_is_pointlike = False else: self.ref_pdf += [np.nan] self._ref_is_pointlike = False self._bounds[i] = [-np.inf, np.inf] try: self._bounds[i] = self.pdf[-1].interval(1) except AttributeError: raise LoggedError( self.log, "No bounds defined for parameter '%s' " "(maybe not a scipy 1d pdf).", p) self._uniform_indices = np.array([ i for i, pdf in enumerate(self.pdf) if pdf.dist.name == 'uniform' ], dtype=int) self._non_uniform_indices = np.array([ i for i in range(len(self.pdf)) if i not in self._uniform_indices ], dtype=int) self._non_uniform_logpdf = [ self.pdf[i].logpdf for i in self._non_uniform_indices ] self._upper_limits = self._bounds[:, 1].copy() self._lower_limits = self._bounds[:, 0].copy() self._uniform_logp = -np.sum( np.log(self._upper_limits[self._uniform_indices] - self._lower_limits[self._uniform_indices])) # Process the external prior(s): self.external = {} for name in (info_prior if info_prior else {}): if name == _prior_1d_name: raise LoggedError( self.log, "The name '%s' is a reserved prior name. " "Please use a different one.", _prior_1d_name) self.log.debug("Loading external prior '%s' from: '%s'", name, info_prior[name]) opts = {"logp": get_external_function(info_prior[name], name=name)} self.external[name] = opts opts["argspec"] = (getfullargspec(opts["logp"])) opts["params"] = { p: list(sampled_params_info).index(p) for p in opts["argspec"].args if p in sampled_params_info } opts["constant_params"] = { p: constant_params_info[p] for p in opts["argspec"].args if p in constant_params_info } if (not (len(opts["params"]) + len(opts["constant_params"]))): raise LoggedError( self.log, "None of the arguments of the external prior '%s' " "are known *fixed* or *sampled* parameters. " "This prior recognizes: %r", name, opts["argspec"].args) params_without_default = opts["argspec"].args[:( len(opts["argspec"].args) - len(opts["argspec"].defaults or []))] if not all((p in opts["params"] or p in opts["constant_params"]) for p in params_without_default): raise LoggedError( self.log, "Some of the arguments of the external prior '%s' cannot " "be found and don't have a default value either: %s", name, list( set(params_without_default).difference( opts["params"]).difference( opts["constant_params"]))) self.mpi_warning( "External prior '%s' loaded. " "Mind that it might not be normalized!", name)
def __init__(self, parameterization, info_prior=None): """ Initializes the prior and reference pdf's from the input information. """ constant_params_info = parameterization.constant_params() sampled_params_info = parameterization.sampled_params_info() if not sampled_params_info: log.warning("No sampled parameters requested! " "This will fail for non-mock samplers.") # pdf: a list of independent components # in principle, separable: one per parameter self.name = [] self.pdf = [] self.ref_pdf = [] self._bounds = np.zeros((len(sampled_params_info), 2)) for i, p in enumerate(sampled_params_info): self.name += [p] prior = sampled_params_info[p].get(_prior) self.pdf += [get_scipy_1d_pdf({p: prior})] fast_logpdf = fast_logpdfs.get(self.pdf[-1].dist.name) if fast_logpdf: self.pdf[-1].logpdf = MethodType(fast_logpdf, self.pdf[-1]) # Get the reference (1d) pdf ref = sampled_params_info[p].get(_p_ref) # Cases: number, pdf (something, but not a number), nothing if isinstance(ref, numbers.Number): self.ref_pdf += [float(ref)] elif ref is not None: self.ref_pdf += [get_scipy_1d_pdf({p: ref})] else: self.ref_pdf += [np.nan] self._bounds[i] = [-np.inf, np.inf] try: self._bounds[i] = self.pdf[-1].interval(1) except AttributeError: log.error( "No bounds defined for parameter '%s' " "(maybe not a scipy 1d pdf).", p) raise HandledException # Process the external prior(s): self.external = odict() for name in (info_prior if info_prior else {}): if name == _prior_1d_name: log.error( "The name '%s' is a reserved prior name. " "Please use a different one.", _prior_1d_name) raise HandledException log.debug("Loading external prior '%s' from: '%s'", name, info_prior[name]) self.external[name] = ({ "logp": get_external_function(info_prior[name], name=name) }) self.external[name]["argspec"] = (getargspec( self.external[name]["logp"])) self.external[name]["params"] = { p: list(sampled_params_info).index(p) for p in self.external[name]["argspec"].args if p in sampled_params_info } self.external[name]["constant_params"] = { p: constant_params_info[p] for p in self.external[name]["argspec"].args if p in constant_params_info } if (not (len(self.external[name]["params"]) + len(self.external[name]["constant_params"]))): log.error( "None of the arguments of the external prior '%s' " "are known *fixed* or *sampled* parameters. " "This prior recognizes: %r", name, self.external[name]["argspec"].args) raise HandledException params_without_default = self.external[name]["argspec"].args[:( len(self.external[name]["argspec"].args) - len(self.external[name]["argspec"].defaults or []))] if not all([(p in self.external[name]["params"] or p in self.external[name]["constant_params"]) for p in params_without_default]): log.error( "Some of the arguments of the external prior '%s' " "cannot be found and don't have a default value either: %s", name, list( set(params_without_default).difference( self.external[name]["params"]).difference( self.external[name]["constant_params"]))) raise HandledException log.warning( "External prior '%s' loaded. " "Mind that it might not be normalized!", name)
def __init__(self, parameterization: Parameterization, info_prior: Optional[PriorsDict] = None): """ Initializes the prior and reference pdf's from the input information. """ self.set_logger() self._parameterization = parameterization sampled_params_info = parameterization.sampled_params_info() # pdf: a list of independent components # in principle, separable: one per parameter self.params = [] self.pdf = [] self._bounds = np.zeros((len(sampled_params_info), 2)) for i, p in enumerate(sampled_params_info): self.params += [p] prior = sampled_params_info[p].get("prior") self.pdf += [get_scipy_1d_pdf({p: prior})] fast_logpdf = fast_logpdfs.get(self.pdf[-1].dist.name) if fast_logpdf: self.pdf[-1].logpdf = MethodType(fast_logpdf, self.pdf[-1]) self._bounds[i] = [-np.inf, np.inf] try: self._bounds[i] = self.pdf[-1].interval(1) except AttributeError: raise LoggedError( self.log, "No bounds defined for parameter '%s' " "(maybe not a scipy 1d pdf).", p) self._uniform_indices = np.array([ i for i, pdf in enumerate(self.pdf) if pdf.dist.name == 'uniform' ], dtype=int) self._non_uniform_indices = np.array([ i for i in range(len(self.pdf)) if i not in self._uniform_indices ], dtype=int) self._non_uniform_logpdf = [ self.pdf[i].logpdf for i in self._non_uniform_indices ] self._upper_limits = self._bounds[:, 1].copy() self._lower_limits = self._bounds[:, 0].copy() self._uniform_logp = -np.sum( np.log(self._upper_limits[self._uniform_indices] - self._lower_limits[self._uniform_indices])) # Set the reference pdf's self.set_reference( {p: v.get("ref") for p, v in sampled_params_info.items()}) # Process the external prior(s): self.external = {} self.external_dependence = set() info_prior = info_prior or {} for name in info_prior: if name == prior_1d_name: raise LoggedError( self.log, "The name '%s' is a reserved prior name. " "Please use a different one.", prior_1d_name) self.log.debug("Loading external prior '%s' from: '%s'", name, info_prior[name]) logp = get_external_function(info_prior[name], name=name) argspec = getfullargspec(logp) known = set(parameterization.input_params()) params = [p for p in argspec.args if p in known] params_without_default = set( argspec.args[:(len(argspec.args) - len(argspec.defaults or []))]) unknown = params_without_default - known if unknown: if unknown.intersection(parameterization.derived_params()): err = ( "External prior '%s' has arguments %s that are output derived " "parameters, Priors must be functions of input parameters. " "Use a separate 'likelihood' for the prior if needed.") else: err = ( "Some of the arguments of the external prior '%s' cannot be " "found and don't have a default value either: %s") raise LoggedError(self.log, err, name, list(unknown)) self.external_dependence.update(params) self.external[name] = ExternalPrior(logp=logp, params=params) self.mpi_warning( "External prior '%s' loaded. " "Mind that it might not be normalized!", name) parameterization.check_dropped(self.external_dependence)