Exemplo n.º 1
0
    def __getitem__(self, idx):
        if isinstance(idx, slice):
            return self._slice(idx)

        try:
            return self.point(int(idx))
        except (ValueError, TypeError):  # Passed variable or variable name.
            pass

        if isinstance(idx, tuple):
            var, vslice = idx
            burn, thin = vslice.start, vslice.step
            if burn is None:
                burn = 0
            if thin is None:
                thin = 1
        else:
            var = idx
            burn, thin = 0, 1

        var = get_var_name(var)
        if var in self.varnames:
            if var in self.stat_names:
                warnings.warn(
                    "Attribute access on a trace object is ambigous. "
                    "Sampler statistic and model variable share a name. Use "
                    "trace.get_values or trace.get_sampler_stats.")
            return self.get_values(var, burn=burn, thin=thin)
        if var in self.stat_names:
            return self.get_sampler_stats(var, burn=burn, thin=thin)
        raise KeyError("Unknown variable %s" % var)
Exemplo n.º 2
0
    def get_values(self, varname, burn=0, thin=1, combine=True, chains=None, squeeze=True):
        """Get values from traces.

        Parameters
        ----------
        varname: str
        burn: int
        thin: int
        combine: bool
            If True, results from `chains` will be concatenated.
        chains: int or list of ints
            Chains to retrieve. If None, all chains are used. A single
            chain value can also be given.
        squeeze: bool
            Return a single array element if the resulting list of
            values only has one element. If False, the result will
            always be a list of arrays, even if `combine` is True.

        Returns
        -------
        A list of NumPy arrays or a single NumPy array (depending on
        `squeeze`).
        """
        if chains is None:
            chains = self.chains
        varname = get_var_name(varname)
        try:
            results = [self._straces[chain].get_values(varname, burn, thin) for chain in chains]
        except TypeError:  # Single chain passed.
            results = [self._straces[chains].get_values(varname, burn, thin)]
        return _squeeze_cat(results, combine, squeeze)
Exemplo n.º 3
0
 def setup_class(cls):
     super().setup_class()
     cls.model = cls.make_model()
     with cls.model:
         cls.step = cls.make_step()
         cls.trace = pm.sample(cls.n_samples, tune=cls.tune, step=cls.step, cores=cls.chains)
     cls.samples = {}
     for var in cls.model.unobserved_RVs:
         cls.samples[get_var_name(var)] = cls.trace.get_values(var, burn=cls.burn)
Exemplo n.º 4
0
 def __init__(self, vars, shared, blocked=True):
     """
     Parameters
     ----------
     vars: list of sampling variables
     shared: dict of Aesara variable -> shared variable
     blocked: Boolean (default True)
     """
     self.vars = vars
     self.shared = {
         get_var_name(var): shared
         for var, shared in shared.items()
     }
     self.blocked = blocked
Exemplo n.º 5
0
 def _filter_parents(self, var, parents) -> Set[VarName]:
     """Get direct parents of a var, as strings"""
     keep = set()  # type: Set[VarName]
     for p in parents:
         if p == var:
             continue
         elif p.name in self.var_names:
             keep.add(p.name)
         elif p in self.transform_map:
             if self.transform_map[p] != var.name:
                 keep.add(self.transform_map[p])
         else:
             raise AssertionError("Do not know what to do with {}".format(
                 get_var_name(p)))
     return keep
Exemplo n.º 6
0
 def __init__(self, vars, shared, blocked=True):
     """
     Parameters
     ----------
     vars: list of sampling variables
     shared: dict of theano variable -> shared variable
     blocked: Boolean (default True)
     """
     self.vars = vars
     self.ordering = ArrayOrdering(vars)
     self.shared = {
         get_var_name(var): shared
         for var, shared in shared.items()
     }
     self.blocked = blocked
     self.bij = None
Exemplo n.º 7
0
 def setup_class(cls):
     super().setup_class()
     cls.model = cls.make_model()
     with cls.model:
         cls.step = cls.make_step()
         cls.trace = pm.sample(
             cls.n_samples,
             tune=cls.tune,
             step=cls.step,
             cores=cls.chains,
             return_inferencedata=False,
             compute_convergence_checks=False,
         )
     cls.samples = {}
     for var in cls.model.unobserved_RVs:
         cls.samples[get_var_name(var)] = cls.trace.get_values(
             var, burn=cls.burn)
Exemplo n.º 8
0
    def __getattr__(self, name):
        # Avoid infinite recursion when called before __init__
        # variables are set up (e.g., when pickling).
        if name in self._attrs:
            raise AttributeError

        name = get_var_name(name)
        if name in self.varnames:
            if name in self.stat_names:
                warnings.warn(
                    "Attribute access on a trace object is ambigous. "
                    "Sampler statistic and model variable share a name. Use "
                    "trace.get_values or trace.get_sampler_stats.")
            return self.get_values(name)
        if name in self.stat_names:
            return self.get_sampler_stats(name)
        raise AttributeError("'{}' object has no attribute '{}'".format(
            type(self).__name__, name))
Exemplo n.º 9
0
    def draw_values(self) -> list[np.ndarray]:
        vars = self.vars
        trace = self.trace
        samples = self.samples
        # size = self.size
        params = dict(enumerate(vars))

        with _DrawValuesContext() as context:
            self.init()
            self.make_graph()

            drawn = context.drawn_vars

            # Init givens and the stack of nodes to try to `_draw_value` from
            givens = {
                p.name: (p, v)
                for (p, samples), v in drawn.items()
                if getattr(p, "name", None) is not None
            }
            stack = list(
                self.leaf_nodes.values())  # A queue would be more appropriate

            while stack:
                next_ = stack.pop(0)
                if (next_, samples) in drawn:
                    # If the node already has a givens value, skip it
                    continue
                elif isinstance(next_, (Constant, SharedVariable)):
                    # If the node is a aesara.tensor.TensorConstant or a
                    # aesara.tensor.sharedvar.SharedVariable, its value will be
                    # available automatically in _compile_aesara_function so
                    # we can skip it. Furthermore, if this node was treated as a
                    # TensorVariable that should be compiled by aesara in
                    # _compile_aesara_function, it would raise a `TypeError:
                    # ('Constants not allowed in param list', ...)` for
                    # TensorConstant, and a `TypeError: Cannot use a shared
                    # variable (...) as explicit input` for SharedVariable.
                    # ObservedRV and MultiObservedRV instances are ViewOPs
                    # of TensorConstants or SharedVariables, we must add them
                    # to the stack or risk evaluating deterministics with the
                    # wrong values (issue #3354)
                    stack.extend([
                        node for node in self.named_nodes_parents[next_]
                        if isinstance(node, (ObservedRV, MultiObservedRV)) and
                        (node, samples) not in drawn
                    ])
                    continue
                else:
                    # If the node does not have a givens value, try to draw it.
                    # The named node's children givens values must also be taken
                    # into account.
                    children = self.named_nodes_children[next_]
                    temp_givens = [givens[k] for k in givens if k in children]
                    try:
                        # This may fail for autotransformed RVs, which don't
                        # have the random method
                        value = self.draw_value(next_,
                                                trace=trace,
                                                givens=temp_givens)
                        assert isinstance(value, np.ndarray)
                        givens[next_.name] = (next_, value)
                        drawn[(next_, samples)] = value
                    except aesara.graph.fg.MissingInputError:
                        # The node failed, so we must add the node's parents to
                        # the stack of nodes to try to draw from. We exclude the
                        # nodes in the `params` list.
                        stack.extend([
                            node for node in self.named_nodes_parents[next_]
                            if node is not None and (node,
                                                     samples) not in drawn
                        ])

            # the below makes sure the graph is evaluated in order
            # test_distributions_random::TestDrawValues::test_draw_order fails without it
            # The remaining params that must be drawn are all hashable
            to_eval: set[int] = set()
            missing_inputs: set[int] = {j for j, p in self.symbolic_params}

            while to_eval or missing_inputs:
                if to_eval == missing_inputs:
                    raise ValueError("Cannot resolve inputs for {}".format(
                        [get_var_name(trace.varnames[j]) for j in to_eval]))
                to_eval = set(missing_inputs)
                missing_inputs = set()
                for param_idx in to_eval:
                    param = vars[param_idx]
                    drawn = context.drawn_vars
                    if (param, samples) in drawn:
                        self.evaluated[param_idx] = drawn[(param, samples)]
                    else:
                        try:
                            if param in self.named_nodes_children:
                                for node in self.named_nodes_children[param]:
                                    if node.name not in givens and (
                                            node, samples) in drawn:
                                        givens[node.name] = (
                                            node,
                                            drawn[(node, samples)],
                                        )
                            value = self.draw_value(param,
                                                    trace=self.trace,
                                                    givens=givens.values())
                            assert isinstance(value, np.ndarray)
                            self.evaluated[param_idx] = drawn[(
                                param, samples)] = value
                            givens[param.name] = (param, value)
                        except aesara.graph.fg.MissingInputError:
                            missing_inputs.add(param_idx)
        return [self.evaluated[j] for j in params]
Exemplo n.º 10
0
def draw_values(params, point=None, size=None):
    """
    Draw (fix) parameter values. Handles a number of cases:

        1) The parameter is a scalar
        2) The parameter is an RV

            a) parameter can be fixed to the value in the point
            b) parameter can be fixed by sampling from the RV
            c) parameter can be fixed using tag.test_value (last resort)

        3) The parameter is a tensor variable/constant. Can be evaluated using
        theano.function, but a variable may contain nodes which

            a) are named parameters in the point
            b) are RVs with a random method
    """
    # The following check intercepts and redirects calls to
    # draw_values in the context of sample_posterior_predictive
    size = to_tuple(size)
    ppc_sampler = vectorized_ppc.get(None)
    if ppc_sampler is not None:
        # this is being done inside new, vectorized sample_posterior_predictive
        return ppc_sampler(params, trace=point, samples=size)

    if point is None:
        point = {}
    # Get fast drawable values (i.e. things in point or numbers, arrays,
    # constants or shares, or things that were already drawn in related
    # contexts)
    with _DrawValuesContext() as context:
        params = dict(enumerate(params))
        drawn = context.drawn_vars
        evaluated = {}
        symbolic_params = []
        for i, p in params.items():
            # If the param is fast drawable, then draw the value immediately
            if is_fast_drawable(p):
                v = _draw_value(p, point=point, size=size)
                evaluated[i] = v
                continue

            name = getattr(p, "name", None)
            if (p, size) in drawn:
                # param was drawn in related contexts
                v = drawn[(p, size)]
                evaluated[i] = v
            # We filter out Deterministics by checking for `model` attribute
            elif name is not None and hasattr(p, "model") and name in point:
                # param.name is in point
                v = point[name]
                evaluated[i] = drawn[(p, size)] = v
            else:
                # param still needs to be drawn
                symbolic_params.append((i, p))

        if not symbolic_params:
            # We only need to enforce the correct order if there are symbolic
            # params that could be drawn in variable order
            return [evaluated[i] for i in params]

        # Distribution parameters may be nodes which have named node-inputs
        # specified in the point. Need to find the node-inputs, their
        # parents and children to replace them.
        leaf_nodes, named_nodes_descendents, named_nodes_ancestors = build_named_node_tree(
            (param for _, param in symbolic_params if hasattr(param, "name"))
        )

        # Init givens and the stack of nodes to try to `_draw_value` from
        givens = {
            p.name: (p, v) for (p, size), v in drawn.items() if getattr(p, "name", None) is not None
        }
        stack = list(leaf_nodes.values())
        while stack:
            next_ = stack.pop(0)
            if (next_, size) in drawn:
                # If the node already has a givens value, skip it
                continue
            elif isinstance(next_, (theano_constant, tt.sharedvar.SharedVariable)):
                # If the node is a theano.tensor.TensorConstant or a
                # theano.tensor.sharedvar.SharedVariable, its value will be
                # available automatically in _compile_theano_function so
                # we can skip it. Furthermore, if this node was treated as a
                # TensorVariable that should be compiled by theano in
                # _compile_theano_function, it would raise a `TypeError:
                # ('Constants not allowed in param list', ...)` for
                # TensorConstant, and a `TypeError: Cannot use a shared
                # variable (...) as explicit input` for SharedVariable.
                # ObservedRV and MultiObservedRV instances are ViewOPs
                # of TensorConstants or SharedVariables, we must add them
                # to the stack or risk evaluating deterministics with the
                # wrong values (issue #3354)
                stack.extend(
                    [
                        node
                        for node in named_nodes_descendents[next_]
                        if isinstance(node, (ObservedRV, MultiObservedRV))
                        and (node, size) not in drawn
                    ]
                )
                continue
            else:
                # If the node does not have a givens value, try to draw it.
                # The named node's children givens values must also be taken
                # into account.
                children = named_nodes_ancestors[next_]
                temp_givens = [givens[k] for k in givens if k in children]
                try:
                    # This may fail for autotransformed RVs, which don't
                    # have the random method
                    value = _draw_value(next_, point=point, givens=temp_givens, size=size)
                    givens[next_.name] = (next_, value)
                    drawn[(next_, size)] = value
                except theano.gof.fg.MissingInputError:
                    # The node failed, so we must add the node's parents to
                    # the stack of nodes to try to draw from. We exclude the
                    # nodes in the `params` list.
                    stack.extend(
                        [
                            node
                            for node in named_nodes_descendents[next_]
                            if node is not None and (node, size) not in drawn
                        ]
                    )

        # the below makes sure the graph is evaluated in order
        # test_distributions_random::TestDrawValues::test_draw_order fails without it
        # The remaining params that must be drawn are all hashable
        to_eval = set()
        missing_inputs = {j for j, p in symbolic_params}
        while to_eval or missing_inputs:
            if to_eval == missing_inputs:
                raise ValueError(
                    "Cannot resolve inputs for {}".format(
                        [get_var_name(params[j]) for j in to_eval]
                    )
                )
            to_eval = set(missing_inputs)
            missing_inputs = set()
            for param_idx in to_eval:
                param = params[param_idx]
                if (param, size) in drawn:
                    evaluated[param_idx] = drawn[(param, size)]
                else:
                    try:  # might evaluate in a bad order,
                        # Sometimes _draw_value recurrently calls draw_values.
                        # This may set values for certain nodes in the drawn
                        # dictionary, but they don't get added to the givens
                        # dictionary. Here, we try to fix that.
                        if param in named_nodes_ancestors:
                            for node in named_nodes_ancestors[param]:
                                if node.name not in givens and (node, size) in drawn:
                                    givens[node.name] = (node, drawn[(node, size)])
                        value = _draw_value(param, point=point, givens=givens.values(), size=size)
                        evaluated[param_idx] = drawn[(param, size)] = value
                        givens[param.name] = (param, value)
                    except theano.gof.fg.MissingInputError:
                        missing_inputs.add(param_idx)

    return [evaluated[j] for j in params]  # set the order back
Exemplo n.º 11
0
 def flat_t(var):
     x = trace[get_var_name(var)]
     return x.reshape((x.shape[0], np.prod(x.shape[1:], dtype=int)))
Exemplo n.º 12
0
 def __init__(self, var, idx, dpoint):
     self.var = get_var_name(var)
     self.idx = idx
     self.dpt = dpoint