11. Markov Chain, Stationary Distribution
When we have a matrix that represents transition probabilities or a Markov chain, it is often of interest to find the marginal probabilities of the states. Such probabilities are called the stationary distribution
which represents the probabilities of the states in the long run. There are many ways to compute the stationary distribution:
sampling,
Power Method or
matrix decomposition.
The following matrix represents 5 states. We will normalize them so that the matrix represents probabilities and each row sums to 1.
[1]:
import numpy as np
M = np.array([
[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 1],
[0, 1, 1, 0, 1],
[0, 1, 0, 0, 0]
])
M
[1]:
array([[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 1],
[0, 1, 1, 0, 1],
[0, 1, 0, 0, 0]])
Here we detect any row with all zeroes and set all the elements to 1.
[2]:
for r, s in enumerate(M.sum(axis=1)):
if s == 0:
M[r, :] = 1
[3]:
M
[3]:
array([[0, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[0, 1, 0, 0, 1],
[0, 1, 1, 0, 1],
[0, 1, 0, 0, 0]])
Finally, we normalize the rows to probabilities in such a way that they sum to 1.
[4]:
M = np.array([M[r,:] * s for r, s in enumerate(1 / M.sum(axis=1))])
[5]:
M
[5]:
array([[0. , 0.25 , 0.25 , 0.25 , 0.25 ],
[0.2 , 0.2 , 0.2 , 0.2 , 0.2 ],
[0. , 0.5 , 0. , 0. , 0.5 ],
[0. , 0.33333333, 0.33333333, 0. , 0.33333333],
[0. , 1. , 0. , 0. , 0. ]])
11.1. Sampling
Sampling is easy.
Start with a random state.
Loop
Increment the state (signal that we have seen the state)
Sample uniformly \(p\) from \([0, 1)\) and pick the next state
[6]:
from random import choice
import bisect
np.random.seed(37)
indexes = {r: [c for c in range(M.shape[1]) if M[r][c] > 0] for r in range(M.shape[0])}
cumsum = {r: M[r][c].cumsum() for r, c in indexes.items()}
c = np.zeros(M.shape[0]) + 1e-100
r = choice(range(M.shape[0]))
for it in range(30_000):
prev = c / c.sum()
c[r] += 1
curr = c / c.sum()
d = np.linalg.norm(prev - curr, 1)
if d < 0.001:
print(f'num of iterations: {it}')
break
p = np.random.random()
i = bisect.bisect_left(cumsum[r], p)
r = indexes[r][i]
c = c / c.sum()
c
num of iterations: 1151
[6]:
array([0.09548611, 0.42534722, 0.14670139, 0.11024306, 0.22222222])
11.2. Power Method
Using power iteration, we can also find the stationary distribution.
[7]:
X = M.dot(M)
for it in range(10):
Y = X.dot(X)
x = np.diag(X)
y = np.diag(Y)
d = np.linalg.norm(x - y, 1)
if d < 0.001:
print(f'num of iterations: {it}')
break
X = Y
np.diag(X)
num of iterations: 3
[7]:
array([0.08759149, 0.43795601, 0.1459856 , 0.10948932, 0.2189781 ])
11.3. Numpy, eig
We can also use eigen decomposition from Numpy. The stationary probability will be the normalized eigenvector associated with the eigenvalue closest to 1. Note that we have to transpose \(M\).
[8]:
S, U = np.linalg.eig(M.T)
[9]:
S.real
[9]:
array([ 1. , -0.19142147, -0.19142147, -0.20857853, -0.20857853])
[10]:
U.real
[10]:
array([[-0.16531686, -0.16178269, -0.16178269, -0.6152745 , -0.6152745 ],
[-0.8265843 , 0.7453579 , 0.7453579 , 0.6968425 , 0.6968425 ],
[-0.2755281 , -0.2841635 , -0.2841635 , 0.09145666, 0.09145666],
[-0.20664607, -0.28528506, -0.28528506, 0.0053999 , 0.0053999 ],
[-0.41329215, -0.01412664, -0.01412664, -0.17842455, -0.17842455]])
[11]:
(U[:,np.isclose(S, 1)][:,0] / U[:,np.isclose(S, 1)][:,0].sum()).real
[11]:
array([0.08759124, 0.4379562 , 0.1459854 , 0.10948905, 0.2189781 ])
11.4. Scipy, eig
Eigen decomposition from Scipy works the same way as Numpy.
[12]:
from scipy.linalg import eig
S, U = eig(M.T)
[13]:
S.real
[13]:
array([ 1. , -0.19142147, -0.19142147, -0.20857853, -0.20857853])
[14]:
U.real
[14]:
array([[-0.16531686, -0.16178269, -0.16178269, -0.6152745 , -0.6152745 ],
[-0.8265843 , 0.7453579 , 0.7453579 , 0.6968425 , 0.6968425 ],
[-0.2755281 , -0.2841635 , -0.2841635 , 0.09145666, 0.09145666],
[-0.20664607, -0.28528506, -0.28528506, 0.0053999 , 0.0053999 ],
[-0.41329215, -0.01412664, -0.01412664, -0.17842455, -0.17842455]])
[15]:
(U[:,np.isclose(S, 1)][:,0] / U[:,np.isclose(S, 1)][:,0].sum()).real
[15]:
array([0.08759124, 0.4379562 , 0.1459854 , 0.10948905, 0.2189781 ])