in python/graphscope/nx/generators/random_graphs.py [0:0]
def extended_barabasi_albert_graph(n, m, p, q, seed=None):
if m < 1 or m >= n:
msg = "Extended Barabasi-Albert network needs m>=1 and m<n, m=%d, n=%d"
raise nx.NetworkXError(msg % (m, n))
if p + q >= 1:
msg = "Extended Barabasi-Albert network needs p + q <= 1, p=%d, q=%d"
raise nx.NetworkXError(msg % (p, q))
# Add m initial nodes (m0 in barabasi-speak)
G = empty_graph(m)
# List of nodes to represent the preferential attachment random selection.
# At the creation of the graph, all nodes are added to the list
# so that even nodes that are not connected have a chance to get selected,
# for rewiring and adding of edges.
# With each new edge, nodes at the ends of the edge are added to the list.
attachment_preference = []
attachment_preference.extend(range(m))
# Start adding the other n-m nodes. The first node is m.
new_node = m
while new_node < n:
a_probability = seed.random()
# Total number of edges of a Clique of all the nodes
clique_degree = len(G) - 1
clique_size = (len(G) * clique_degree) / 2
# Adding m new edges, if there is room to add them
if a_probability < p and G.size() <= clique_size - m:
# Select the nodes where an edge can be added
elligible_nodes = [nd for nd, deg in G.degree() if deg < clique_degree]
for i in range(m):
# Choosing a random source node from elligible_nodes
src_node = seed.choice(elligible_nodes)
# Picking a possible node that is not 'src_node' or
# neighbor with 'src_node', with preferential attachment
prohibited_nodes = list(G[src_node])
prohibited_nodes.append(src_node)
# This will raise an exception if the sequence is empty
dest_node = seed.choice(
[nd for nd in attachment_preference if nd not in prohibited_nodes]
)
# Adding the new edge
G.add_edge(src_node, dest_node)
# Appending both nodes to add to their preferential attachment
attachment_preference.append(src_node)
attachment_preference.append(dest_node)
# Adjusting the elligible nodes. Degree may be saturated.
if G.degree(src_node) == clique_degree:
elligible_nodes.remove(src_node)
if (
G.degree(dest_node) == clique_degree
and dest_node in elligible_nodes
):
elligible_nodes.remove(dest_node)
# Rewiring m edges, if there are enough edges
elif p <= a_probability < (p + q) and m <= G.size() < clique_size:
# Selecting nodes that have at least 1 edge but that are not
# fully connected to ALL other nodes (center of star).
# These nodes are the pivot nodes of the edges to rewire
elligible_nodes = [nd for nd, deg in G.degree() if 0 < deg < clique_degree]
for i in range(m):
# Choosing a random source node
node = seed.choice(elligible_nodes)
# The available nodes do have a neighbor at least.
neighbor_nodes = list(G[node])
# Choosing the other end that will get detached
src_node = seed.choice(neighbor_nodes)
# Picking a target node that is not 'node' or
# neighbor with 'node', with preferential attachment
neighbor_nodes.append(node)
dest_node = seed.choice(
[nd for nd in attachment_preference if nd not in neighbor_nodes]
)
# Rewire
G.remove_edge(node, src_node)
G.add_edge(node, dest_node)
# Adjusting the preferential attachment list
attachment_preference.remove(src_node)
attachment_preference.append(dest_node)
# Adjusting the elligible nodes.
# nodes may be saturated or isolated.
if G.degree(src_node) == 0 and src_node in elligible_nodes:
elligible_nodes.remove(src_node)
if dest_node in elligible_nodes:
if G.degree(dest_node) == clique_degree:
elligible_nodes.remove(dest_node)
else:
if G.degree(dest_node) == 1:
elligible_nodes.append(dest_node)
# Adding new node with m edges
else:
# Select the edges' nodes by preferential attachment
targets = _random_subset(attachment_preference, m, seed)
G.add_edges_from(zip([new_node] * m, targets))
# Add one node to the list for each new edge just created.
attachment_preference.extend(targets)
# The new node has m edges to it, plus itself: m + 1
attachment_preference.extend([new_node] * (m + 1))
new_node += 1
return G