def random_reference(G, niter=1, connectivity=True, seed=None): """Compute a random graph by swapping edges of a given graph. Parameters ---------- G : graph An undirected graph with 4 or more nodes. niter : integer (optional, default=1) An edge is rewired approximately `niter` times. connectivity : boolean (optional, default=True) When True, ensure connectivity for the randomized graph. seed : integer, random_state, or None (default) Indicator of random number generation state. See :ref:`Randomness<randomness>`. Returns ------- G : graph The randomized graph. Notes ----- The implementation is adapted from the algorithm by Maslov and Sneppen (2002) [1]_. References ---------- .. [1] Maslov, Sergei, and Kim Sneppen. "Specificity and stability in topology of protein networks." Science 296.5569 (2002): 910-913. """ if G.is_directed(): msg = "random_reference() not defined for directed graphs." raise nx.NetworkXError(msg) if len(G) < 4: raise nx.NetworkXError("Graph has less than four nodes.") from networkx.utils import cumulative_distribution, discrete_sequence local_conn = nx.connectivity.local_edge_connectivity G = G.copy() keys, degrees = zip(*G.degree()) # keys, degree cdf = cumulative_distribution(degrees) # cdf of degree nnodes = len(G) nedges = nx.number_of_edges(G) niter = niter * nedges ntries = int(nnodes * nedges / (nnodes * (nnodes - 1) / 2)) swapcount = 0 for i in range(niter): n = 0 while n < ntries: # pick two random edges without creating edge list # choose source node indices from discrete distribution (ai, ci) = discrete_sequence(2, cdistribution=cdf, seed=seed) if ai == ci: continue # same source, skip a = keys[ai] # convert index to label c = keys[ci] # choose target uniformly from neighbors b = seed.choice(list(G.neighbors(a))) d = seed.choice(list(G.neighbors(c))) if b in [a, c, d] or d in [a, b, c]: continue # all vertices should be different # don't create parallel edges if (d not in G[a]) and (b not in G[c]): G.add_edge(a, d) G.add_edge(c, b) G.remove_edge(a, b) G.remove_edge(c, d) # Check if the graph is still connected if connectivity and local_conn(G, a, b) == 0: # Not connected, revert the swap G.remove_edge(a, d) G.remove_edge(c, b) G.add_edge(a, b) G.add_edge(c, d) else: swapcount += 1 break n += 1 return G
def lattice_reference(G, niter=1, D=None, connectivity=True, seed=None): """Latticize the given graph by swapping edges. Parameters ---------- G : graph An undirected graph with 4 or more nodes. niter : integer (optional, default=1) An edge is rewired approximatively niter times. D : numpy.array (optional, default=None) Distance to the diagonal matrix. connectivity : boolean (optional, default=True) Ensure connectivity for the latticized graph when set to True. seed : integer, random_state, or None (default) Indicator of random number generation state. See :ref:`Randomness<randomness>`. Returns ------- G : graph The latticized graph. Notes ----- The implementation is adapted from the algorithm by Sporns et al. [1]_. which is inspired from the original work by Maslov and Sneppen(2002) [2]_. References ---------- .. [1] Sporns, Olaf, and Jonathan D. Zwi. "The small world of the cerebral cortex." Neuroinformatics 2.2 (2004): 145-162. .. [2] Maslov, Sergei, and Kim Sneppen. "Specificity and stability in topology of protein networks." Science 296.5569 (2002): 910-913. """ import numpy as np from networkx.utils import cumulative_distribution, discrete_sequence local_conn = nx.connectivity.local_edge_connectivity if G.is_directed(): msg = "lattice_reference() not defined for directed graphs." raise nx.NetworkXError(msg) if len(G) < 4: raise nx.NetworkXError("Graph has less than four nodes.") # Instead of choosing uniformly at random from a generated edge list, # this algorithm chooses nonuniformly from the set of nodes with # probability weighted by degree. G = G.copy() keys, degrees = zip(*G.degree()) # keys, degree cdf = cumulative_distribution(degrees) # cdf of degree nnodes = len(G) nedges = nx.number_of_edges(G) if D is None: D = np.zeros((nnodes, nnodes)) un = np.arange(1, nnodes) um = np.arange(nnodes - 1, 0, -1) u = np.append((0, ), np.where(un < um, un, um)) for v in range(int(np.ceil(nnodes / 2))): D[nnodes - v - 1, :] = np.append(u[v + 1:], u[:v + 1]) D[v, :] = D[nnodes - v - 1, :][::-1] niter = niter * nedges ntries = int(nnodes * nedges / (nnodes * (nnodes - 1) / 2)) swapcount = 0 for i in range(niter): n = 0 while n < ntries: # pick two random edges without creating edge list # choose source node indices from discrete distribution (ai, ci) = discrete_sequence(2, cdistribution=cdf, seed=seed) if ai == ci: continue # same source, skip a = keys[ai] # convert index to label c = keys[ci] # choose target uniformly from neighbors b = seed.choice(list(G.neighbors(a))) d = seed.choice(list(G.neighbors(c))) bi = keys.index(b) di = keys.index(d) if b in [a, c, d] or d in [a, b, c]: continue # all vertices should be different # don't create parallel edges if (d not in G[a]) and (b not in G[c]): if D[ai, bi] + D[ci, di] >= D[ai, ci] + D[bi, di]: # only swap if we get closer to the diagonal G.add_edge(a, d) G.add_edge(c, b) G.remove_edge(a, b) G.remove_edge(c, d) # Check if the graph is still connected if connectivity and local_conn(G, a, b) == 0: # Not connected, revert the swap G.remove_edge(a, d) G.remove_edge(c, b) G.add_edge(a, b) G.add_edge(c, d) else: swapcount += 1 break n += 1 return G
def lattice_reference(G, niter=1, D=None, connectivity=True, seed=None): """Latticize the given graph by swapping edges. Parameters ---------- G : graph An undirected graph with 4 or more nodes. niter : integer (optional, default=1) An edge is rewired approximatively niter times. D : numpy.array (optional, default=None) Distance to the diagonal matrix. connectivity : boolean (optional, default=True) Ensure connectivity for the latticized graph when set to True. seed : integer, random_state, or None (default) Indicator of random number generation state. See :ref:`Randomness<randomness>`. Returns ------- G : graph The latticized graph. Notes ----- The implementation is adapted from the algorithm by Sporns et al. [1]_. which is inspired from the original work by Maslov and Sneppen(2002) [2]_. References ---------- .. [1] Sporns, Olaf, and Jonathan D. Zwi. "The small world of the cerebral cortex." Neuroinformatics 2.2 (2004): 145-162. .. [2] Maslov, Sergei, and Kim Sneppen. "Specificity and stability in topology of protein networks." Science 296.5569 (2002): 910-913. """ import numpy as np from networkx.utils import cumulative_distribution, discrete_sequence local_conn = nx.connectivity.local_edge_connectivity if G.is_directed(): msg = "lattice_reference() not defined for directed graphs." raise nx.NetworkXError(msg) if len(G) < 4: raise nx.NetworkXError("Graph has less than four nodes.") # Instead of choosing uniformly at random from a generated edge list, # this algorithm chooses nonuniformly from the set of nodes with # probability weighted by degree. G = G.copy() keys, degrees = zip(*G.degree()) # keys, degree cdf = cumulative_distribution(degrees) # cdf of degree nnodes = len(G) nedges = nx.number_of_edges(G) if D is None: D = np.zeros((nnodes, nnodes)) un = np.arange(1, nnodes) um = np.arange(nnodes - 1, 0, -1) u = np.append((0,), np.where(un < um, un, um)) for v in range(int(np.ceil(nnodes / 2))): D[nnodes - v - 1, :] = np.append(u[v + 1:], u[:v + 1]) D[v, :] = D[nnodes - v - 1, :][::-1] niter = niter*nedges ntries = int(nnodes * nedges / (nnodes * (nnodes - 1) / 2)) swapcount = 0 for i in range(niter): n = 0 while n < ntries: # pick two random edges without creating edge list # choose source node indices from discrete distribution (ai, ci) = discrete_sequence(2, cdistribution=cdf, seed=seed) if ai == ci: continue # same source, skip a = keys[ai] # convert index to label c = keys[ci] # choose target uniformly from neighbors b = seed.choice(list(G.neighbors(a))) d = seed.choice(list(G.neighbors(c))) bi = keys.index(b) di = keys.index(d) if b in [a, c, d] or d in [a, b, c]: continue # all vertices should be different # don't create parallel edges if (d not in G[a]) and (b not in G[c]): if D[ai, bi] + D[ci, di] >= D[ai, ci] + D[bi, di]: # only swap if we get closer to the diagonal G.add_edge(a, d) G.add_edge(c, b) G.remove_edge(a, b) G.remove_edge(c, d) # Check if the graph is still connected if connectivity and local_conn(G, a, b) == 0: # Not connected, revert the swap G.remove_edge(a, d) G.remove_edge(c, b) G.add_edge(a, b) G.add_edge(c, d) else: swapcount += 1 break n += 1 return G
def random_reference(G, n_iter=1, D=None, seed=np.random.seed(np.random.randint(0, 2**30))): """Latticize the given graph by swapping edges. Parameters ---------- G : graph An undirected graph with 4 or more nodes. n_iter : integer (optional, default=1) An edge is rewired approximatively n_iter times. D : numpy.array (optional, default=None) Distance to the diagonal matrix. Returns ------- G : graph The latticized graph. Notes ----- The implementation is adapted from the algorithm by Sporns et al. [1]_. which is inspired from the original work by Maslov and Sneppen(2002) [2]_. References ---------- .. [1] Sporns, Olaf, and Jonathan D. Zwi. "The small world of the cerebral cortex." Neuroinformatics 2.2 (2004): 145-162. .. [2] Maslov, Sergei, and Kim Sneppen. "Specificity and stability in topology of protein networks." Science 296.5569 (2002): 910-913. """ from networkx.utils import cumulative_distribution, discrete_sequence # Instead of choosing uniformly at random from a generated edge list, # this algorithm chooses nonuniformly from the set of nodes with # probability weighted by degree. G = G.copy() #keys, degrees = zip(*G.degree()) # keys, degree keys = [i for i in range(len(G))] degrees = weighted_node_degree(G) cdf = cumulative_distribution(degrees) # cdf of degree nnodes = len(G) nedges = nnodes * (nnodes - 1) // 2 # NOTE: assuming full connectivity #nedges = nx.number_of_edges(G) if D is None: D = np.zeros((nnodes, nnodes)) un = np.arange(1, nnodes) um = np.arange(nnodes - 1, 0, -1) u = np.append((0, ), np.where(un < um, un, um)) for v in range(int(np.ceil(nnodes / 2))): D[nnodes - v - 1, :] = np.append(u[v + 1:], u[:v + 1]) D[v, :] = D[nnodes - v - 1, :][::-1] n_iter = n_iter * nedges ntries = int(nnodes * nedges / (nnodes * (nnodes - 1) / 2)) swapcount = 0 for i in range(n_iter): n = 0 while n < ntries: # pick two random edges without creating edge list # choose source node indices from discrete distribution (ai, bi, ci, di) = discrete_sequence(4, cdistribution=cdf, seed=seed) if len(set((ai, bi, ci, di))) < 4: continue # picked same node twice a = keys[ai] # convert index to label b = keys[bi] c = keys[ci] d = keys[di] # only swap if we get closer to the diagonal ab = G[a, b] cd = G[c, d] G[a, b] = cd G[b, a] = cd G[c, d] = ab G[d, c] = ab swapcount += 1 break return G
def connected_double_edge_swap_with_birthday_check(G, nswap=1): """ Completes nswap double-edge swaps on the graph G. A double-edge swap removes two randomly chosen edges u->v and x->y and creates the new edges u->x and y->v, provided this move retains the 'birthday' ordering of the nodes in the original edges. Parameters G=A directed graph, nswap : integer = Number of successful double-edge swaps to perform. Returns The number of successful swaps """ # uncomment below if want to ensure connectedness of initial graph. This should be true anyway for all our models/data, unless edge removal used # if not nx.is_connected(G): # raise nx.NetworkXError("Graph not connected") # if len(G) < 4: # raise nx.NetworkXError("Graph has less than four nodes.") n = 0 swapcount = 0 deg = G.in_degree() dk = list(deg.keys()) # Label key for nodes cdf = utils.cumulative_distribution(list(G.in_degree().values())) window = 1 while swapcount < nswap: wcount = 0 swapped = [] while wcount < window and swapcount < nswap: # Pick two random edges without creating edge list # Choose source nodes from discrete degree distribution (ui, xi) = utils.discrete_sequence(2, cdistribution=cdf) if ui == xi: continue # same source u = dk[ui] # convert index to label x = dk[xi] # Choose targets uniformly from neighbors u_neighbors = G.neighbors(u) x_neighbors = G.neighbors(x) if len(u_neighbors) != 0 and len(x_neighbors) != 0: v = random.choice(u_neighbors) y = random.choice(x_neighbors) if v == y: continue # same target if models.birthday_check(G, x, v) == False or models.birthday_check(G, u, y) == False: print "birthday condition not met" continue else: if (not G.has_edge(x, v)) and (not G.has_edge(u, y)): G.remove_edge(u, v) G.remove_edge(x, y) G.add_edge(x, v) G.add_edge(u, y) swapped.append((u, v, x, y)) swapcount += 1 print "swapcount is = ", swapcount n += 1 wcount += 1 # uncomment below if want to ensure connectedness of final graph, is this necessary? WIll be for some measures, but not for k_in...? # UG=G.to_undirected() # if nx.is_connected(UG): # window+=1 # else: # "graph has become disconnected, undoing changes that caused this" # # not connected, undo changes from previous window, decrease window # while swapped: # (u,v,x,y)=swapped.pop() # G.add_edge(u,v) # G.add_edge(x,y) # G.remove_edge(u,x) # G.remove_edge(v,y) # swapcount-=1 # window = int(math.ceil(float(window)/2)) # print "swapcount = " , swapcount return swapcount