예제 #1
0
 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)]
예제 #2
0
 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))
예제 #3
0
 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)
예제 #4
0
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."
            )