# Do imports
import networkx as nx
import matplotlib.pyplot as plt
import scipy
import numpy as np
MCMC Pagerank
# Create a directed graph
= nx.DiGraph()
G
# Add nodes
1, 2, 3, 4, 5, 6])
G.add_nodes_from([
# Add vertices
= [(1, 2), (1, 3), (3, 2), (3, 1), (3, 4), (5, 6), (6, 5),(3,2),(2,4),(4,1)]
edges
G.add_edges_from(edges)
# Draw the graph
=True)
nx.draw_circular(G, with_labels plt.show()
# Since the graph G is a DiGraph (Directed Graph), we can use the adjacency_matrix method from networkx to get the adjacency matrix
# Then, we normalize each row to sum to 1 to get the transition matrix
# Get the adjacency matrix (returns a scipy sparse matrix)
= nx.adjacency_matrix(G)
adj_matrix
# Convert to a dense numpy array
= adj_matrix.toarray()
adj_matrix
# Calculate the sum of each row
= adj_matrix.sum(axis=1)
row_sums
# Normalize the matrix (this is the transition matrix)
= adj_matrix / row_sums[:, np.newaxis]
transition_matrix print(transition_matrix.T)
[[0. 0. 0.33333333 1. 0. 0. ]
[0.5 0. 0.33333333 0. 0. 0. ]
[0.5 0. 0. 0. 0. 0. ]
[0. 1. 0.33333333 0. 0. 0. ]
[0. 0. 0. 0. 0. 1. ]
[0. 0. 0. 0. 1. 0. ]]
If not doing pagerank: Initializing a random stochastic matrix
import numpy as np
# Create a 6x6 matrix with random values
= np.random.rand(6, 6)
matrix
# Normalize the matrix to make it a stochastic matrix
= matrix / matrix.sum(axis=1, keepdims=True)
stochastic_matrix
# Square the matrix to increase the difference between values
= np.square(stochastic_matrix)
matrix_squared
# Normalize the squared matrix to make it a stochastic matrix
= matrix_squared / matrix_squared.sum(axis=1, keepdims=True) stochastic_matrix_squared
Finding the stationary state of the matrix
# Normalize the teleportation matrix
= np.ones(np.shape(transition_matrix))
teleportation = teleportation.sum(axis=1)
row_sums = teleportation / row_sums[:, np.newaxis]
teleportation
= .15
alpha
= (1-alpha)*transition_matrix + alpha*teleportation transition_matrix_combined
# Use the transition matrix from the pagerank
= transition_matrix_combined
stochastic_matrix
# Compute the eigenvalues and right eigenvectors
= np.linalg.eig(np.transpose(stochastic_matrix))
eigenvalues, eigenvectors
# Find the index of the eigenvalue equal to 1
= np.where(np.isclose(eigenvalues, 1))
index
# The corresponding eigenvector is the stationary distribution
= np.real_if_close(eigenvectors[:, index].flatten())
stationary_distribution
# Normalize the stationary distribution so it sums to 1
/= np.sum(stationary_distribution)
stationary_distribution
print(stationary_distribution)
print(index)
[0.2154013 0.14956679 0.11654555 0.18515302 0.16666667 0.16666667]
(array([0]),)
Double-check that the stationary state really is stationary
# Multiply the stationary distribution by the transition matrix
= np.dot(stationary_distribution, stochastic_matrix)
result
# Check if the result is the same as the stationary distribution
= np.allclose(result, stationary_distribution)
is_stationary
print(is_stationary)
True
Doing a MCMC simulation of the system
Simulate the states
# Initialize the state
= np.random.choice(6)
state
# Number of passes
= 1000
num_passes
# Store the states
# Initialize the state variable
= np.zeros(num_passes)
states
for _ in range(num_passes):
# Choose the next state based on the probabilities in the transition matrix
# The column in the transition matrix corresponding to our current state gives the probabilities
= np.random.choice(6, p=stochastic_matrix[state])
state =(state)
states[_]
# Convert the states to a numpy array
= np.array(states) states
Plot the emperical distribution
import matplotlib.pyplot as plt
=6, alpha=0.5)
plt.hist(states, bins'State')
plt.xlabel('Frequency')
plt.ylabel('Histogram of States')
plt.title( plt.show()
Comparing the theoretical and emperical results
# Plotting the histogram of states
=6, density=True, alpha=0.7, label='Empirical')
plt.hist(states, bins
# Plotting the stationary distribution
='Theoretical', marker='o')
plt.plot(stationary_distribution, label
'Comparison of Empirical and Theoretical Distributions')
plt.title('States')
plt.xlabel('Probability')
plt.ylabel(
plt.legend()
plt.show()
Watching the emperical distribution develop over time
import numpy as np
from IPython.display import HTML
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Use stochastic_matrix as the transition matrix
= stochastic_matrix
Q
# Initialize the figure
= plt.subplots()
fig, ax =100;
num_frames# Initialize an empty array for the empirical distribution
= np.zeros((num_frames, len(Q[0])))
empirical_distribution
# Initialize the state variable
= 0
state
# Function to update the empirical distribution
# Modify the update function to run 10 iterations without updating i
def update(i):
global state, empirical_distribution
= empirical_distribution[i-1]
empirical_distribution[i] for _ in range(10): # Run 10 iterations
= np.random.choice(range(len(Q[state])), p=Q[state])
state += 1
empirical_distribution[i, state] # Plot the distribution
ax.clear()range(len(Q[state])), empirical_distribution[i] )
ax.bar(
# Create the animation
= animation.FuncAnimation(fig, update, frames=range(1, num_frames), repeat=False)
ani
# Use the HTML object to display the animation
HTML(ani.to_jshtml())