In [1]:
    
from collections import Counter
from random import randint, random, choices
import numpy as np
from scipy.stats import binom
from matplotlib import pyplot as plt
    
In [2]:
    
def generate_markov_chain(k):
    return np.apply_along_axis(lambda x: np.random.dirichlet(alpha=np.ones(k + 1), size=1).flatten(),
                               axis=1, arr=np.empty([k + 1, k + 1]))
    
In [3]:
    
cases = 20
probabily = 0.2
pi = binom.pmf(np.arange(0, cases + 1),cases, probabily).reshape(cases + 1, 1)
    
In [4]:
    
Q = generate_markov_chain(cases)
A = np.minimum(1, (pi * Q).T / (pi * Q))
    
In [5]:
    
sampled = Counter()
current = randint(0, cases)
sampled[current] += 1
for _ in range(int(10e3)):
    proposed = choices(range(cases + 1), weights=Q[current,:].tolist())[0]
    if random() <= A[current, proposed]:
        current = proposed
    sampled[current] += 1
    
In [6]:
    
pi_simulated = np.empty(cases + 1)
for k, v in sampled.items():
    pi_simulated[k] = v 
pi_simulated = pi_simulated / sum(pi_simulated)
    
In [7]:
    
plt.rcParams["figure.figsize"]= 14, 7
plt.plot(pi_simulated, label = "Simulated")
plt.plot(pi, label = "Exact")
plt.legend()
plt.show()