我试图模拟一个加权的、定向的、无标度的网络,如本文第3节所述。由于没有LaTeX排版,我尝试在下图中总结我对该过程的理解:
请记住,这是我对所述程序的解释,因此我理解中的错误可能是错误的根源。到目前为止,我尝试的解决方案并没有导致幂律度分布。我的代码如下:
import numpy as np
import networkx as nx
N = 1000 # Final no. of nodes
d = 80 # Parameter for edge-weight update (called delta in paper)
m0 = 10 # Initial nodes
w0 = 100 # Initial weights
m1_in = 1 # Number of new edges pointing TOWARDS the new node
m1_out = 1 # Number of new edges pointing AWAY from the new node
m2 = 0 # Number of new edges between previously existing nodes
# Create fully connected networks
G = nx.complete_graph(n=m0, create_using=nx.DiGraph)
# Set initial weights equal to w0
for e in G.edges():
G[e[0]][e[1]]['Transaction'] = w0
# At each timestep t
t = m0 #0
while t <= N:
# Calculate in/out strength of existing nodes
s_in = dict(G.in_degree(weight='Transaction'))
s_out = dict(G.out_degree(weight='Transaction'))
# Calculate totals
s_in_sum = sum(s_in.values(), 0.0)
s_out_sum = sum(s_out.values(), 0.0)
# Probability of linking joining new edges to existing nodes
p_in = {k: v / s_in_sum for k, v in s_in.items()}
p_out = {k: v / s_out_sum for k, v in s_out.items()}
# ----------------------------------------------------------
# Prob. of existing node RECEIVING new edge is given by in_limit
# In-limit
in_list = [i for i in p_in.values()] # Stores in-probabilities as a list
in_limit = [0.0] # Initializes value at 0
for i, p_i in enumerate(in_list):
in_limit.append(p_i + in_limit[i])
# Select m1_out existing nodes that will RECEIVE new edges
in_neigh = select_neigh(lim_list=in_limit, num_neigh=m1_out)
# CALL WEIGHT UPDATE
# Before adding new neighbour, update strength of existing neighbours
# After updating, add link (weight w0) to new node. New node is SOURCE of edge
update_weights(G=G, neighbours=in_neigh, delta=d, s_dict=s_in, case_1a=False, case_1b=True)
# ----------------------------------------------------------
# Prob. of existing node being the SOURCE of a new edge is given by out_limit
# Out-limit
out_list = [i for i in p_out.values()] # Stores out-probabilities as a list
out_limit = [0.0] # Initializes value at 0
for i, p_o in enumerate(out_list):
out_limit.append(p_o + out_limit[i])
# CALL SELECT OUT-NEIGHBOUR
# Select m1_in existing nodes that will be SOURCES of new edges
out_neigh = select_neigh(lim_list=out_limit, num_neigh=m1_in)
# CALL WEIGHT UPDATE
# Before adding new neighbour, update strength of existing neighbours
# After updating, add link (weight w0) to new node. New node is DESTINATION of edge
update_weights(G=G, neighbours=out_neigh, delta=d, s_dict=s_out, case_1a=True, case_1b=False)
# -----------------------------------------------------------
# Add new node - new node is DESTINATION of new edge
G.add_node(t)
# Add paths to selected nodes
for n in in_neigh:
G.add_edge(n, t, Transaction=w0)
# Add new node - new node is SOURCE of new edge
for n in out_neigh:
G.add_edge(t, n, Transaction=w0)
# Case 2 - New edges appear between previously existing nodes
# To do
# Next step
t += 1
我定义的功能如下:
# Select neighbours
def select_neigh(lim_list, num_neigh):
'''
Args
------------------------------------
lim_list: list - Probability range for each node
num_neigh: int - Number of incoming nodes to assign to the new node.
m1_out in the paper for INCOMING edges to a new node
m1_in in the paper for OUTGOING edges to a new node
------------------------------------
Returns: list of nodes selected for the new edge
'''
# List to store the neighbours
neighbours = []
# Flag to see if node is already a neighbour
already_neighbour = False
# Iterate through EXISTING NODES
i = 0
while i < num_neigh:
rnd = np.random.random() # random number between 0 and 1
# compare the random number to the limits and add node accordingly
for j in range(len(lim_list) - 1):
if rnd >= lim_list[j] and rnd < lim_list[j+1]:
# if j is already a neighbor
if j in neighbours:
# Raise the flag
already_neighbour =True
else:
# if j is not already a neighbor add it to the neighbors list
neighbours.append(j)
# if the alread_neighbor flag is true, decrement i to redo the choice randomly and reset flag
if already_neighbour == True:
already_neighbour = False
i -= 1 # To repeat the choice of the node
i+=1
# Return result
return neighbours
# Update weights
def update_weights(G, neighbours, delta, s_dict, case_1a = False, case_1b=False):
'''
Args
------------------------------------
G: nx.DiGraph object - NetworkX network before being updated
neighbours: list - list of neighbours to generate links into/from
delta: float - delta parameter in the paper. Updates weights
s_dict: Strength dictionary of every node.
In/Out depends on incoming/outgoing edges
case_1a: bool - Flag that determines if new edges are INCOMING towards existing nodes
case_1b: bool - Flag that determines if new edges are OUTGOING from existing nodes
'''
if case_1b:
# Update weights of existing edges
for n in neighbours:
for e in G.in_edges(n, data=True):
w_in = G[e[0]][n]['Transaction'] # Value of edges incoming to n
d_w = (delta*w_in)/s_dict[e[0]] # Value of shift
# Update weight
G[e[0]][n]['Transaction'] += d_w
if case_1a:
# Update weights of existing edges
for n in neighbours:
for e in G.out_edges(n, data=True):
w_nj = G[n][e[1]]['Transaction'] # Value of edges incoming to n
d_w = (delta*w_nj)/s_dict[e[1]] # Value of shift
# Update weight
G[n][e[1]]['Transaction'] += d_w