mpsic_project_2_random_walks/random-walks.ipynb

147 KiB

from sklearn.preprocessing import normalize as skl_normalize
from matplotlib.pylab import show, cm, axis
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import sys
def normalize_cols(target_matrix):
    return skl_normalize(target_matrix, norm="l1", axis=0)

def inflate_matrix(target_matrix, factor):
    return normalize_cols(np.power(target_matrix, factor))

def expand_matrix(target_matrix, factor):
    return np.linalg.matrix_power(target_matrix, factor)
def get_clusters(target_matrix):
    print("Matrix to 'cluster':\n", target_matrix)
    clusters = []
    for i, edge in enumerate(target_matrix): 
        #* Check whether the there is connection between origin node and currently checked one
        if edge[i]:
            #* print(f"For node {i} we have: {edge} → {target_matrix[i,:]}")
            clusters.append(edge)
            
    clust_map = {}
    for cluster_id, cluster in enumerate(clusters): #* loop over clusters list and retrieve connected nodes ids 
        cluster_nodes = [ i for i, node in enumerate(cluster) if node ]
        #* print(f"For cluster {cluster_id} we have: {cluster} \n\t→\tnodes: {cluster_nodes}")
        clust_map[cluster_id] = cluster_nodes
    return clust_map

def stop_condition(matrix, iteration):
    #* print("Stop matrix: ", matrix)
    if iteration % 5 == 4:
        m = np.max( matrix**2 - matrix) - np.min( matrix**2 - matrix)
        #* print("m = ", m)
        if m == 0:
            print("Stop at iteration %s" % iteration)
            return True
    return False


def markov_chain(M, max_loop = 10):
    expand_factor = 2
    inflate_factor = 2
    diag_factor = 1
    
    M = M + diag_factor * np.identity(M.shape[0])   #* add self-loops to the matrix by setting diagonal 1
    #* print("My M after diagonalization", M)
    M = normalize_cols(M)
    
    for i in range(max_loop):
        M = expand_matrix(M, expand_factor)         #* expand by raising the matrix to the given power
        M = inflate_matrix(M, inflate_factor)       #* inflate the given matrix by raising each element to the given power
        if stop_condition(M, i):                    #* if values 'converged' to ones (1) → stop the markov algorithm
            break

    clusters = get_clusters(M)
    return M, clusters

def networkx_markov_chain(graph, max_loop=75):
    A = np.array(nx.adjacency_matrix(graph).todense())
    return markov_chain(A, max_loop)

def draw_graph(graph, cluster_map, arrows):
    _, _ = plt.subplots(figsize=(15,8))
    graph = nx.Graph(graph)
    clust_map = {v: k for k, vals in cluster_map.items() for v in vals}   
    colors = [clust_map.get(i, 100) for i in range(len(graph.nodes()))]
    nx.draw_networkx(graph, node_color=colors, linewidths=5, arrows=arrows, cmap='gist_rainbow')
    axis("off")
    show(block=False)

def calculate_and_draw(graph, arrows=None): 
    M = nx.to_numpy_matrix(graph)
    print("Nodes number: %s\n" % M.shape[0])
    
    M, clusters = networkx_markov_chain(graph)
    print(f"\nClustering result:")
    for key, value in clusters.items():
        print(key, ": ", value) 
    
    draw_graph(graph, clusters, arrows)

bin_graph = nx.binomial_tree(4) 
calculate_and_draw(bin_graph)
Nodes number: 16

Stop at iteration 14
Matrix to 'cluster':
 [[1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

Clustering result:
0 :  [0, 1]
1 :  [2, 3]
2 :  [4, 5]
3 :  [6, 7]
4 :  [8, 9]
5 :  [10, 12]
6 :  [11, 13]
7 :  [14, 15]
balanced_graph = nx.balanced_tree(2,3) # perfectly balanced 
calculate_and_draw(balanced_graph)
Nodes number: 15

Matrix to 'cluster':
 [[0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.5 0.  1.  0.  0.  0.  1.  1.  0.  0.  0.  0.  0.  0. ]
 [0.  0.5 0.  0.  1.  0.  0.  0.  0.  1.  1.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  1.  0.  0.  0.  0.  0.  1.  1.  0.  0. ]
 [1.  0.  1.  0.  0.  0.  1.  0.  0.  0.  0.  0.  0.  1.  1. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]]

Clustering result:
0 :  [1, 3, 7, 8]
1 :  [1, 4, 9, 10]
2 :  [5, 11, 12]
3 :  [0, 2, 6, 13, 14]
balanced_graph = nx.balanced_tree(2,4) 
calculate_and_draw(balanced_graph)
Nodes number: 31

Stop at iteration 69
Matrix to 'cluster':
 [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.
  1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 1. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 1. 1. 0. 0.]
 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 1. 1.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0.]]

Clustering result:
0 :  [0, 1]
1 :  [2]
2 :  [3, 7, 15, 16]
3 :  [8, 17, 18]
4 :  [9, 19, 20]
5 :  [4, 10, 21, 22]
6 :  [5, 11, 23, 24]
7 :  [12, 25, 26]
8 :  [13, 27, 28]
9 :  [6, 14, 29, 30]
random_tree = nx.random_tree(10) # uniformly random tree 
calculate_and_draw(random_tree)
Nodes number: 10

Stop at iteration 14
Matrix to 'cluster':
 [[1. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 1. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 1. 1. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
 [0. 0. 0. 1. 0. 0. 0. 0. 0. 1.]]

Clustering result:
0 :  [0, 5]
1 :  [1, 2]
2 :  [4, 6, 7]
3 :  [8]
4 :  [3, 9]

Directed graph

gn_graph = nx.gn_graph(10) # growing network digraph 
calculate_and_draw(gn_graph, arrows=True)
Nodes number: 10

Matrix to 'cluster':
 [[0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.        ]
 [0.5        1.         0.         0.         0.         0.
  0.         0.         0.         0.        ]
 [0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.        ]
 [0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.        ]
 [0.         0.         0.33333333 0.         1.         0.
  0.         0.         0.         0.        ]
 [0.         0.         0.33333333 0.         0.         1.
  0.         0.         0.         0.        ]
 [0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.        ]
 [0.5        0.         0.         0.         0.         0.
  0.         1.         0.         0.        ]
 [0.         0.         0.33333333 0.         0.         0.
  0.         0.         1.         0.        ]
 [0.         0.         0.         1.         0.         0.
  1.         0.         0.         1.        ]]

Clustering result:
0 :  [0, 1]
1 :  [2, 4]
2 :  [2, 5]
3 :  [0, 7]
4 :  [2, 8]
5 :  [3, 6, 9]