def __init__(self, name, info, parameterization, _theory=None, timing=None): self.name = name self.log = logging.getLogger(self.name) # Load info of the likelihood for k in info: setattr(self, k, info[k]) # Store the external function and its arguments self.external_function = get_external_function(info[_external], name=self.name) argspec = getargspec(self.external_function) self.input_params = odict( [(p, None) for p in argspec.args if p not in ["_derived", "_theory"] and p in parameterization.input_params()]) self.has_derived = "_derived" in argspec.args if self.has_derived: derived_kw_index = argspec.args[-len(argspec.defaults):].index("_derived") self.output_params = argspec.defaults[derived_kw_index] else: self.output_params = [] self.has_theory = "_theory" in argspec.args if self.has_theory: theory_kw_index = argspec.args[-len(argspec.defaults):].index("_theory") self.needs = argspec.defaults[theory_kw_index] # Timing self.timing = timing self.n = 0 self.time_avg = 0 # States, to avoid recomputing self.n_states = 3 self.states = [{"params": None, "logp": None, "derived": None, "last": 0} for _ in range(self.n_states)]
def __init__(self, info_params, allow_renames=True, ignore_unused_sampled=False): self.set_logger(lowercase=True) self.allow_renames = allow_renames # First, we load the parameters, # not caring about whether they are understood by any likelihood. # `input` contains the parameters (expected to be) understood by the likelihood, # with its fixed value, its fixing function, or None if their value is given # directly by the sampler. self._infos = odict() self._input = odict() self._input_funcs = dict() self._input_args = dict() self._output = odict() self._constant = odict() self._sampled = odict() self._sampled_renames = odict() self._derived = odict() self._derived_funcs = dict() self._derived_args = dict() # Notice here that expand_info_param *always* adds a _p_derived:True tag # to infos without _prior or _p_value, and a _p_value field to fixed params for p, info in info_params.items(): self._infos[p] = deepcopy(info) if is_fixed_param(info): if isinstance(info[_p_value], Number): self._constant[p] = info[_p_value] if not info.get(_p_drop, False): self._input[p] = self._constant[p] else: self._input[p] = None self._input_funcs[p] = get_external_function( info[_p_value]) self._input_args[p] = getargspec(self._input_funcs[p]).args if is_sampled_param(info): self._sampled[p] = None if not info.get(_p_drop, False): self._input[p] = None self._sampled_renames[p] = (( lambda x: [x] if isinstance(x, string_types) else x)(info.get( _p_renames, []))) if is_derived_param(info): self._derived[p] = deepcopy(info) # Dynamical parameters whose value we want to save if info[_p_derived] is True and is_fixed_param(info): info[_p_derived] = "lambda %s: %s" % (p, p) if info[_p_derived] is True: self._output[p] = None else: self._derived_funcs[p] = get_external_function( info[_p_derived]) self._derived_args[p] = getargspec( self._derived_funcs[p]).args # Check that the sampled and derived params are all valid python variable names for p in chain(self.sampled_params(), self.derived_params()): if not is_valid_variable_name(p): is_in = p in self.sampled_params() eg_in = " p_prime:\n prior: ...\n %s: 'lambda p_prime: p_prime'\n" % p eg_out = " p_prime: 'lambda %s: %s'\n" % (p, p) raise LoggedError( self.log, "Parameter name '%s' is not a valid Python variable name " "(it needs to start with a letter or '_').\n" "If this is an %s parameter of a likelihood or theory, " "whose name you cannot change,%s define an associated " "%s one with a valid name 'p_prime' as: \n\n%s", p, "input" if is_in else "output", "" if is_in else " remove it and", "sampled" if is_in else "derived", eg_in if is_in else eg_out) # Assume that the *un*known function arguments are likelihood output parameters args = (set(chain(*self._input_args.values())).union( chain(*self._derived_args.values()))) for p in (list(self._constant) + list(self._input) + list(self._sampled) + list(self._derived)): if p in args: args.remove(p) self._output.update({p: None for p in args}) # Useful sets: directly-sampled input parameters and directly "output-ed" derived self._directly_sampled = [p for p in self._input if p in self._sampled] self._directly_output = [p for p in self._derived if p in self._output] # Useful mapping: input params that vary if each sampled is varied self._sampled_input_dependence = odict( [[s, [i for i in self._input if s in self._input_args.get(i, {})]] for s in self._sampled]) # From here on, some error control. dropped_but_never_used = (set([ p for p, v in self._sampled_input_dependence.items() if not v ]).difference(set(self._directly_sampled))) if dropped_but_never_used and not ignore_unused_sampled: raise LoggedError( self.log, "Parameters %r are sampled but not passed to the likelihood or theory " "code, neither ever used as arguments for any parameters. " "Check that you are not using the '%s' tag unintentionally.", list(dropped_but_never_used), _p_drop) # input params depend on input and sampled only, never on output/derived all_input_arguments = set(chain(*self._input_args.values())) bad_input_dependencies = all_input_arguments.difference( set(self.input_params()).union(set(self.sampled_params())).union( set(self.constant_params()))) if bad_input_dependencies: raise LoggedError( self.log, "Input parameters defined as functions can only depend on other " "input parameters that are not defined as functions. " "In particular, an input parameter cannot depend on %r", list(bad_input_dependencies))
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 body_of_test(info_logpdf, kind, tmpdir, derived=False, manual=False): # For pytest's handling of tmp dirs if hasattr(tmpdir, "dirpath"): tmpdir = tmpdir.dirname prefix = os.path.join(tmpdir, "%d" % round(1e8 * random())) + os.sep if os.path.exists(prefix): shutil.rmtree(prefix) # build full info info = { _output_prefix: prefix, _params: { "x": { _prior: { "min": 0, "max": 1 }, "proposal": 0.05 }, "y": { _prior: { "min": -1, "max": 1 }, "proposal": 0.05 } }, _sampler: { "mcmc": { "max_samples": (10 if not manual else 5000), "learn_proposal": False } } } if derived: info[_params].update({ "r": { "min": 0, "max": 1 }, "theta": { "min": -0.5, "max": 0.5 } }) # Complete according to kind if kind == _prior: info.update({_prior: info_logpdf, _likelihood: {"one": None}}) elif kind == _likelihood: info.update({_likelihood: info_logpdf}) else: raise ValueError("Kind of test not known.") # If there is an ext function that is not a string, don't write output! stringy = dict([(k, v) for k, v in info_logpdf.items() if isinstance(v, six.string_types)]) if stringy != info_logpdf: info.pop(_output_prefix) # Run updated_info, products = run(info) # Test values logprior_base = -np.log( (info[_params]["x"][_prior]["max"] - info[_params]["x"][_prior]["min"]) * (info[_params]["y"][_prior]["max"] - info[_params]["y"][_prior]["min"])) logps = dict([(name, logpdf(**dict([(arg, products["sample"][arg].values) for arg in getargspec(logpdf)[0]]))) for name, logpdf in { "half_ring": half_ring_func, "gaussian_y": gaussian_func }.items()]) # Test #1: values of logpdf's if kind == _prior: columns_priors = [ c for c in products["sample"].data.columns if c.startswith("minuslogprior") ] assert np.allclose( products["sample"][columns_priors[0]].values, np.sum(products["sample"][columns_priors[1:]].values, axis=-1)), ( "The single prior values do not add up to the total one.") assert np.allclose( logprior_base + sum(logps[p] for p in info_logpdf), -products["sample"]["minuslogprior"].values), ( "The value of the total prior is not reproduced correctly.") elif kind == _likelihood: for lik in info[_likelihood]: assert np.allclose( -2 * logps[lik], products["sample"][_chi2 + _separator + lik].values ), ("The value of the likelihood '%s' is not reproduced correctly." % lik) assert np.allclose( logprior_base + sum(logps[p] for p in info_logpdf), -products["sample"]["minuslogpost"].values), ( "The value of the posterior is not reproduced correctly.") # Test derived parameters, if present -- for now just for "r" if derived: derived_values = dict([(param, func(**dict([(arg, products["sample"][arg].values) for arg in ["x", "y"]]))) for param, func in derived_funcs.items()]) assert np.all([ np.allclose(v, products["sample"][p].values) for p, v in derived_values.items() ]), ( "The value of the derived parameters is not reproduced correctly.") # Test updated info -- scripted if kind == _prior: assert info[_prior] == updated_info[_prior], ( "The prior information has not been updated correctly.") elif kind == _likelihood: # Transform the likelihood info to the "external" convention and add defaults info_likelihood = deepcopy(info[_likelihood]) for lik, value in list(info_likelihood.items()): if not hasattr(value, "get"): info_likelihood[lik] = {_external: value} info_likelihood[lik].update({ k: v for k, v in class_options.items() if not k in info_likelihood[lik] }) for k in [_input_params, _output_params]: updated_info[_likelihood][lik].pop(k) assert info_likelihood == dict(updated_info[_likelihood]), ( "The likelihood information has not been updated correctly\n %r vs %r" % (info_likelihood, dict(updated_info[_likelihood]))) # Test updated info -- yaml # For now, only if ALL external pdfs are given as strings, since the YAML load fails otherwise if stringy == info_logpdf: full_output_file = os.path.join(prefix, _full_suffix + ".yaml") with open(full_output_file) as full: updated_yaml = yaml_load("".join(full.readlines())) for k, v in stringy.items(): to_test = updated_yaml[kind][k] if kind == _likelihood: to_test = to_test[_external] assert to_test == info_logpdf[k], ( "The updated external pdf info has not been written correctly." )