764 lines
38 KiB
Python
764 lines
38 KiB
Python
|
from sympy.concrete.summations import Sum
|
||
|
from sympy.core.containers import Tuple
|
||
|
from sympy.core.function import Lambda
|
||
|
from sympy.core.numbers import (Float, Rational, oo, pi)
|
||
|
from sympy.core.relational import (Eq, Ge, Gt, Le, Lt, Ne)
|
||
|
from sympy.core.singleton import S
|
||
|
from sympy.core.symbol import (Symbol, symbols)
|
||
|
from sympy.functions.combinatorial.factorials import factorial
|
||
|
from sympy.functions.elementary.exponential import exp
|
||
|
from sympy.functions.elementary.integers import ceiling
|
||
|
from sympy.functions.elementary.miscellaneous import sqrt
|
||
|
from sympy.functions.elementary.piecewise import Piecewise
|
||
|
from sympy.functions.special.error_functions import erf
|
||
|
from sympy.functions.special.gamma_functions import (gamma, lowergamma)
|
||
|
from sympy.logic.boolalg import (And, Not)
|
||
|
from sympy.matrices.dense import Matrix
|
||
|
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
||
|
from sympy.matrices.immutable import ImmutableMatrix
|
||
|
from sympy.sets.contains import Contains
|
||
|
from sympy.sets.fancysets import Range
|
||
|
from sympy.sets.sets import (FiniteSet, Interval)
|
||
|
from sympy.stats import (DiscreteMarkovChain, P, TransitionMatrixOf, E,
|
||
|
StochasticStateSpaceOf, variance, ContinuousMarkovChain,
|
||
|
BernoulliProcess, PoissonProcess, WienerProcess,
|
||
|
GammaProcess, sample_stochastic_process)
|
||
|
from sympy.stats.joint_rv import JointDistribution
|
||
|
from sympy.stats.joint_rv_types import JointDistributionHandmade
|
||
|
from sympy.stats.rv import RandomIndexedSymbol
|
||
|
from sympy.stats.symbolic_probability import Probability, Expectation
|
||
|
from sympy.testing.pytest import (raises, skip, ignore_warnings,
|
||
|
warns_deprecated_sympy)
|
||
|
from sympy.external import import_module
|
||
|
from sympy.stats.frv_types import BernoulliDistribution
|
||
|
from sympy.stats.drv_types import PoissonDistribution
|
||
|
from sympy.stats.crv_types import NormalDistribution, GammaDistribution
|
||
|
from sympy.core.symbol import Str
|
||
|
|
||
|
|
||
|
def test_DiscreteMarkovChain():
|
||
|
|
||
|
# pass only the name
|
||
|
X = DiscreteMarkovChain("X")
|
||
|
assert isinstance(X.state_space, Range)
|
||
|
assert X.index_set == S.Naturals0
|
||
|
assert isinstance(X.transition_probabilities, MatrixSymbol)
|
||
|
t = symbols('t', positive=True, integer=True)
|
||
|
assert isinstance(X[t], RandomIndexedSymbol)
|
||
|
assert E(X[0]) == Expectation(X[0])
|
||
|
raises(TypeError, lambda: DiscreteMarkovChain(1))
|
||
|
raises(NotImplementedError, lambda: X(t))
|
||
|
raises(NotImplementedError, lambda: X.communication_classes())
|
||
|
raises(NotImplementedError, lambda: X.canonical_form())
|
||
|
raises(NotImplementedError, lambda: X.decompose())
|
||
|
|
||
|
nz = Symbol('n', integer=True)
|
||
|
TZ = MatrixSymbol('M', nz, nz)
|
||
|
SZ = Range(nz)
|
||
|
YZ = DiscreteMarkovChain('Y', SZ, TZ)
|
||
|
assert P(Eq(YZ[2], 1), Eq(YZ[1], 0)) == TZ[0, 1]
|
||
|
|
||
|
raises(ValueError, lambda: sample_stochastic_process(t))
|
||
|
raises(ValueError, lambda: next(sample_stochastic_process(X)))
|
||
|
# pass name and state_space
|
||
|
# any hashable object should be a valid state
|
||
|
# states should be valid as a tuple/set/list/Tuple/Range
|
||
|
sym, rainy, cloudy, sunny = symbols('a Rainy Cloudy Sunny', real=True)
|
||
|
state_spaces = [(1, 2, 3), [Str('Hello'), sym, DiscreteMarkovChain("Y", (1,2,3))],
|
||
|
Tuple(S(1), exp(sym), Str('World'), sympify=False), Range(-1, 5, 2),
|
||
|
[rainy, cloudy, sunny]]
|
||
|
chains = [DiscreteMarkovChain("Y", state_space) for state_space in state_spaces]
|
||
|
|
||
|
for i, Y in enumerate(chains):
|
||
|
assert isinstance(Y.transition_probabilities, MatrixSymbol)
|
||
|
assert Y.state_space == state_spaces[i] or Y.state_space == FiniteSet(*state_spaces[i])
|
||
|
assert Y.number_of_states == 3
|
||
|
|
||
|
with ignore_warnings(UserWarning): # TODO: Restore tests once warnings are removed
|
||
|
assert P(Eq(Y[2], 1), Eq(Y[0], 2), evaluate=False) == Probability(Eq(Y[2], 1), Eq(Y[0], 2))
|
||
|
assert E(Y[0]) == Expectation(Y[0])
|
||
|
|
||
|
raises(ValueError, lambda: next(sample_stochastic_process(Y)))
|
||
|
|
||
|
raises(TypeError, lambda: DiscreteMarkovChain("Y", {1: 1}))
|
||
|
Y = DiscreteMarkovChain("Y", Range(1, t, 2))
|
||
|
assert Y.number_of_states == ceiling((t-1)/2)
|
||
|
|
||
|
# pass name and transition_probabilities
|
||
|
chains = [DiscreteMarkovChain("Y", trans_probs=Matrix([[]])),
|
||
|
DiscreteMarkovChain("Y", trans_probs=Matrix([[0, 1], [1, 0]])),
|
||
|
DiscreteMarkovChain("Y", trans_probs=Matrix([[pi, 1-pi], [sym, 1-sym]]))]
|
||
|
for Z in chains:
|
||
|
assert Z.number_of_states == Z.transition_probabilities.shape[0]
|
||
|
assert isinstance(Z.transition_probabilities, ImmutableMatrix)
|
||
|
|
||
|
# pass name, state_space and transition_probabilities
|
||
|
T = Matrix([[0.5, 0.2, 0.3],[0.2, 0.5, 0.3],[0.2, 0.3, 0.5]])
|
||
|
TS = MatrixSymbol('T', 3, 3)
|
||
|
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||
|
YS = DiscreteMarkovChain("Y", ['One', 'Two', 3], TS)
|
||
|
assert Y.joint_distribution(1, Y[2], 3) == JointDistribution(Y[1], Y[2], Y[3])
|
||
|
raises(ValueError, lambda: Y.joint_distribution(Y[1].symbol, Y[2].symbol))
|
||
|
assert P(Eq(Y[3], 2), Eq(Y[1], 1)).round(2) == Float(0.36, 2)
|
||
|
assert (P(Eq(YS[3], 2), Eq(YS[1], 1)) -
|
||
|
(TS[0, 2]*TS[1, 0] + TS[1, 1]*TS[1, 2] + TS[1, 2]*TS[2, 2])).simplify() == 0
|
||
|
assert P(Eq(YS[1], 1), Eq(YS[2], 2)) == Probability(Eq(YS[1], 1))
|
||
|
assert P(Eq(YS[3], 3), Eq(YS[1], 1)) == TS[0, 2]*TS[1, 0] + TS[1, 1]*TS[1, 2] + TS[1, 2]*TS[2, 2]
|
||
|
TO = Matrix([[0.25, 0.75, 0],[0, 0.25, 0.75],[0.75, 0, 0.25]])
|
||
|
assert P(Eq(Y[3], 2), Eq(Y[1], 1) & TransitionMatrixOf(Y, TO)).round(3) == Float(0.375, 3)
|
||
|
with ignore_warnings(UserWarning): ### TODO: Restore tests once warnings are removed
|
||
|
assert E(Y[3], evaluate=False) == Expectation(Y[3])
|
||
|
assert E(Y[3], Eq(Y[2], 1)).round(2) == Float(1.1, 3)
|
||
|
TSO = MatrixSymbol('T', 4, 4)
|
||
|
raises(ValueError, lambda: str(P(Eq(YS[3], 2), Eq(YS[1], 1) & TransitionMatrixOf(YS, TSO))))
|
||
|
raises(TypeError, lambda: DiscreteMarkovChain("Z", [0, 1, 2], symbols('M')))
|
||
|
raises(ValueError, lambda: DiscreteMarkovChain("Z", [0, 1, 2], MatrixSymbol('T', 3, 4)))
|
||
|
raises(ValueError, lambda: E(Y[3], Eq(Y[2], 6)))
|
||
|
raises(ValueError, lambda: E(Y[2], Eq(Y[3], 1)))
|
||
|
|
||
|
|
||
|
# extended tests for probability queries
|
||
|
TO1 = Matrix([[Rational(1, 4), Rational(3, 4), 0],[Rational(1, 3), Rational(1, 3), Rational(1, 3)],[0, Rational(1, 4), Rational(3, 4)]])
|
||
|
assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)),
|
||
|
Eq(Probability(Eq(Y[0], 0)), Rational(1, 4)) & TransitionMatrixOf(Y, TO1)) == Rational(1, 16)
|
||
|
assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)), TransitionMatrixOf(Y, TO1)) == \
|
||
|
Probability(Eq(Y[0], 0))/4
|
||
|
assert P(Lt(X[1], 2) & Gt(X[1], 0), Eq(X[0], 2) &
|
||
|
StochasticStateSpaceOf(X, [0, 1, 2]) & TransitionMatrixOf(X, TO1)) == Rational(1, 4)
|
||
|
assert P(Lt(X[1], 2) & Gt(X[1], 0), Eq(X[0], 2) &
|
||
|
StochasticStateSpaceOf(X, [S(0), '0', 1]) & TransitionMatrixOf(X, TO1)) == Rational(1, 4)
|
||
|
assert P(Ne(X[1], 2) & Ne(X[1], 1), Eq(X[0], 2) &
|
||
|
StochasticStateSpaceOf(X, [0, 1, 2]) & TransitionMatrixOf(X, TO1)) is S.Zero
|
||
|
assert P(Ne(X[1], 2) & Ne(X[1], 1), Eq(X[0], 2) &
|
||
|
StochasticStateSpaceOf(X, [S(0), '0', 1]) & TransitionMatrixOf(X, TO1)) is S.Zero
|
||
|
assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)), Eq(Y[1], 1)) == 0.1*Probability(Eq(Y[0], 0))
|
||
|
|
||
|
# testing properties of Markov chain
|
||
|
TO2 = Matrix([[S.One, 0, 0],[Rational(1, 3), Rational(1, 3), Rational(1, 3)],[0, Rational(1, 4), Rational(3, 4)]])
|
||
|
TO3 = Matrix([[Rational(1, 4), Rational(3, 4), 0],[Rational(1, 3), Rational(1, 3), Rational(1, 3)], [0, Rational(1, 4), Rational(3, 4)]])
|
||
|
Y2 = DiscreteMarkovChain('Y', trans_probs=TO2)
|
||
|
Y3 = DiscreteMarkovChain('Y', trans_probs=TO3)
|
||
|
assert Y3.fundamental_matrix() == ImmutableMatrix([[176, 81, -132], [36, 141, -52], [-44, -39, 208]])/125
|
||
|
assert Y2.is_absorbing_chain() == True
|
||
|
assert Y3.is_absorbing_chain() == False
|
||
|
assert Y2.canonical_form() == ([0, 1, 2], TO2)
|
||
|
assert Y3.canonical_form() == ([0, 1, 2], TO3)
|
||
|
assert Y2.decompose() == ([0, 1, 2], TO2[0:1, 0:1], TO2[1:3, 0:1], TO2[1:3, 1:3])
|
||
|
assert Y3.decompose() == ([0, 1, 2], TO3, Matrix(0, 3, []), Matrix(0, 0, []))
|
||
|
TO4 = Matrix([[Rational(1, 5), Rational(2, 5), Rational(2, 5)], [Rational(1, 10), S.Half, Rational(2, 5)], [Rational(3, 5), Rational(3, 10), Rational(1, 10)]])
|
||
|
Y4 = DiscreteMarkovChain('Y', trans_probs=TO4)
|
||
|
w = ImmutableMatrix([[Rational(11, 39), Rational(16, 39), Rational(4, 13)]])
|
||
|
assert Y4.limiting_distribution == w
|
||
|
assert Y4.is_regular() == True
|
||
|
assert Y4.is_ergodic() == True
|
||
|
TS1 = MatrixSymbol('T', 3, 3)
|
||
|
Y5 = DiscreteMarkovChain('Y', trans_probs=TS1)
|
||
|
assert Y5.limiting_distribution(w, TO4).doit() == True
|
||
|
assert Y5.stationary_distribution(condition_set=True).subs(TS1, TO4).contains(w).doit() == S.true
|
||
|
TO6 = Matrix([[S.One, 0, 0, 0, 0],[S.Half, 0, S.Half, 0, 0],[0, S.Half, 0, S.Half, 0], [0, 0, S.Half, 0, S.Half], [0, 0, 0, 0, 1]])
|
||
|
Y6 = DiscreteMarkovChain('Y', trans_probs=TO6)
|
||
|
assert Y6.fundamental_matrix() == ImmutableMatrix([[Rational(3, 2), S.One, S.Half], [S.One, S(2), S.One], [S.Half, S.One, Rational(3, 2)]])
|
||
|
assert Y6.absorbing_probabilities() == ImmutableMatrix([[Rational(3, 4), Rational(1, 4)], [S.Half, S.Half], [Rational(1, 4), Rational(3, 4)]])
|
||
|
with warns_deprecated_sympy():
|
||
|
Y6.absorbing_probabilites()
|
||
|
TO7 = Matrix([[Rational(1, 2), Rational(1, 4), Rational(1, 4)], [Rational(1, 2), 0, Rational(1, 2)], [Rational(1, 4), Rational(1, 4), Rational(1, 2)]])
|
||
|
Y7 = DiscreteMarkovChain('Y', trans_probs=TO7)
|
||
|
assert Y7.is_absorbing_chain() == False
|
||
|
assert Y7.fundamental_matrix() == ImmutableMatrix([[Rational(86, 75), Rational(1, 25), Rational(-14, 75)],
|
||
|
[Rational(2, 25), Rational(21, 25), Rational(2, 25)],
|
||
|
[Rational(-14, 75), Rational(1, 25), Rational(86, 75)]])
|
||
|
|
||
|
# test for zero-sized matrix functionality
|
||
|
X = DiscreteMarkovChain('X', trans_probs=Matrix([[]]))
|
||
|
assert X.number_of_states == 0
|
||
|
assert X.stationary_distribution() == Matrix([[]])
|
||
|
assert X.communication_classes() == []
|
||
|
assert X.canonical_form() == ([], Matrix([[]]))
|
||
|
assert X.decompose() == ([], Matrix([[]]), Matrix([[]]), Matrix([[]]))
|
||
|
assert X.is_regular() == False
|
||
|
assert X.is_ergodic() == False
|
||
|
|
||
|
# test communication_class
|
||
|
# see https://drive.google.com/drive/folders/1HbxLlwwn2b3U8Lj7eb_ASIUb5vYaNIjg?usp=sharing
|
||
|
# tutorial 2.pdf
|
||
|
TO7 = Matrix([[0, 5, 5, 0, 0],
|
||
|
[0, 0, 0, 10, 0],
|
||
|
[5, 0, 5, 0, 0],
|
||
|
[0, 10, 0, 0, 0],
|
||
|
[0, 3, 0, 3, 4]])/10
|
||
|
Y7 = DiscreteMarkovChain('Y', trans_probs=TO7)
|
||
|
tuples = Y7.communication_classes()
|
||
|
classes, recurrence, periods = list(zip(*tuples))
|
||
|
assert classes == ([1, 3], [0, 2], [4])
|
||
|
assert recurrence == (True, False, False)
|
||
|
assert periods == (2, 1, 1)
|
||
|
|
||
|
TO8 = Matrix([[0, 0, 0, 10, 0, 0],
|
||
|
[5, 0, 5, 0, 0, 0],
|
||
|
[0, 4, 0, 0, 0, 6],
|
||
|
[10, 0, 0, 0, 0, 0],
|
||
|
[0, 10, 0, 0, 0, 0],
|
||
|
[0, 0, 0, 5, 5, 0]])/10
|
||
|
Y8 = DiscreteMarkovChain('Y', trans_probs=TO8)
|
||
|
tuples = Y8.communication_classes()
|
||
|
classes, recurrence, periods = list(zip(*tuples))
|
||
|
assert classes == ([0, 3], [1, 2, 5, 4])
|
||
|
assert recurrence == (True, False)
|
||
|
assert periods == (2, 2)
|
||
|
|
||
|
TO9 = Matrix([[2, 0, 0, 3, 0, 0, 3, 2, 0, 0],
|
||
|
[0, 10, 0, 0, 0, 0, 0, 0, 0, 0],
|
||
|
[0, 2, 2, 0, 0, 0, 0, 0, 3, 3],
|
||
|
[0, 0, 0, 3, 0, 0, 6, 1, 0, 0],
|
||
|
[0, 0, 0, 0, 5, 5, 0, 0, 0, 0],
|
||
|
[0, 0, 0, 0, 0, 10, 0, 0, 0, 0],
|
||
|
[4, 0, 0, 5, 0, 0, 1, 0, 0, 0],
|
||
|
[2, 0, 0, 4, 0, 0, 2, 2, 0, 0],
|
||
|
[3, 0, 1, 0, 0, 0, 0, 0, 4, 2],
|
||
|
[0, 0, 4, 0, 0, 0, 0, 0, 3, 3]])/10
|
||
|
Y9 = DiscreteMarkovChain('Y', trans_probs=TO9)
|
||
|
tuples = Y9.communication_classes()
|
||
|
classes, recurrence, periods = list(zip(*tuples))
|
||
|
assert classes == ([0, 3, 6, 7], [1], [2, 8, 9], [5], [4])
|
||
|
assert recurrence == (True, True, False, True, False)
|
||
|
assert periods == (1, 1, 1, 1, 1)
|
||
|
|
||
|
# test canonical form
|
||
|
# see https://web.archive.org/web/20201230182007/https://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/Chapter11.pdf
|
||
|
# example 11.13
|
||
|
T = Matrix([[1, 0, 0, 0, 0],
|
||
|
[S(1) / 2, 0, S(1) / 2, 0, 0],
|
||
|
[0, S(1) / 2, 0, S(1) / 2, 0],
|
||
|
[0, 0, S(1) / 2, 0, S(1) / 2],
|
||
|
[0, 0, 0, 0, S(1)]])
|
||
|
DW = DiscreteMarkovChain('DW', [0, 1, 2, 3, 4], T)
|
||
|
states, A, B, C = DW.decompose()
|
||
|
assert states == [0, 4, 1, 2, 3]
|
||
|
assert A == Matrix([[1, 0], [0, 1]])
|
||
|
assert B == Matrix([[S(1)/2, 0], [0, 0], [0, S(1)/2]])
|
||
|
assert C == Matrix([[0, S(1)/2, 0], [S(1)/2, 0, S(1)/2], [0, S(1)/2, 0]])
|
||
|
states, new_matrix = DW.canonical_form()
|
||
|
assert states == [0, 4, 1, 2, 3]
|
||
|
assert new_matrix == Matrix([[1, 0, 0, 0, 0],
|
||
|
[0, 1, 0, 0, 0],
|
||
|
[S(1)/2, 0, 0, S(1)/2, 0],
|
||
|
[0, 0, S(1)/2, 0, S(1)/2],
|
||
|
[0, S(1)/2, 0, S(1)/2, 0]])
|
||
|
|
||
|
# test regular and ergodic
|
||
|
# https://web.archive.org/web/20201230182007/https://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/Chapter11.pdf
|
||
|
T = Matrix([[0, 4, 0, 0, 0],
|
||
|
[1, 0, 3, 0, 0],
|
||
|
[0, 2, 0, 2, 0],
|
||
|
[0, 0, 3, 0, 1],
|
||
|
[0, 0, 0, 4, 0]])/4
|
||
|
X = DiscreteMarkovChain('X', trans_probs=T)
|
||
|
assert not X.is_regular()
|
||
|
assert X.is_ergodic()
|
||
|
T = Matrix([[0, 1], [1, 0]])
|
||
|
X = DiscreteMarkovChain('X', trans_probs=T)
|
||
|
assert not X.is_regular()
|
||
|
assert X.is_ergodic()
|
||
|
# http://www.math.wisc.edu/~valko/courses/331/MC2.pdf
|
||
|
T = Matrix([[2, 1, 1],
|
||
|
[2, 0, 2],
|
||
|
[1, 1, 2]])/4
|
||
|
X = DiscreteMarkovChain('X', trans_probs=T)
|
||
|
assert X.is_regular()
|
||
|
assert X.is_ergodic()
|
||
|
# https://docs.ufpr.br/~lucambio/CE222/1S2014/Kemeny-Snell1976.pdf
|
||
|
T = Matrix([[1, 1], [1, 1]])/2
|
||
|
X = DiscreteMarkovChain('X', trans_probs=T)
|
||
|
assert X.is_regular()
|
||
|
assert X.is_ergodic()
|
||
|
|
||
|
# test is_absorbing_chain
|
||
|
T = Matrix([[0, 1, 0],
|
||
|
[1, 0, 0],
|
||
|
[0, 0, 1]])
|
||
|
X = DiscreteMarkovChain('X', trans_probs=T)
|
||
|
assert not X.is_absorbing_chain()
|
||
|
# https://en.wikipedia.org/wiki/Absorbing_Markov_chain
|
||
|
T = Matrix([[1, 1, 0, 0],
|
||
|
[0, 1, 1, 0],
|
||
|
[1, 0, 0, 1],
|
||
|
[0, 0, 0, 2]])/2
|
||
|
X = DiscreteMarkovChain('X', trans_probs=T)
|
||
|
assert X.is_absorbing_chain()
|
||
|
T = Matrix([[2, 0, 0, 0, 0],
|
||
|
[1, 0, 1, 0, 0],
|
||
|
[0, 1, 0, 1, 0],
|
||
|
[0, 0, 1, 0, 1],
|
||
|
[0, 0, 0, 0, 2]])/2
|
||
|
X = DiscreteMarkovChain('X', trans_probs=T)
|
||
|
assert X.is_absorbing_chain()
|
||
|
|
||
|
# test custom state space
|
||
|
Y10 = DiscreteMarkovChain('Y', [1, 2, 3], TO2)
|
||
|
tuples = Y10.communication_classes()
|
||
|
classes, recurrence, periods = list(zip(*tuples))
|
||
|
assert classes == ([1], [2, 3])
|
||
|
assert recurrence == (True, False)
|
||
|
assert periods == (1, 1)
|
||
|
assert Y10.canonical_form() == ([1, 2, 3], TO2)
|
||
|
assert Y10.decompose() == ([1, 2, 3], TO2[0:1, 0:1], TO2[1:3, 0:1], TO2[1:3, 1:3])
|
||
|
|
||
|
# testing miscellaneous queries
|
||
|
T = Matrix([[S.Half, Rational(1, 4), Rational(1, 4)],
|
||
|
[Rational(1, 3), 0, Rational(2, 3)],
|
||
|
[S.Half, S.Half, 0]])
|
||
|
X = DiscreteMarkovChain('X', [0, 1, 2], T)
|
||
|
assert P(Eq(X[1], 2) & Eq(X[2], 1) & Eq(X[3], 0),
|
||
|
Eq(P(Eq(X[1], 0)), Rational(1, 4)) & Eq(P(Eq(X[1], 1)), Rational(1, 4))) == Rational(1, 12)
|
||
|
assert P(Eq(X[2], 1) | Eq(X[2], 2), Eq(X[1], 1)) == Rational(2, 3)
|
||
|
assert P(Eq(X[2], 1) & Eq(X[2], 2), Eq(X[1], 1)) is S.Zero
|
||
|
assert P(Ne(X[2], 2), Eq(X[1], 1)) == Rational(1, 3)
|
||
|
assert E(X[1]**2, Eq(X[0], 1)) == Rational(8, 3)
|
||
|
assert variance(X[1], Eq(X[0], 1)) == Rational(8, 9)
|
||
|
raises(ValueError, lambda: E(X[1], Eq(X[2], 1)))
|
||
|
raises(ValueError, lambda: DiscreteMarkovChain('X', [0, 1], T))
|
||
|
|
||
|
# testing miscellaneous queries with different state space
|
||
|
X = DiscreteMarkovChain('X', ['A', 'B', 'C'], T)
|
||
|
assert P(Eq(X[1], 2) & Eq(X[2], 1) & Eq(X[3], 0),
|
||
|
Eq(P(Eq(X[1], 0)), Rational(1, 4)) & Eq(P(Eq(X[1], 1)), Rational(1, 4))) == Rational(1, 12)
|
||
|
assert P(Eq(X[2], 1) | Eq(X[2], 2), Eq(X[1], 1)) == Rational(2, 3)
|
||
|
assert P(Eq(X[2], 1) & Eq(X[2], 2), Eq(X[1], 1)) is S.Zero
|
||
|
assert P(Ne(X[2], 2), Eq(X[1], 1)) == Rational(1, 3)
|
||
|
a = X.state_space.args[0]
|
||
|
c = X.state_space.args[2]
|
||
|
assert (E(X[1] ** 2, Eq(X[0], 1)) - (a**2/3 + 2*c**2/3)).simplify() == 0
|
||
|
assert (variance(X[1], Eq(X[0], 1)) - (2*(-a/3 + c/3)**2/3 + (2*a/3 - 2*c/3)**2/3)).simplify() == 0
|
||
|
raises(ValueError, lambda: E(X[1], Eq(X[2], 1)))
|
||
|
|
||
|
#testing queries with multiple RandomIndexedSymbols
|
||
|
T = Matrix([[Rational(5, 10), Rational(3, 10), Rational(2, 10)], [Rational(2, 10), Rational(7, 10), Rational(1, 10)], [Rational(3, 10), Rational(3, 10), Rational(4, 10)]])
|
||
|
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||
|
assert P(Eq(Y[7], Y[5]), Eq(Y[2], 0)).round(5) == Float(0.44428, 5)
|
||
|
assert P(Gt(Y[3], Y[1]), Eq(Y[0], 0)).round(2) == Float(0.36, 2)
|
||
|
assert P(Le(Y[5], Y[10]), Eq(Y[4], 2)).round(6) == Float(0.583120, 6)
|
||
|
assert Float(P(Eq(Y[10], Y[5]), Eq(Y[4], 1)), 14) == Float(1 - P(Ne(Y[10], Y[5]), Eq(Y[4], 1)), 14)
|
||
|
assert Float(P(Gt(Y[8], Y[9]), Eq(Y[3], 2)), 14) == Float(1 - P(Le(Y[8], Y[9]), Eq(Y[3], 2)), 14)
|
||
|
assert Float(P(Lt(Y[1], Y[4]), Eq(Y[0], 0)), 14) == Float(1 - P(Ge(Y[1], Y[4]), Eq(Y[0], 0)), 14)
|
||
|
assert P(Eq(Y[5], Y[10]), Eq(Y[2], 1)) == P(Eq(Y[10], Y[5]), Eq(Y[2], 1))
|
||
|
assert P(Gt(Y[1], Y[2]), Eq(Y[0], 1)) == P(Lt(Y[2], Y[1]), Eq(Y[0], 1))
|
||
|
assert P(Ge(Y[7], Y[6]), Eq(Y[4], 1)) == P(Le(Y[6], Y[7]), Eq(Y[4], 1))
|
||
|
|
||
|
#test symbolic queries
|
||
|
a, b, c, d = symbols('a b c d')
|
||
|
T = Matrix([[Rational(1, 10), Rational(4, 10), Rational(5, 10)], [Rational(3, 10), Rational(4, 10), Rational(3, 10)], [Rational(7, 10), Rational(2, 10), Rational(1, 10)]])
|
||
|
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||
|
query = P(Eq(Y[a], b), Eq(Y[c], d))
|
||
|
assert query.subs({a:10, b:2, c:5, d:1}).evalf().round(4) == P(Eq(Y[10], 2), Eq(Y[5], 1)).round(4)
|
||
|
assert query.subs({a:15, b:0, c:10, d:1}).evalf().round(4) == P(Eq(Y[15], 0), Eq(Y[10], 1)).round(4)
|
||
|
query_gt = P(Gt(Y[a], b), Eq(Y[c], d))
|
||
|
query_le = P(Le(Y[a], b), Eq(Y[c], d))
|
||
|
assert query_gt.subs({a:5, b:2, c:1, d:0}).evalf() + query_le.subs({a:5, b:2, c:1, d:0}).evalf() == 1.0
|
||
|
query_ge = P(Ge(Y[a], b), Eq(Y[c], d))
|
||
|
query_lt = P(Lt(Y[a], b), Eq(Y[c], d))
|
||
|
assert query_ge.subs({a:4, b:1, c:0, d:2}).evalf() + query_lt.subs({a:4, b:1, c:0, d:2}).evalf() == 1.0
|
||
|
|
||
|
#test issue 20078
|
||
|
assert (2*Y[1] + 3*Y[1]).simplify() == 5*Y[1]
|
||
|
assert (2*Y[1] - 3*Y[1]).simplify() == -Y[1]
|
||
|
assert (2*(0.25*Y[1])).simplify() == 0.5*Y[1]
|
||
|
assert ((2*Y[1]) * (0.25*Y[1])).simplify() == 0.5*Y[1]**2
|
||
|
assert (Y[1]**2 + Y[1]**3).simplify() == (Y[1] + 1)*Y[1]**2
|
||
|
|
||
|
def test_sample_stochastic_process():
|
||
|
if not import_module('scipy'):
|
||
|
skip('SciPy Not installed. Skip sampling tests')
|
||
|
import random
|
||
|
random.seed(0)
|
||
|
numpy = import_module('numpy')
|
||
|
if numpy:
|
||
|
numpy.random.seed(0) # scipy uses numpy to sample so to set its seed
|
||
|
T = Matrix([[0.5, 0.2, 0.3],[0.2, 0.5, 0.3],[0.2, 0.3, 0.5]])
|
||
|
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||
|
for samps in range(10):
|
||
|
assert next(sample_stochastic_process(Y)) in Y.state_space
|
||
|
Z = DiscreteMarkovChain("Z", ['1', 1, 0], T)
|
||
|
for samps in range(10):
|
||
|
assert next(sample_stochastic_process(Z)) in Z.state_space
|
||
|
|
||
|
T = Matrix([[S.Half, Rational(1, 4), Rational(1, 4)],
|
||
|
[Rational(1, 3), 0, Rational(2, 3)],
|
||
|
[S.Half, S.Half, 0]])
|
||
|
X = DiscreteMarkovChain('X', [0, 1, 2], T)
|
||
|
for samps in range(10):
|
||
|
assert next(sample_stochastic_process(X)) in X.state_space
|
||
|
W = DiscreteMarkovChain('W', [1, pi, oo], T)
|
||
|
for samps in range(10):
|
||
|
assert next(sample_stochastic_process(W)) in W.state_space
|
||
|
|
||
|
|
||
|
def test_ContinuousMarkovChain():
|
||
|
T1 = Matrix([[S(-2), S(2), S.Zero],
|
||
|
[S.Zero, S.NegativeOne, S.One],
|
||
|
[Rational(3, 2), Rational(3, 2), S(-3)]])
|
||
|
C1 = ContinuousMarkovChain('C', [0, 1, 2], T1)
|
||
|
assert C1.limiting_distribution() == ImmutableMatrix([[Rational(3, 19), Rational(12, 19), Rational(4, 19)]])
|
||
|
|
||
|
T2 = Matrix([[-S.One, S.One, S.Zero], [S.One, -S.One, S.Zero], [S.Zero, S.One, -S.One]])
|
||
|
C2 = ContinuousMarkovChain('C', [0, 1, 2], T2)
|
||
|
A, t = C2.generator_matrix, symbols('t', positive=True)
|
||
|
assert C2.transition_probabilities(A)(t) == Matrix([[S.Half + exp(-2*t)/2, S.Half - exp(-2*t)/2, 0],
|
||
|
[S.Half - exp(-2*t)/2, S.Half + exp(-2*t)/2, 0],
|
||
|
[S.Half - exp(-t) + exp(-2*t)/2, S.Half - exp(-2*t)/2, exp(-t)]])
|
||
|
with ignore_warnings(UserWarning): ### TODO: Restore tests once warnings are removed
|
||
|
assert P(Eq(C2(1), 1), Eq(C2(0), 1), evaluate=False) == Probability(Eq(C2(1), 1), Eq(C2(0), 1))
|
||
|
assert P(Eq(C2(1), 1), Eq(C2(0), 1)) == exp(-2)/2 + S.Half
|
||
|
assert P(Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 1),
|
||
|
Eq(P(Eq(C2(1), 0)), S.Half)) == (Rational(1, 4) - exp(-2)/4)*(exp(-2)/2 + S.Half)
|
||
|
assert P(Not(Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 2)) |
|
||
|
(Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 2)),
|
||
|
Eq(P(Eq(C2(1), 0)), Rational(1, 4)) & Eq(P(Eq(C2(1), 1)), Rational(1, 4))) is S.One
|
||
|
assert E(C2(Rational(3, 2)), Eq(C2(0), 2)) == -exp(-3)/2 + 2*exp(Rational(-3, 2)) + S.Half
|
||
|
assert variance(C2(Rational(3, 2)), Eq(C2(0), 1)) == ((S.Half - exp(-3)/2)**2*(exp(-3)/2 + S.Half)
|
||
|
+ (Rational(-1, 2) - exp(-3)/2)**2*(S.Half - exp(-3)/2))
|
||
|
raises(KeyError, lambda: P(Eq(C2(1), 0), Eq(P(Eq(C2(1), 1)), S.Half)))
|
||
|
assert P(Eq(C2(1), 0), Eq(P(Eq(C2(5), 1)), S.Half)) == Probability(Eq(C2(1), 0))
|
||
|
TS1 = MatrixSymbol('G', 3, 3)
|
||
|
CS1 = ContinuousMarkovChain('C', [0, 1, 2], TS1)
|
||
|
A = CS1.generator_matrix
|
||
|
assert CS1.transition_probabilities(A)(t) == exp(t*A)
|
||
|
|
||
|
C3 = ContinuousMarkovChain('C', [Symbol('0'), Symbol('1'), Symbol('2')], T2)
|
||
|
assert P(Eq(C3(1), 1), Eq(C3(0), 1)) == exp(-2)/2 + S.Half
|
||
|
assert P(Eq(C3(1), Symbol('1')), Eq(C3(0), Symbol('1'))) == exp(-2)/2 + S.Half
|
||
|
|
||
|
#test probability queries
|
||
|
G = Matrix([[-S(1), Rational(1, 10), Rational(9, 10)], [Rational(2, 5), -S(1), Rational(3, 5)], [Rational(1, 2), Rational(1, 2), -S(1)]])
|
||
|
C = ContinuousMarkovChain('C', state_space=[0, 1, 2], gen_mat=G)
|
||
|
assert P(Eq(C(7.385), C(3.19)), Eq(C(0.862), 0)).round(5) == Float(0.35469, 5)
|
||
|
assert P(Gt(C(98.715), C(19.807)), Eq(C(11.314), 2)).round(5) == Float(0.32452, 5)
|
||
|
assert P(Le(C(5.9), C(10.112)), Eq(C(4), 1)).round(6) == Float(0.675214, 6)
|
||
|
assert Float(P(Eq(C(7.32), C(2.91)), Eq(C(2.63), 1)), 14) == Float(1 - P(Ne(C(7.32), C(2.91)), Eq(C(2.63), 1)), 14)
|
||
|
assert Float(P(Gt(C(3.36), C(1.101)), Eq(C(0.8), 2)), 14) == Float(1 - P(Le(C(3.36), C(1.101)), Eq(C(0.8), 2)), 14)
|
||
|
assert Float(P(Lt(C(4.9), C(2.79)), Eq(C(1.61), 0)), 14) == Float(1 - P(Ge(C(4.9), C(2.79)), Eq(C(1.61), 0)), 14)
|
||
|
assert P(Eq(C(5.243), C(10.912)), Eq(C(2.174), 1)) == P(Eq(C(10.912), C(5.243)), Eq(C(2.174), 1))
|
||
|
assert P(Gt(C(2.344), C(9.9)), Eq(C(1.102), 1)) == P(Lt(C(9.9), C(2.344)), Eq(C(1.102), 1))
|
||
|
assert P(Ge(C(7.87), C(1.008)), Eq(C(0.153), 1)) == P(Le(C(1.008), C(7.87)), Eq(C(0.153), 1))
|
||
|
|
||
|
#test symbolic queries
|
||
|
a, b, c, d = symbols('a b c d')
|
||
|
query = P(Eq(C(a), b), Eq(C(c), d))
|
||
|
assert query.subs({a:3.65, b:2, c:1.78, d:1}).evalf().round(10) == P(Eq(C(3.65), 2), Eq(C(1.78), 1)).round(10)
|
||
|
query_gt = P(Gt(C(a), b), Eq(C(c), d))
|
||
|
query_le = P(Le(C(a), b), Eq(C(c), d))
|
||
|
assert query_gt.subs({a:13.2, b:0, c:3.29, d:2}).evalf() + query_le.subs({a:13.2, b:0, c:3.29, d:2}).evalf() == 1.0
|
||
|
query_ge = P(Ge(C(a), b), Eq(C(c), d))
|
||
|
query_lt = P(Lt(C(a), b), Eq(C(c), d))
|
||
|
assert query_ge.subs({a:7.43, b:1, c:1.45, d:0}).evalf() + query_lt.subs({a:7.43, b:1, c:1.45, d:0}).evalf() == 1.0
|
||
|
|
||
|
#test issue 20078
|
||
|
assert (2*C(1) + 3*C(1)).simplify() == 5*C(1)
|
||
|
assert (2*C(1) - 3*C(1)).simplify() == -C(1)
|
||
|
assert (2*(0.25*C(1))).simplify() == 0.5*C(1)
|
||
|
assert (2*C(1) * 0.25*C(1)).simplify() == 0.5*C(1)**2
|
||
|
assert (C(1)**2 + C(1)**3).simplify() == (C(1) + 1)*C(1)**2
|
||
|
|
||
|
def test_BernoulliProcess():
|
||
|
|
||
|
B = BernoulliProcess("B", p=0.6, success=1, failure=0)
|
||
|
assert B.state_space == FiniteSet(0, 1)
|
||
|
assert B.index_set == S.Naturals0
|
||
|
assert B.success == 1
|
||
|
assert B.failure == 0
|
||
|
|
||
|
X = BernoulliProcess("X", p=Rational(1,3), success='H', failure='T')
|
||
|
assert X.state_space == FiniteSet('H', 'T')
|
||
|
H, T = symbols("H,T")
|
||
|
assert E(X[1]+X[2]*X[3]) == H**2/9 + 4*H*T/9 + H/3 + 4*T**2/9 + 2*T/3
|
||
|
|
||
|
t, x = symbols('t, x', positive=True, integer=True)
|
||
|
assert isinstance(B[t], RandomIndexedSymbol)
|
||
|
|
||
|
raises(ValueError, lambda: BernoulliProcess("X", p=1.1, success=1, failure=0))
|
||
|
raises(NotImplementedError, lambda: B(t))
|
||
|
|
||
|
raises(IndexError, lambda: B[-3])
|
||
|
assert B.joint_distribution(B[3], B[9]) == JointDistributionHandmade(Lambda((B[3], B[9]),
|
||
|
Piecewise((0.6, Eq(B[3], 1)), (0.4, Eq(B[3], 0)), (0, True))
|
||
|
*Piecewise((0.6, Eq(B[9], 1)), (0.4, Eq(B[9], 0)), (0, True))))
|
||
|
|
||
|
assert B.joint_distribution(2, B[4]) == JointDistributionHandmade(Lambda((B[2], B[4]),
|
||
|
Piecewise((0.6, Eq(B[2], 1)), (0.4, Eq(B[2], 0)), (0, True))
|
||
|
*Piecewise((0.6, Eq(B[4], 1)), (0.4, Eq(B[4], 0)), (0, True))))
|
||
|
|
||
|
# Test for the sum distribution of Bernoulli Process RVs
|
||
|
Y = B[1] + B[2] + B[3]
|
||
|
assert P(Eq(Y, 0)).round(2) == Float(0.06, 1)
|
||
|
assert P(Eq(Y, 2)).round(2) == Float(0.43, 2)
|
||
|
assert P(Eq(Y, 4)).round(2) == 0
|
||
|
assert P(Gt(Y, 1)).round(2) == Float(0.65, 2)
|
||
|
# Test for independency of each Random Indexed variable
|
||
|
assert P(Eq(B[1], 0) & Eq(B[2], 1) & Eq(B[3], 0) & Eq(B[4], 1)).round(2) == Float(0.06, 1)
|
||
|
|
||
|
assert E(2 * B[1] + B[2]).round(2) == Float(1.80, 3)
|
||
|
assert E(2 * B[1] + B[2] + 5).round(2) == Float(6.80, 3)
|
||
|
assert E(B[2] * B[4] + B[10]).round(2) == Float(0.96, 2)
|
||
|
assert E(B[2] > 0, Eq(B[1],1) & Eq(B[2],1)).round(2) == Float(0.60,2)
|
||
|
assert E(B[1]) == 0.6
|
||
|
assert P(B[1] > 0).round(2) == Float(0.60, 2)
|
||
|
assert P(B[1] < 1).round(2) == Float(0.40, 2)
|
||
|
assert P(B[1] > 0, B[2] <= 1).round(2) == Float(0.60, 2)
|
||
|
assert P(B[12] * B[5] > 0).round(2) == Float(0.36, 2)
|
||
|
assert P(B[12] * B[5] > 0, B[4] < 1).round(2) == Float(0.36, 2)
|
||
|
assert P(Eq(B[2], 1), B[2] > 0) == 1.0
|
||
|
assert P(Eq(B[5], 3)) == 0
|
||
|
assert P(Eq(B[1], 1), B[1] < 0) == 0
|
||
|
assert P(B[2] > 0, Eq(B[2], 1)) == 1
|
||
|
assert P(B[2] < 0, Eq(B[2], 1)) == 0
|
||
|
assert P(B[2] > 0, B[2]==7) == 0
|
||
|
assert P(B[5] > 0, B[5]) == BernoulliDistribution(0.6, 0, 1)
|
||
|
raises(ValueError, lambda: P(3))
|
||
|
raises(ValueError, lambda: P(B[3] > 0, 3))
|
||
|
|
||
|
# test issue 19456
|
||
|
expr = Sum(B[t], (t, 0, 4))
|
||
|
expr2 = Sum(B[t], (t, 1, 3))
|
||
|
expr3 = Sum(B[t]**2, (t, 1, 3))
|
||
|
assert expr.doit() == B[0] + B[1] + B[2] + B[3] + B[4]
|
||
|
assert expr2.doit() == Y
|
||
|
assert expr3.doit() == B[1]**2 + B[2]**2 + B[3]**2
|
||
|
assert B[2*t].free_symbols == {B[2*t], t}
|
||
|
assert B[4].free_symbols == {B[4]}
|
||
|
assert B[x*t].free_symbols == {B[x*t], x, t}
|
||
|
|
||
|
#test issue 20078
|
||
|
assert (2*B[t] + 3*B[t]).simplify() == 5*B[t]
|
||
|
assert (2*B[t] - 3*B[t]).simplify() == -B[t]
|
||
|
assert (2*(0.25*B[t])).simplify() == 0.5*B[t]
|
||
|
assert (2*B[t] * 0.25*B[t]).simplify() == 0.5*B[t]**2
|
||
|
assert (B[t]**2 + B[t]**3).simplify() == (B[t] + 1)*B[t]**2
|
||
|
|
||
|
def test_PoissonProcess():
|
||
|
X = PoissonProcess("X", 3)
|
||
|
assert X.state_space == S.Naturals0
|
||
|
assert X.index_set == Interval(0, oo)
|
||
|
assert X.lamda == 3
|
||
|
|
||
|
t, d, x, y = symbols('t d x y', positive=True)
|
||
|
assert isinstance(X(t), RandomIndexedSymbol)
|
||
|
assert X.distribution(t) == PoissonDistribution(3*t)
|
||
|
with warns_deprecated_sympy():
|
||
|
X.distribution(X(t))
|
||
|
raises(ValueError, lambda: PoissonProcess("X", -1))
|
||
|
raises(NotImplementedError, lambda: X[t])
|
||
|
raises(IndexError, lambda: X(-5))
|
||
|
|
||
|
assert X.joint_distribution(X(2), X(3)) == JointDistributionHandmade(Lambda((X(2), X(3)),
|
||
|
6**X(2)*9**X(3)*exp(-15)/(factorial(X(2))*factorial(X(3)))))
|
||
|
|
||
|
assert X.joint_distribution(4, 6) == JointDistributionHandmade(Lambda((X(4), X(6)),
|
||
|
12**X(4)*18**X(6)*exp(-30)/(factorial(X(4))*factorial(X(6)))))
|
||
|
|
||
|
assert P(X(t) < 1) == exp(-3*t)
|
||
|
assert P(Eq(X(t), 0), Contains(t, Interval.Lopen(3, 5))) == exp(-6) # exp(-2*lamda)
|
||
|
res = P(Eq(X(t), 1), Contains(t, Interval.Lopen(3, 4)))
|
||
|
assert res == 3*exp(-3)
|
||
|
|
||
|
# Equivalent to P(Eq(X(t), 1))**4 because of non-overlapping intervals
|
||
|
assert P(Eq(X(t), 1) & Eq(X(d), 1) & Eq(X(x), 1) & Eq(X(y), 1), Contains(t, Interval.Lopen(0, 1))
|
||
|
& Contains(d, Interval.Lopen(1, 2)) & Contains(x, Interval.Lopen(2, 3))
|
||
|
& Contains(y, Interval.Lopen(3, 4))) == res**4
|
||
|
|
||
|
# Return Probability because of overlapping intervals
|
||
|
assert P(Eq(X(t), 2) & Eq(X(d), 3), Contains(t, Interval.Lopen(0, 2))
|
||
|
& Contains(d, Interval.Ropen(2, 4))) == \
|
||
|
Probability(Eq(X(d), 3) & Eq(X(t), 2), Contains(t, Interval.Lopen(0, 2))
|
||
|
& Contains(d, Interval.Ropen(2, 4)))
|
||
|
|
||
|
raises(ValueError, lambda: P(Eq(X(t), 2) & Eq(X(d), 3),
|
||
|
Contains(t, Interval.Lopen(0, 4)) & Contains(d, Interval.Lopen(3, oo)))) # no bound on d
|
||
|
assert P(Eq(X(3), 2)) == 81*exp(-9)/2
|
||
|
assert P(Eq(X(t), 2), Contains(t, Interval.Lopen(0, 5))) == 225*exp(-15)/2
|
||
|
|
||
|
# Check that probability works correctly by adding it to 1
|
||
|
res1 = P(X(t) <= 3, Contains(t, Interval.Lopen(0, 5)))
|
||
|
res2 = P(X(t) > 3, Contains(t, Interval.Lopen(0, 5)))
|
||
|
assert res1 == 691*exp(-15)
|
||
|
assert (res1 + res2).simplify() == 1
|
||
|
|
||
|
# Check Not and Or
|
||
|
assert P(Not(Eq(X(t), 2) & (X(d) > 3)), Contains(t, Interval.Ropen(2, 4)) & \
|
||
|
Contains(d, Interval.Lopen(7, 8))).simplify() == -18*exp(-6) + 234*exp(-9) + 1
|
||
|
assert P(Eq(X(t), 2) | Ne(X(t), 4), Contains(t, Interval.Ropen(2, 4))) == 1 - 36*exp(-6)
|
||
|
raises(ValueError, lambda: P(X(t) > 2, X(t) + X(d)))
|
||
|
assert E(X(t)) == 3*t # property of the distribution at a given timestamp
|
||
|
assert E(X(t)**2 + X(d)*2 + X(y)**3, Contains(t, Interval.Lopen(0, 1))
|
||
|
& Contains(d, Interval.Lopen(1, 2)) & Contains(y, Interval.Ropen(3, 4))) == 75
|
||
|
assert E(X(t)**2, Contains(t, Interval.Lopen(0, 1))) == 12
|
||
|
assert E(x*(X(t) + X(d))*(X(t)**2+X(d)**2), Contains(t, Interval.Lopen(0, 1))
|
||
|
& Contains(d, Interval.Ropen(1, 2))) == \
|
||
|
Expectation(x*(X(d) + X(t))*(X(d)**2 + X(t)**2), Contains(t, Interval.Lopen(0, 1))
|
||
|
& Contains(d, Interval.Ropen(1, 2)))
|
||
|
|
||
|
# Value Error because of infinite time bound
|
||
|
raises(ValueError, lambda: E(X(t)**3, Contains(t, Interval.Lopen(1, oo))))
|
||
|
|
||
|
# Equivalent to E(X(t)**2) - E(X(d)**2) == E(X(1)**2) - E(X(1)**2) == 0
|
||
|
assert E((X(t) + X(d))*(X(t) - X(d)), Contains(t, Interval.Lopen(0, 1))
|
||
|
& Contains(d, Interval.Lopen(1, 2))) == 0
|
||
|
assert E(X(2) + x*E(X(5))) == 15*x + 6
|
||
|
assert E(x*X(1) + y) == 3*x + y
|
||
|
assert P(Eq(X(1), 2) & Eq(X(t), 3), Contains(t, Interval.Lopen(1, 2))) == 81*exp(-6)/4
|
||
|
Y = PoissonProcess("Y", 6)
|
||
|
Z = X + Y
|
||
|
assert Z.lamda == X.lamda + Y.lamda == 9
|
||
|
raises(ValueError, lambda: X + 5) # should be added be only PoissonProcess instance
|
||
|
N, M = Z.split(4, 5)
|
||
|
assert N.lamda == 4
|
||
|
assert M.lamda == 5
|
||
|
raises(ValueError, lambda: Z.split(3, 2)) # 2+3 != 9
|
||
|
|
||
|
raises(ValueError, lambda :P(Eq(X(t), 0), Contains(t, Interval.Lopen(1, 3)) & Eq(X(1), 0)))
|
||
|
# check if it handles queries with two random variables in one args
|
||
|
res1 = P(Eq(N(3), N(5)))
|
||
|
assert res1 == P(Eq(N(t), 0), Contains(t, Interval(3, 5)))
|
||
|
res2 = P(N(3) > N(1))
|
||
|
assert res2 == P((N(t) > 0), Contains(t, Interval(1, 3)))
|
||
|
assert P(N(3) < N(1)) == 0 # condition is not possible
|
||
|
res3 = P(N(3) <= N(1)) # holds only for Eq(N(3), N(1))
|
||
|
assert res3 == P(Eq(N(t), 0), Contains(t, Interval(1, 3)))
|
||
|
|
||
|
# tests from https://www.probabilitycourse.com/chapter11/11_1_2_basic_concepts_of_the_poisson_process.php
|
||
|
X = PoissonProcess('X', 10) # 11.1
|
||
|
assert P(Eq(X(S(1)/3), 3) & Eq(X(1), 10)) == exp(-10)*Rational(8000000000, 11160261)
|
||
|
assert P(Eq(X(1), 1), Eq(X(S(1)/3), 3)) == 0
|
||
|
assert P(Eq(X(1), 10), Eq(X(S(1)/3), 3)) == P(Eq(X(S(2)/3), 7))
|
||
|
|
||
|
X = PoissonProcess('X', 2) # 11.2
|
||
|
assert P(X(S(1)/2) < 1) == exp(-1)
|
||
|
assert P(X(3) < 1, Eq(X(1), 0)) == exp(-4)
|
||
|
assert P(Eq(X(4), 3), Eq(X(2), 3)) == exp(-4)
|
||
|
|
||
|
X = PoissonProcess('X', 3)
|
||
|
assert P(Eq(X(2), 5) & Eq(X(1), 2)) == Rational(81, 4)*exp(-6)
|
||
|
|
||
|
# check few properties
|
||
|
assert P(X(2) <= 3, X(1)>=1) == 3*P(Eq(X(1), 0)) + 2*P(Eq(X(1), 1)) + P(Eq(X(1), 2))
|
||
|
assert P(X(2) <= 3, X(1) > 1) == 2*P(Eq(X(1), 0)) + 1*P(Eq(X(1), 1))
|
||
|
assert P(Eq(X(2), 5) & Eq(X(1), 2)) == P(Eq(X(1), 3))*P(Eq(X(1), 2))
|
||
|
assert P(Eq(X(3), 4), Eq(X(1), 3)) == P(Eq(X(2), 1))
|
||
|
|
||
|
#test issue 20078
|
||
|
assert (2*X(t) + 3*X(t)).simplify() == 5*X(t)
|
||
|
assert (2*X(t) - 3*X(t)).simplify() == -X(t)
|
||
|
assert (2*(0.25*X(t))).simplify() == 0.5*X(t)
|
||
|
assert (2*X(t) * 0.25*X(t)).simplify() == 0.5*X(t)**2
|
||
|
assert (X(t)**2 + X(t)**3).simplify() == (X(t) + 1)*X(t)**2
|
||
|
|
||
|
def test_WienerProcess():
|
||
|
X = WienerProcess("X")
|
||
|
assert X.state_space == S.Reals
|
||
|
assert X.index_set == Interval(0, oo)
|
||
|
|
||
|
t, d, x, y = symbols('t d x y', positive=True)
|
||
|
assert isinstance(X(t), RandomIndexedSymbol)
|
||
|
assert X.distribution(t) == NormalDistribution(0, sqrt(t))
|
||
|
with warns_deprecated_sympy():
|
||
|
X.distribution(X(t))
|
||
|
raises(ValueError, lambda: PoissonProcess("X", -1))
|
||
|
raises(NotImplementedError, lambda: X[t])
|
||
|
raises(IndexError, lambda: X(-2))
|
||
|
|
||
|
assert X.joint_distribution(X(2), X(3)) == JointDistributionHandmade(
|
||
|
Lambda((X(2), X(3)), sqrt(6)*exp(-X(2)**2/4)*exp(-X(3)**2/6)/(12*pi)))
|
||
|
assert X.joint_distribution(4, 6) == JointDistributionHandmade(
|
||
|
Lambda((X(4), X(6)), sqrt(6)*exp(-X(4)**2/8)*exp(-X(6)**2/12)/(24*pi)))
|
||
|
|
||
|
assert P(X(t) < 3).simplify() == erf(3*sqrt(2)/(2*sqrt(t)))/2 + S(1)/2
|
||
|
assert P(X(t) > 2, Contains(t, Interval.Lopen(3, 7))).simplify() == S(1)/2 -\
|
||
|
erf(sqrt(2)/2)/2
|
||
|
|
||
|
# Equivalent to P(X(1)>1)**4
|
||
|
assert P((X(t) > 4) & (X(d) > 3) & (X(x) > 2) & (X(y) > 1),
|
||
|
Contains(t, Interval.Lopen(0, 1)) & Contains(d, Interval.Lopen(1, 2))
|
||
|
& Contains(x, Interval.Lopen(2, 3)) & Contains(y, Interval.Lopen(3, 4))).simplify() ==\
|
||
|
(1 - erf(sqrt(2)/2))*(1 - erf(sqrt(2)))*(1 - erf(3*sqrt(2)/2))*(1 - erf(2*sqrt(2)))/16
|
||
|
|
||
|
# Contains an overlapping interval so, return Probability
|
||
|
assert P((X(t)< 2) & (X(d)> 3), Contains(t, Interval.Lopen(0, 2))
|
||
|
& Contains(d, Interval.Ropen(2, 4))) == Probability((X(d) > 3) & (X(t) < 2),
|
||
|
Contains(d, Interval.Ropen(2, 4)) & Contains(t, Interval.Lopen(0, 2)))
|
||
|
|
||
|
assert str(P(Not((X(t) < 5) & (X(d) > 3)), Contains(t, Interval.Ropen(2, 4)) &
|
||
|
Contains(d, Interval.Lopen(7, 8))).simplify()) == \
|
||
|
'-(1 - erf(3*sqrt(2)/2))*(2 - erfc(5/2))/4 + 1'
|
||
|
# Distribution has mean 0 at each timestamp
|
||
|
assert E(X(t)) == 0
|
||
|
assert E(x*(X(t) + X(d))*(X(t)**2+X(d)**2), Contains(t, Interval.Lopen(0, 1))
|
||
|
& Contains(d, Interval.Ropen(1, 2))) == Expectation(x*(X(d) + X(t))*(X(d)**2 + X(t)**2),
|
||
|
Contains(d, Interval.Ropen(1, 2)) & Contains(t, Interval.Lopen(0, 1)))
|
||
|
assert E(X(t) + x*E(X(3))) == 0
|
||
|
|
||
|
#test issue 20078
|
||
|
assert (2*X(t) + 3*X(t)).simplify() == 5*X(t)
|
||
|
assert (2*X(t) - 3*X(t)).simplify() == -X(t)
|
||
|
assert (2*(0.25*X(t))).simplify() == 0.5*X(t)
|
||
|
assert (2*X(t) * 0.25*X(t)).simplify() == 0.5*X(t)**2
|
||
|
assert (X(t)**2 + X(t)**3).simplify() == (X(t) + 1)*X(t)**2
|
||
|
|
||
|
|
||
|
def test_GammaProcess_symbolic():
|
||
|
t, d, x, y, g, l = symbols('t d x y g l', positive=True)
|
||
|
X = GammaProcess("X", l, g)
|
||
|
|
||
|
raises(NotImplementedError, lambda: X[t])
|
||
|
raises(IndexError, lambda: X(-1))
|
||
|
assert isinstance(X(t), RandomIndexedSymbol)
|
||
|
assert X.state_space == Interval(0, oo)
|
||
|
assert X.distribution(t) == GammaDistribution(g*t, 1/l)
|
||
|
with warns_deprecated_sympy():
|
||
|
X.distribution(X(t))
|
||
|
assert X.joint_distribution(5, X(3)) == JointDistributionHandmade(Lambda(
|
||
|
(X(5), X(3)), l**(8*g)*exp(-l*X(3))*exp(-l*X(5))*X(3)**(3*g - 1)*X(5)**(5*g
|
||
|
- 1)/(gamma(3*g)*gamma(5*g))))
|
||
|
# property of the gamma process at any given timestamp
|
||
|
assert E(X(t)) == g*t/l
|
||
|
assert variance(X(t)).simplify() == g*t/l**2
|
||
|
|
||
|
# Equivalent to E(2*X(1)) + E(X(1)**2) + E(X(1)**3), where E(X(1)) == g/l
|
||
|
assert E(X(t)**2 + X(d)*2 + X(y)**3, Contains(t, Interval.Lopen(0, 1))
|
||
|
& Contains(d, Interval.Lopen(1, 2)) & Contains(y, Interval.Ropen(3, 4))) == \
|
||
|
2*g/l + (g**2 + g)/l**2 + (g**3 + 3*g**2 + 2*g)/l**3
|
||
|
|
||
|
assert P(X(t) > 3, Contains(t, Interval.Lopen(3, 4))).simplify() == \
|
||
|
1 - lowergamma(g, 3*l)/gamma(g) # equivalent to P(X(1)>3)
|
||
|
|
||
|
|
||
|
#test issue 20078
|
||
|
assert (2*X(t) + 3*X(t)).simplify() == 5*X(t)
|
||
|
assert (2*X(t) - 3*X(t)).simplify() == -X(t)
|
||
|
assert (2*(0.25*X(t))).simplify() == 0.5*X(t)
|
||
|
assert (2*X(t) * 0.25*X(t)).simplify() == 0.5*X(t)**2
|
||
|
assert (X(t)**2 + X(t)**3).simplify() == (X(t) + 1)*X(t)**2
|
||
|
def test_GammaProcess_numeric():
|
||
|
t, d, x, y = symbols('t d x y', positive=True)
|
||
|
X = GammaProcess("X", 1, 2)
|
||
|
assert X.state_space == Interval(0, oo)
|
||
|
assert X.index_set == Interval(0, oo)
|
||
|
assert X.lamda == 1
|
||
|
assert X.gamma == 2
|
||
|
|
||
|
raises(ValueError, lambda: GammaProcess("X", -1, 2))
|
||
|
raises(ValueError, lambda: GammaProcess("X", 0, -2))
|
||
|
raises(ValueError, lambda: GammaProcess("X", -1, -2))
|
||
|
|
||
|
# all are independent because of non-overlapping intervals
|
||
|
assert P((X(t) > 4) & (X(d) > 3) & (X(x) > 2) & (X(y) > 1), Contains(t,
|
||
|
Interval.Lopen(0, 1)) & Contains(d, Interval.Lopen(1, 2)) & Contains(x,
|
||
|
Interval.Lopen(2, 3)) & Contains(y, Interval.Lopen(3, 4))).simplify() == \
|
||
|
120*exp(-10)
|
||
|
|
||
|
# Check working with Not and Or
|
||
|
assert P(Not((X(t) < 5) & (X(d) > 3)), Contains(t, Interval.Ropen(2, 4)) &
|
||
|
Contains(d, Interval.Lopen(7, 8))).simplify() == -4*exp(-3) + 472*exp(-8)/3 + 1
|
||
|
assert P((X(t) > 2) | (X(t) < 4), Contains(t, Interval.Ropen(1, 4))).simplify() == \
|
||
|
-643*exp(-4)/15 + 109*exp(-2)/15 + 1
|
||
|
|
||
|
assert E(X(t)) == 2*t # E(X(t)) == gamma*t/l
|
||
|
assert E(X(2) + x*E(X(5))) == 10*x + 4
|