Ejemplo n.º 1
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))
Ejemplo n.º 2
0
    def __init__(self,
                 info_params: Union[ParamsDict, ExpandedParamsDict],
                 allow_renames=True,
                 ignore_unused_sampled=False):
        self.set_logger()
        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 = {}
        self._input: ParamValuesDict = {}
        self._input_funcs = {}
        self._input_args = {}
        self._input_dependencies: Dict[str, Set[str]] = {}
        self._dropped: Set[str] = set()
        self._output: ParamValuesDict = {}
        self._constant: ParamValuesDict = {}
        self._sampled: ParamValuesDict = {}
        self._sampled_renames: Dict[str, List[str]] = {}
        self._derived: ParamValuesDict = {}
        self._derived_inputs = []
        self._derived_funcs = {}
        self._derived_args = {}
        self._derived_dependencies: Dict[str, Set[str]] = {}
        # Notice here that expand_info_param *always* adds a "derived":True tag
        # to infos without "prior" or "value", and a "value" field
        # to fixed params
        for p, info in info_params.items():
            if isinstance(info, Mapping) and not set(info).issubset(partags):
                raise LoggedError(self.log,
                                  "Parameter '%s' has unknown options %s", p,
                                  set(info).difference(partags))
            info = expand_info_param(info)
            self._infos[p] = info
            if is_fixed_or_function_param(info):
                if isinstance(info["value"], Real):
                    self._constant[p] = float(info["value"])
                    self._input[p] = self._constant[p]
                    if info.get("drop"):
                        self._dropped.add(p)
                else:
                    self._input[p] = np.nan
                    self._input_funcs[p] = get_external_function(info["value"])
                    self._input_args[p] = getfullargspec(
                        self._input_funcs[p]).args
            if is_sampled_param(info):
                self._sampled[p] = np.nan
                self._input[p] = np.nan
                if info.get("drop"):
                    self._dropped.add(p)
                self._sampled_renames[p] = str_to_list(
                    info.get("renames") or [])
            if is_derived_param(info):
                self._derived[p] = np.nan
                # Dynamical parameters whose value we want to save
                if info["derived"] is True and is_fixed_or_function_param(
                        info):
                    # parameters that are already known or computed by input funcs
                    self._derived_inputs.append(p)
                elif info["derived"] is True:
                    self._output[p] = np.nan
                else:
                    self._derived_funcs[p] = get_external_function(
                        info["derived"])
                    self._derived_args[p] = getfullargspec(
                        self._derived_funcs[p]).args
        # Check that the sampled and derived params are all valid python variable names
        for p in chain(self._sampled, self._derived):
            if not is_valid_variable_name(p):
                is_in = p in self._sampled
                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)

        # input params depend on input and sampled only,
        # never on output/derived unless constant
        known_input = set(self._input)
        all_input_arguments = set(chain(*self._input_args.values()))
        bad_input_dependencies = all_input_arguments - known_input
        if bad_input_dependencies:
            raise LoggedError(
                self.log,
                "Input parameters defined as functions can only depend on other "
                "input parameters. In particular, an input parameter cannot depend on %r."
                " Use an explicit Theory calculator for more complex dependencies.\n"
                "If you intended to define a derived output parameter use derived: "
                "instead of value:", list(bad_input_dependencies))

        # Assume that the *un*known function arguments are likelihood/theory
        # output parameters
        for arg in (all_input_arguments.union(*self._derived_args.values()).
                    difference(known_input).difference(self._derived)):
            self._output[arg] = np.nan

        # Useful set: directly "output-ed" derived
        self._directly_output = [p for p in self._derived if p in self._output]

        self._wrapped_input_funcs, self._wrapped_derived_funcs = \
            self._get_wrapped_functions_evaluation_order()

        # Useful mapping: input params that vary if each sample is varied
        self._sampled_input_dependence = {
            s: [
                i for i in self._input
                if s in self._input_dependencies.get(i, {})
            ]
            for s in self._sampled
        }
        # From here on, some error control.
        # Only actually raise error after checking if used by prior.
        if not ignore_unused_sampled:
            self._dropped_not_directly_used = self._dropped.intersection(
                p for p, v in self._sampled_input_dependence.items() if not v)
        else:
            self._dropped_not_directly_used = set()

        # warn if repeated labels
        labels_inv_repeated = invert_dict(self.labels())
        labels_inv_repeated = {
            k: v
            for k, v in labels_inv_repeated.items() if len(v) > 1
        }
        if labels_inv_repeated:
            self.log.warning("There are repeated parameter labels: %r",
                             labels_inv_repeated)