Beispiel #1
0
    def probability(self,
                    condition,
                    given_condition=None,
                    evaluate=True,
                    **kwargs):
        """
        Handles probability queries for discrete Markov chains.

        Parameters
        ==========

        condition: Relational
        given_condition: Relational/And

        Returns
        =======

        Probability
            If the transition probabilities are not available
        Expr
            If the transition probabilities is MatrixSymbol or Matrix

        Note
        ====

        Any information passed at the time of query overrides
        any information passed at the time of object creation like
        transition probabilities, state space.

        Pass the transition matrix using TransitionMatrixOf and state space
        using StochasticStateSpaceOf in given_condition using & or And.
        """

        check, trans_probs, state_space, given_condition = \
            self._preprocess(given_condition, evaluate)

        if check:
            return Probability(condition, given_condition)

        if isinstance(condition, Eq) and \
            isinstance(given_condition, Eq) and \
            len(given_condition.atoms(RandomSymbol)) == 1:
            # handles simple queries like P(Eq(X[i], dest_state), Eq(X[i], init_state))
            lhsc, rhsc = condition.lhs, condition.rhs
            lhsg, rhsg = given_condition.lhs, given_condition.rhs
            if not isinstance(lhsc, RandomIndexedSymbol):
                lhsc, rhsc = (rhsc, lhsc)
            if not isinstance(lhsg, RandomIndexedSymbol):
                lhsg, rhsg = (rhsg, lhsg)
            keyc, statec, keyg, stateg = (lhsc.key, rhsc, lhsg.key, rhsg)
            if Lt(stateg, trans_probs.shape[0]) == False or Lt(
                    statec, trans_probs.shape[1]) == False:
                raise IndexError(
                    "No information is available for (%s, %s) in "
                    "transition probabilities of shape, (%s, %s). "
                    "State space is zero indexed." %
                    (stateg, statec, trans_probs.shape[0],
                     trans_probs.shape[1]))
            if keyc < keyg:
                raise ValueError(
                    "Incorrect given condition is given, probability "
                    "of past state cannot be computed from future state.")
            nsteptp = trans_probs**(keyc - keyg)
            if hasattr(nsteptp, "__getitem__"):
                return nsteptp.__getitem__((stateg, statec))
            return Indexed(nsteptp, stateg, statec)

        info = TransitionMatrixOf(self, trans_probs) & StochasticStateSpaceOf(
            self, state_space)
        new_gc = given_condition & info

        if isinstance(condition, And):
            # handle queries like,
            # P(Eq(X[i+k], s1) & Eq(X[i+m], s2) . . . & Eq(X[i], sn), Eq(P(Eq(X[i], si)), prob))
            conds = condition.args
            idx2state = dict()
            for cond in conds:
                idx, state = (cond.lhs, cond.rhs) if isinstance(cond.lhs, RandomIndexedSymbol) else \
                                (cond.rhs, cond.lhs)
                idx2state[idx] = cond if idx2state.get(idx, None) is None else \
                                           idx2state[idx] & cond
            if any(
                    len(Intersection(idx2state[idx].as_set(), state_space)) !=
                    1 for idx in idx2state):
                return S.Zero  # a RandomIndexedSymbol cannot go to different states simultaneously
            i, result = -1, 1
            conds = And.fromiter(
                Intersection(idx2state[idx].as_set(),
                             state_space).as_relational(idx)
                for idx in idx2state)
            if not isinstance(conds, And):
                return self.probability(conds, new_gc)
            conds = conds.args
            while i > -len(conds):
                result *= self.probability(conds[i], conds[i - 1] & info)
                i -= 1
            if isinstance(given_condition,
                          (TransitionMatrixOf, StochasticStateSpaceOf)):
                return result * Probability(conds[i])
            if isinstance(given_condition, And):
                idx_sym = conds[i].atoms(RandomIndexedSymbol)
                prob, count = S(0), 0
                for gc in given_condition.args:
                    if gc.atoms(RandomIndexedSymbol) == idx_sym:
                        prob += gc.rhs if isinstance(gc.lhs,
                                                     Probability) else gc.lhs
                        count += 1
                if isinstance(state_space, FiniteSet) and \
                    count == len(state_space) - 1:
                    given_condition = Eq(Probability(conds[i]), S(1) - prob)
            if isinstance(given_condition, Eq):
                if not isinstance(given_condition.lhs, Probability) or \
                    given_condition.lhs.args[0] != conds[i]:
                    raise ValueError("Probability for %s needed", conds[i])
                return result * given_condition.rhs

        if isinstance(condition, Or):
            conds, prob_sum = condition.args, S(0)
            idx2state = dict()
            for cond in conds:
                idx, state = (cond.lhs, cond.rhs) if isinstance(cond.lhs, RandomIndexedSymbol) else \
                                (cond.rhs, cond.lhs)
                idx2state[idx] = cond if idx2state.get(idx, None) is None else \
                                           idx2state[idx] | cond
            conds = Or.fromiter(
                Intersection(idx2state[idx].as_set(),
                             state_space).as_relational(idx)
                for idx in idx2state)
            if not isinstance(conds, Or):
                return self.probability(conds, new_gc)
            return sum([self.probability(cond, new_gc) for cond in conds.args])

        if isinstance(condition, Ne):
            prob = self.probability(Not(condition), new_gc)
            return S(1) - prob

        raise NotImplementedError(
            "Mechanism for handling (%s, %s) queries hasn't been "
            "implemented yet." % (condition, given_condition))