def RWDiffusion(g, samples=5, epsilon=0.01, max_iterations=100000): """Computes the average number of steps requires by a random walk process to fall below a total variation distance below epsilon (TVD computed between the momentary visitation probabilities \pi^t and the stationary distribution \pi = \pi^{\infty}. This time can be used to measure diffusion speed in a given (weighted and directed) network.""" avg_speed = 0 T = Utilities.RWTransitionMatrix(g) pi = Utilities.StationaryDistribution(T) n = len(g.vs()) for s in range(samples): x = np.zeros(n) seed = np.random.randint(n) x[seed] = 1 while Utilities.TVD(x, pi) > epsilon: avg_speed += 1 # NOTE x * T = (T^T * x^T)^T # NOTE T is already transposed to get the left EV x = (T.dot(x.transpose())).transpose() if avg_speed > max_iterations: Log.add("x[0:10] = " + str(x[0:10])) Log.add("pi[0:10] = " + str(pi[0:10])) raise RuntimeError( "Failed to converge within maximal number of iterations. Start of current x and pi are printed above" ) return avg_speed / samples
def exportDiffusionMovieFrames(g, file_prefix='diffusion', visual_style=None, steps=100, initial_index=-1): """Exports an animation showing the evolution of a diffusion process on the network""" T = Utilities.RWTransitionMatrix(g) if visual_style == None: visual_style = {} visual_style["vertex_color"] = "lightblue" visual_style["vertex_label"] = g.vs["name"] visual_style["edge_curved"] = .5 visual_style["vertex_size"] = 30 # lambda expression for the coloring of nodes according to some quantity p \in [0,1] # p = 1 ==> color red # p = 0 ==> color white color_p = lambda p: "rgb(255," + str(int( (1 - p) * 255)) + "," + str(int((1 - p) * 255)) + ")" # Initial state of random walker if initial_index < 0: initial_index = np.random.randint(0, len(g.vs())) x = np.zeros(len(g.vs())) x[initial_index] = 1 # compute stationary state pi = Utilities.StationaryDistribution(T) scale = np.mean(np.abs(x - pi)) # Plot network (useful as poster frame of video) igraph.plot(g, file_prefix + "_network.pdf", **visual_style) # Create frames for i in range(0, steps): visual_style["vertex_color"] = [color_p(p**0.1) for p in x] igraph.plot(g, file_prefix + "_frame_" + str(i).zfill(5) + ".png", **visual_style) if i % 10 == 0: Log.add('Step' + str(i) + '\tTVD = ' + str(Utilities.TVD(x, pi)), Severity.INFO) # NOTE x * T = (T^T * x^T)^T # NOTE T is already transposed to get the left EV x = (T.dot(x.transpose())).transpose()
def exportDiffusionMovieFramesFirstOrder(t, file_prefix='diffusion', visual_style=None, steps=100, initial_index=-1, model='SECOND', dynamic=False, NWframesPerRWStep=5): """Exports an animation showing the evolution of a diffusion process on the first-order aggregate network, where random walk dynamics either follows a first-order (mode='NULL') or second-order (model='SECOND') Markov model""" assert model == 'SECOND' or model == 'NULL' g1 = t.igraphFirstOrder() if model == 'SECOND': g2 = t.igraphSecondOrder() temporal = tn.TemporalNetwork.ShuffleTwoPaths(t, l=steps) elif model == 'NULL': g2 = t.igraphSecondOrderNull() temporal = tn.TemporalNetwork.ShuffleEdges(t, l=steps) T = Utilities.RWTransitionMatrix(g2) # visual style is for *first-order* aggregate network if visual_style == None: visual_style = {} visual_style["vertex_color"] = "lightblue" visual_style["vertex_label"] = g1.vs["name"] visual_style["edge_curved"] = .5 visual_style["vertex_size"] = 30 visual_style["edge_color"] = ["darkgrey"] * g1.ecount() visual_style["edge_width"] = [.5] * g1.ecount() # Initial state of random walker if initial_index < 0: initial_index = np.random.randint(0, len(g2.vs())) exp = 1.0 / .75 x = np.zeros(len(g2.vs())) x[initial_index] = 1 # This index allows to quickly map node names to indices in the first-order network map_name_to_id = {} for i in range(len(g1.vs())): map_name_to_id[g1.vs()['name'][i]] = i # Index to quickly map second-order node indices to first-order node indices map_2_to_1 = {} for j in range(len(g2.vs())): # j is index of node in *second-order* network # we first get the name of the *target* of the underlying edge node = g2.vs()["name"][j].split(t.separator)[1] # we map the target of second-order node j to the index of the *first-order* node map_2_to_1[j] = map_name_to_id[node] # compute stationary state of random walk process pi = Utilities.StationaryDistribution(T) scale = np.mean(np.abs(x - pi)) # lambda expression for the coloring of nodes according to some quantity p \in [0,1] # p = 1 ==> color red # p = 0 ==> color white color_p = lambda p: "rgb(255," + str(int( (1 - p) * 255)) + "," + str(int((1 - p) * 255)) + ")" visual_style["edge_color"] = ["darkgrey"] * g1.ecount() visual_style["edge_width"] = [.5] * g1.ecount() # Create video frames for i in range(0, steps): # based on visitation probabilities in *second-order* aggregate network, # we need to compute visitation probabilities of nodes in the *first-order* # aggregate network x_firstorder = np.zeros(len(g1.vs())) # j is the index of nodes in the *second-order* network, which we need to map # to nodes in the *first-order* network for j in range(len(x)): if x[j] > 0: x_firstorder[ map_2_to_1[j]] = x_firstorder[map_2_to_1[j]] + x[j] # Perform some reasonable color scaling visual_style["vertex_color"] = [ color_p(np.power((p - min(x)) / (max(x) - min(x)), exp)) for p in x_firstorder ] # Visualize illustrative network dynamics if dynamic == True: #slice = igraph.Graph(n=len(g1.vs())) #slice.vs["name"] = g1.vs["name"] L = len(temporal.ordered_times) for e in temporal.time[temporal.ordered_times[i % L]]: e_id = g1.get_eid(e[0], e[1]) visual_style["edge_width"][e_id] = 5 visual_style["edge_color"][e_id] = "black" #slice.add_edge(e[0], e[1]) igraph.plot(g1, file_prefix + "_frame_" + str(i).zfill(5) + ".png", **visual_style) else: igraph.plot(g1, file_prefix + "_frame_" + str(i).zfill(5) + ".png", **visual_style) # Plot first-order aggregate network after 30 RW steps (particularly useful as poster frame of video) if i == 30: visual_style["edge_color"] = "black" visual_style["edge_width"] = 1 igraph.plot(g1, file_prefix + "_network.pdf", **visual_style) # Reset edge color and width visual_style["edge_color"] = ["darkgrey"] * g1.ecount() visual_style["edge_width"] = [.5] * g1.ecount() if i % 50 == 0: Log.add('Frame ' + str(i) + '\tTVD = ' + str(Utilities.TVD(x, pi))) # every NWframesPerRWStep frames, perform one random walk step if i % NWframesPerRWStep == 0: # NOTE x * T = (T^T * x^T)^T # NOTE T is already transposed to get the left EV x = (T.dot(x.transpose())).transpose()