def gnc_graph(n,seed=None): """Return the GNC (growing network with copying) digraph with n nodes. The graph is built by adding nodes one at a time with a links to one previously added node (chosen uniformly at random) and to all of that node's successors. Reference:: @article{krapivsky-2005-network, title = {Network Growth by Copying}, author = {P. L. Krapivsky and S. Redner}, journal = {Phys. Rev. E}, volume = {71}, pages = {036118}, year = {2005}, } """ G=empty_graph(1,create_using=networkx_v099.DiGraph()) G.name="gnc_graph(%s)"%(n) if not seed is None: random.seed(seed) if n==1: return G for source in range(1,n): target=random.randrange(0,source) for succ in G.successors(target): G.add_edge(source,succ) G.add_edge(source,target) return G
def gnp_random_graph(n, p, seed=None): """ Return a random graph G_{n,p}. Choses each of the possible [n(n-1)]/2 edges with probability p. This is the same as binomial_graph and erdos_renyi_graph. Sometimes called Erdős-Rényi graph, or binomial graph. :Parameters: - `n`: the number of nodes - `p`: probability for edge creation - `seed`: seed for random number generator (default=None) This is an O(n^2) algorithm. For sparse graphs (small p) see fast_gnp_random_graph. P. Erdős and A. Rényi, On Random Graphs, Publ. Math. 6, 290 (1959). E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959). """ G=empty_graph(n) G.name="gnp_random_graph(%s,%s)"%(n,p) if not seed is None: random.seed(seed) for u in xrange(n): for v in xrange(u+1,n): if random.random() < p: G.add_edge(u,v) return G
def gnc_graph(n, seed=None): """Return the GNC (growing network with copying) digraph with n nodes. The graph is built by adding nodes one at a time with a links to one previously added node (chosen uniformly at random) and to all of that node's successors. Reference:: @article{krapivsky-2005-network, title = {Network Growth by Copying}, author = {P. L. Krapivsky and S. Redner}, journal = {Phys. Rev. E}, volume = {71}, pages = {036118}, year = {2005}, } """ G = empty_graph(1, create_using=networkx_v099.DiGraph()) G.name = "gnc_graph(%s)" % (n) if not seed is None: random.seed(seed) if n == 1: return G for source in range(1, n): target = random.randrange(0, source) for succ in G.successors(target): G.add_edge(source, succ) G.add_edge(source, target) return G
def degree_sequence_tree(deg_sequence): """ Make a tree for the given degree sequence. A tree has #nodes-#edges=1 so the degree sequence must have len(deg_sequence)-sum(deg_sequence)/2=1 """ if not len(deg_sequence)-sum(deg_sequence)/2.0 == 1.0: raise networkx_v099.NetworkXError,"Degree sequence invalid" G=empty_graph(0) # single node tree if len(deg_sequence)==1: return G deg=[s for s in deg_sequence if s>1] # all degrees greater than 1 deg.sort(reverse=True) # make path graph as backbone n=len(deg)+2 G=networkx_v099.path_graph(n) last=n # add the leaves for source in range(1,n-1): nedges=deg.pop()-2 for target in range(last,last+nedges): G.add_edge(source, target) last+=nedges # in case we added one too many if len(G.degree())>len(deg_sequence): G.delete_node(0) return G
def gnp_random_graph(n, p, seed=None): """ Return a random graph G_{n,p}. Choses each of the possible [n(n-1)]/2 edges with probability p. This is the same as binomial_graph and erdos_renyi_graph. Sometimes called Erdős-Rényi graph, or binomial graph. :Parameters: - `n`: the number of nodes - `p`: probability for edge creation - `seed`: seed for random number generator (default=None) This is an O(n^2) algorithm. For sparse graphs (small p) see fast_gnp_random_graph. P. Erdős and A. Rényi, On Random Graphs, Publ. Math. 6, 290 (1959). E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959). """ G = empty_graph(n) G.name = "gnp_random_graph(%s,%s)" % (n, p) if not seed is None: random.seed(seed) for u in xrange(n): for v in xrange(u + 1, n): if random.random() < p: G.add_edge(u, v) return G
def LCF_graph(n, shift_list, repeats): """ Return the cubic graph specified in LCF notation. LCF notation (LCF=Lederberg-Coxeter-Fruchte) is a compressed notation used in the generation of various cubic Hamiltonian graphs of high symmetry. See, for example, dodecahedral_graph, desargues_graph, heawood_graph and pappus_graph below. n (number of nodes) The starting graph is the n-cycle with nodes 0,...,n-1. (The null graph is returned if n < 0.) shift_list = [s1,s2,..,sk], a list of integer shifts mod n, repeats integer specifying the number of times that shifts in shift_list are successively applied to each v_current in the n-cycle to generate an edge between v_current and v_current+shift mod n. For v1 cycling through the n-cycle a total of k*repeats with shift cycling through shiftlist repeats times connect v1 with v1+shift mod n The utility graph K_{3,3} >>> G=nx.LCF_graph(6,[3,-3],3) The Heawood graph >>> G=nx.LCF_graph(14,[5,-5],7) See http://mathworld.wolfram.com/LCFNotation.html for a description and references. """ if n <= 0: return empty_graph() # start with the n-cycle G = cycle_graph(n) G.name = "LCF_graph" nodes = G.nodes() n_extra_edges = repeats * len(shift_list) # edges are added n_extra_edges times # (not all of these need be new) if n_extra_edges < 1: return G for i in range(n_extra_edges): shift = shift_list[i % len(shift_list)] #cycle through shift_list v1 = nodes[i % n] # cycle repeatedly through nodes v2 = nodes[(i + shift) % n] G.add_edge(v1, v2) return G
def LCF_graph(n,shift_list,repeats): """ Return the cubic graph specified in LCF notation. LCF notation (LCF=Lederberg-Coxeter-Fruchte) is a compressed notation used in the generation of various cubic Hamiltonian graphs of high symmetry. See, for example, dodecahedral_graph, desargues_graph, heawood_graph and pappus_graph below. n (number of nodes) The starting graph is the n-cycle with nodes 0,...,n-1. (The null graph is returned if n < 0.) shift_list = [s1,s2,..,sk], a list of integer shifts mod n, repeats integer specifying the number of times that shifts in shift_list are successively applied to each v_current in the n-cycle to generate an edge between v_current and v_current+shift mod n. For v1 cycling through the n-cycle a total of k*repeats with shift cycling through shiftlist repeats times connect v1 with v1+shift mod n The utility graph K_{3,3} >>> G=nx.LCF_graph(6,[3,-3],3) The Heawood graph >>> G=nx.LCF_graph(14,[5,-5],7) See http://mathworld.wolfram.com/LCFNotation.html for a description and references. """ if n <= 0: return empty_graph() # start with the n-cycle G=cycle_graph(n) G.name="LCF_graph" nodes=G.nodes() n_extra_edges=repeats*len(shift_list) # edges are added n_extra_edges times # (not all of these need be new) if n_extra_edges < 1: return G for i in range(n_extra_edges): shift=shift_list[i%len(shift_list)] #cycle through shift_list v1=nodes[i%n] # cycle repeatedly through nodes v2=nodes[(i + shift)%n] G.add_edge(v1, v2) return G
def barabasi_albert_graph(n, m, seed=None): """Return random graph using Barabási-Albert preferential attachment model. A graph of n nodes is grown by attaching new nodes each with m edges that are preferentially attached to existing nodes with high degree. :Parameters: - `n`: the number of nodes - `m`: number of edges to attach from a new node to existing nodes - `seed`: seed for random number generator (default=None) The initialization is a graph with with m nodes and no edges. Reference:: @article{barabasi-1999-emergence, title = {Emergence of scaling in random networks}, author = {A. L. Barabási and R. Albert}, journal = {Science}, volume = {286}, number = {5439}, pages = {509 -- 512}, year = {1999}, } """ if m < 1 or m >= n: raise networkx_v099.NetworkXError,\ "Barabási-Albert network must have m>=1 and m<n, m=%d,n=%d"%(m,n) if seed is not None: random.seed(seed) G = empty_graph(m) # add m initial nodes (m0 in barabasi-speak) G.name = "barabasi_albert_graph(%s,%s)" % (n, m) edge_targets = range(m) # possible targets for new edges repeated_nodes = [] # list of existing nodes, # with nodes repeated once for each adjacent edge source = m # next node is m while source < n: # Now add the other n-m nodes G.add_edges_from(zip([source] * m, edge_targets)) # add links to m nodes repeated_nodes.extend(edge_targets) # add one node for each new link repeated_nodes.extend([source] * m) # and new node "source" has m links # choose m nodes randomly from existing nodes # N.B. during each step of adding a new node the probabilities # are fixed, is this correct? or should they be updated. # Also, random sampling prevents some parallel edges. edge_targets = random.sample(repeated_nodes, m) source += 1 return G
def gn_graph(n,kernel=lambda x:x ,seed=None): """Return the GN (growing network) digraph with n nodes. The graph is built by adding nodes one at a time with a link to one previously added node. The target node for the link is chosen with probability based on degree. The default attachment kernel is a linear function of degree. The graph is always a (directed) tree. Example: >>> D=nx.gn_graph(10) # the GN graph >>> G=D.to_undirected() # the undirected version To specify an attachment kernel use the kernel keyword >>> D=nx.gn_graph(10,kernel=lambda x:x**1.5) # A_k=k^1.5 Reference:: @article{krapivsky-2001-organization, title = {Organization of Growing Random Networks}, author = {P. L. Krapivsky and S. Redner}, journal = {Phys. Rev. E}, volume = {63}, pages = {066123}, year = {2001}, } """ G=empty_graph(1,create_using=networkx_v099.DiGraph()) G.name="gn_graph(%s)"%(n) if seed is not None: random.seed(seed) if n==1: return G G.add_edge(1,0) # get started ds=[1,1] # degree sequence for source in range(2,n): # compute distribution from kernel and degree dist=[kernel(d) for d in ds] # choose target from discrete distribution target=discrete_sequence(1,distribution=dist)[0] G.add_edge(source,target) ds.append(1) # the source has only one link (degree one) ds[target]+=1 # add one to the target link degree return G
def make_small_graph(graph_description, create_using=None): """ Return the small graph described by graph_description. graph_description is a list of the form [ltype,name,n,xlist] Here ltype is one of "adjacencylist" or "edgelist", name is the name of the graph and n the number of nodes. This constructs a graph of n nodes with integer labels 1,..,n. If ltype="adjacencylist" then xlist is an adjacency list with exactly n entries, in with the j'th entry (which can be empty) specifies the nodes connected to vertex j. e.g. the "square" graph C_4 can be obtained by >>> G=nx.make_small_graph(["adjacencylist","C_4",4,[[2,4],[1,3],[2,4],[1,3]]]) or, since we do not need to add edges twice, >>> G=nx.make_small_graph(["adjacencylist","C_4",4,[[2,4],[3],[4],[]]]) If ltype="edgelist" then xlist is an edge list written as [[v1,w2],[v2,w2],...,[vk,wk]], where vj and wj integers in the range 1,..,n e.g. the "square" graph C_4 can be obtained by >>> G=nx.make_small_graph(["edgelist","C_4",4,[[1,2],[3,4],[2,3],[4,1]]]) Use the create_using argument to choose the graph class/type. """ ltype = graph_description[0] name = graph_description[1] n = graph_description[2] G = empty_graph(n, create_using) nodes = G.nodes() if ltype == "adjacencylist": adjlist = graph_description[3] if len(adjlist) != n: raise NetworkXError, "invalid graph_description" G.add_edges_from([(u - 1, v) for v in nodes for u in adjlist[v]]) elif ltype == "edgelist": edgelist = graph_description[3] for e in edgelist: v1 = e[0] - 1 v2 = e[1] - 1 if v1 < 0 or v1 > n - 1 or v2 < 0 or v2 > n - 1: raise NetworkXError, "invalid graph_description" else: G.add_edge(v1, v2) G.name = name return G
def make_small_graph(graph_description, create_using=None): """ Return the small graph described by graph_description. graph_description is a list of the form [ltype,name,n,xlist] Here ltype is one of "adjacencylist" or "edgelist", name is the name of the graph and n the number of nodes. This constructs a graph of n nodes with integer labels 1,..,n. If ltype="adjacencylist" then xlist is an adjacency list with exactly n entries, in with the j'th entry (which can be empty) specifies the nodes connected to vertex j. e.g. the "square" graph C_4 can be obtained by >>> G=nx.make_small_graph(["adjacencylist","C_4",4,[[2,4],[1,3],[2,4],[1,3]]]) or, since we do not need to add edges twice, >>> G=nx.make_small_graph(["adjacencylist","C_4",4,[[2,4],[3],[4],[]]]) If ltype="edgelist" then xlist is an edge list written as [[v1,w2],[v2,w2],...,[vk,wk]], where vj and wj integers in the range 1,..,n e.g. the "square" graph C_4 can be obtained by >>> G=nx.make_small_graph(["edgelist","C_4",4,[[1,2],[3,4],[2,3],[4,1]]]) Use the create_using argument to choose the graph class/type. """ ltype=graph_description[0] name=graph_description[1] n=graph_description[2] G=empty_graph(n, create_using) nodes=G.nodes() if ltype=="adjacencylist": adjlist=graph_description[3] if len(adjlist) != n: raise NetworkXError,"invalid graph_description" G.add_edges_from([(u-1,v) for v in nodes for u in adjlist[v]]) elif ltype=="edgelist": edgelist=graph_description[3] for e in edgelist: v1=e[0]-1 v2=e[1]-1 if v1<0 or v1>n-1 or v2<0 or v2>n-1: raise NetworkXError,"invalid graph_description" else: G.add_edge(v1,v2) G.name=name return G
def barabasi_albert_graph(n , m, seed=None): """Return random graph using Barabási-Albert preferential attachment model. A graph of n nodes is grown by attaching new nodes each with m edges that are preferentially attached to existing nodes with high degree. :Parameters: - `n`: the number of nodes - `m`: number of edges to attach from a new node to existing nodes - `seed`: seed for random number generator (default=None) The initialization is a graph with with m nodes and no edges. Reference:: @article{barabasi-1999-emergence, title = {Emergence of scaling in random networks}, author = {A. L. Barabási and R. Albert}, journal = {Science}, volume = {286}, number = {5439}, pages = {509 -- 512}, year = {1999}, } """ if m < 1 or m >=n: raise networkx_v099.NetworkXError,\ "Barabási-Albert network must have m>=1 and m<n, m=%d,n=%d"%(m,n) if seed is not None: random.seed(seed) G=empty_graph(m) # add m initial nodes (m0 in barabasi-speak) G.name="barabasi_albert_graph(%s,%s)"%(n,m) edge_targets=range(m) # possible targets for new edges repeated_nodes=[] # list of existing nodes, # with nodes repeated once for each adjacent edge source=m # next node is m while source<n: # Now add the other n-m nodes G.add_edges_from(zip([source]*m,edge_targets)) # add links to m nodes repeated_nodes.extend(edge_targets) # add one node for each new link repeated_nodes.extend([source]*m) # and new node "source" has m links # choose m nodes randomly from existing nodes # N.B. during each step of adding a new node the probabilities # are fixed, is this correct? or should they be updated. # Also, random sampling prevents some parallel edges. edge_targets=random.sample(repeated_nodes,m) source += 1 return G
def gn_graph(n, kernel=lambda x: x, seed=None): """Return the GN (growing network) digraph with n nodes. The graph is built by adding nodes one at a time with a link to one previously added node. The target node for the link is chosen with probability based on degree. The default attachment kernel is a linear function of degree. The graph is always a (directed) tree. Example: >>> D=nx.gn_graph(10) # the GN graph >>> G=D.to_undirected() # the undirected version To specify an attachment kernel use the kernel keyword >>> D=nx.gn_graph(10,kernel=lambda x:x**1.5) # A_k=k^1.5 Reference:: @article{krapivsky-2001-organization, title = {Organization of Growing Random Networks}, author = {P. L. Krapivsky and S. Redner}, journal = {Phys. Rev. E}, volume = {63}, pages = {066123}, year = {2001}, } """ G = empty_graph(1, create_using=networkx_v099.DiGraph()) G.name = "gn_graph(%s)" % (n) if seed is not None: random.seed(seed) if n == 1: return G G.add_edge(1, 0) # get started ds = [1, 1] # degree sequence for source in range(2, n): # compute distribution from kernel and degree dist = [kernel(d) for d in ds] # choose target from discrete distribution target = discrete_sequence(1, distribution=dist)[0] G.add_edge(source, target) ds.append(1) # the source has only one link (degree one) ds[target] += 1 # add one to the target link degree return G
def random_shell_graph(constructor, seed=None): """ Return a random shell graph for the constructor given. - constructor: a list of three-tuples [(n1,m1,d1),(n2,m2,d2),..] one for each shell, starting at the center shell. - n : the number of nodes in the shell - m : the number or edges in the shell - d : the ratio of inter (next) shell edges to intra shell edges. d=0 means no intra shell edges. d=1 for the last shell - `seed`: seed for random number generator (default=None) >>> constructor=[(10,20,0.8),(20,40,0.8)] >>> G=nx.random_shell_graph(constructor) """ G=empty_graph(0) G.name="random_shell_graph(constructor)" if seed is not None: random.seed(seed) glist=[] intra_edges=[] nnodes=0 # create gnm graphs for each shell for (n,m,d) in constructor: inter_edges=int(m*d) intra_edges.append(m-inter_edges) g=networkx_v099.operators.convert_node_labels_to_integers( gnm_random_graph(n,inter_edges), first_label=nnodes) glist.append(g) nnodes+=n G=networkx_v099.operators.union(G,g) # connect the shells randomly for gi in range(len(glist)-1): nlist1=glist[gi].nodes() nlist2=glist[gi+1].nodes() total_edges=intra_edges[gi] edge_count=0 while edge_count < total_edges: u = random.choice(nlist1) v = random.choice(nlist2) if u==v or G.has_edge(u,v): continue else: G.add_edge(u,v) edge_count=edge_count+1 return G
def dense_gnm_random_graph(n, m, seed=None): """ Return the random graph G_{n,m}. Gives a graph picked randomly out of the set of all graphs with n nodes and m edges. This algorithm should be faster than gnm_random_graph for dense graphs. :Parameters: - `n`: the number of nodes - `m`: the number of edges - `seed`: seed for random number generator (default=None) Algorithm by Keith M. Briggs Mar 31, 2006. Inspired by Knuth's Algorithm S (Selection sampling technique), in section 3.4.2 of The Art of Computer Programming by Donald E. Knuth Volume 2 / Seminumerical algorithms Third Edition, Addison-Wesley, 1997. """ mmax=n*(n-1)/2 if m>=mmax: G=complete_graph(n) else: G=empty_graph(n) G.name="dense_gnm_random_graph(%s,%s)"%(n,m) if n==1 or m>=mmax: return G if seed is not None: random.seed(seed) u=0 v=1 t=0 k=0 while True: if random.randrange(mmax-t)<m-k: G.add_edge(u,v) k+=1 if k==m: return G t+=1 v+=1 if v==n: # go to next row of adjacency matrix u+=1 v=u+1
def random_shell_graph(constructor, seed=None): """ Return a random shell graph for the constructor given. - constructor: a list of three-tuples [(n1,m1,d1),(n2,m2,d2),..] one for each shell, starting at the center shell. - n : the number of nodes in the shell - m : the number or edges in the shell - d : the ratio of inter (next) shell edges to intra shell edges. d=0 means no intra shell edges. d=1 for the last shell - `seed`: seed for random number generator (default=None) >>> constructor=[(10,20,0.8),(20,40,0.8)] >>> G=nx.random_shell_graph(constructor) """ G = empty_graph(0) G.name = "random_shell_graph(constructor)" if seed is not None: random.seed(seed) glist = [] intra_edges = [] nnodes = 0 # create gnm graphs for each shell for (n, m, d) in constructor: inter_edges = int(m * d) intra_edges.append(m - inter_edges) g = networkx_v099.operators.convert_node_labels_to_integers( gnm_random_graph(n, inter_edges), first_label=nnodes) glist.append(g) nnodes += n G = networkx_v099.operators.union(G, g) # connect the shells randomly for gi in range(len(glist) - 1): nlist1 = glist[gi].nodes() nlist2 = glist[gi + 1].nodes() total_edges = intra_edges[gi] edge_count = 0 while edge_count < total_edges: u = random.choice(nlist1) v = random.choice(nlist2) if u == v or G.has_edge(u, v): continue else: G.add_edge(u, v) edge_count = edge_count + 1 return G
def dense_gnm_random_graph(n, m, seed=None): """ Return the random graph G_{n,m}. Gives a graph picked randomly out of the set of all graphs with n nodes and m edges. This algorithm should be faster than gnm_random_graph for dense graphs. :Parameters: - `n`: the number of nodes - `m`: the number of edges - `seed`: seed for random number generator (default=None) Algorithm by Keith M. Briggs Mar 31, 2006. Inspired by Knuth's Algorithm S (Selection sampling technique), in section 3.4.2 of The Art of Computer Programming by Donald E. Knuth Volume 2 / Seminumerical algorithms Third Edition, Addison-Wesley, 1997. """ mmax = n * (n - 1) / 2 if m >= mmax: G = complete_graph(n) else: G = empty_graph(n) G.name = "dense_gnm_random_graph(%s,%s)" % (n, m) if n == 1 or m >= mmax: return G if seed is not None: random.seed(seed) u = 0 v = 1 t = 0 k = 0 while True: if random.randrange(mmax - t) < m - k: G.add_edge(u, v) k += 1 if k == m: return G t += 1 v += 1 if v == n: # go to next row of adjacency matrix u += 1 v = u + 1
def sedgewick_maze_graph(): """ Return a small maze with a cycle. This is the maze used in Sedgewick,3rd Edition, Part 5, Graph Algorithms, Chapter 18, e.g. Figure 18.2 and following. Nodes are numbered 0,..,7 """ G=empty_graph(0) G.add_nodes_from(range(8)) G.add_edges_from([[0,2],[0,7],[0,5]]) G.add_edges_from([[1,7],[2,6]]) G.add_edges_from([[3,4],[3,5]]) G.add_edges_from([[4,5],[4,7],[4,6]]) G.name="Sedgewick Maze" return G
def sedgewick_maze_graph(): """ Return a small maze with a cycle. This is the maze used in Sedgewick,3rd Edition, Part 5, Graph Algorithms, Chapter 18, e.g. Figure 18.2 and following. Nodes are numbered 0,..,7 """ G = empty_graph(0) G.add_nodes_from(range(8)) G.add_edges_from([[0, 2], [0, 7], [0, 5]]) G.add_edges_from([[1, 7], [2, 6]]) G.add_edges_from([[3, 4], [3, 5]]) G.add_edges_from([[4, 5], [4, 7], [4, 6]]) G.name = "Sedgewick Maze" return G
def connected_smax_graph(degree_seq): """ Not implemented. """ # incomplete implementation if not is_valid_degree_sequence(degree_seq): raise networkx_v099.NetworkXError, 'Invalid degree sequence' # build dictionary of node id and degree, sorted by degree, largest first degree_seq.sort() degree_seq.reverse() ddict=dict(zip(xrange(len(degree_seq)),degree_seq)) G=empty_graph(1) # start with single node return False
def watts_strogatz_graph(n, k, p, seed=None): """ Return a Watts-Strogatz small world graph. First create a ring over n nodes. Then each node in the ring is connected with its k nearest neighbors (k-1 neighbors if k is odd). Then shortcuts are created by rewiring existing edges as follows: for each edge u-v in the underlying "n-ring with k nearest neighbors" with probability p replace u-v with a new edge u-w with randomly-chosen existing node w. In contrast with newman_watts_strogatz_graph(), the random rewiring does not increase the number of edges. :Parameters: - `n`: the number of nodes - `k`: each node is connected to k neighbors in the ring topology - `p`: the probability of rewiring an edge - `seed`: seed for random number generator (default=None) """ if seed is not None: random.seed(seed) G = empty_graph(n) G.name = "watts_strogatz_graph(%s,%s,%s)" % (n, k, p) nlist = G.nodes() fromv = nlist # connect the k/2 neighbors for n in range(1, k / 2 + 1): tov = fromv[n:] + fromv[0:n] # the first n are now last for i in range(len(fromv)): G.add_edge(fromv[i], tov[i]) # for each edge u-v, with probability p, randomly replace with # edge u-w e = G.edges() for (u, v) in e: if random.random() < p: newv = random.choice(nlist) # avoid self-loops and reject if edge u-newv exists # is that the correct WS model? while newv == u or G.has_edge(u, newv): newv = random.choice(nlist) G.delete_edge(u, v) # conserve number of edges G.add_edge(u, newv) return G
def watts_strogatz_graph(n, k, p, seed=None): """ Return a Watts-Strogatz small world graph. First create a ring over n nodes. Then each node in the ring is connected with its k nearest neighbors (k-1 neighbors if k is odd). Then shortcuts are created by rewiring existing edges as follows: for each edge u-v in the underlying "n-ring with k nearest neighbors" with probability p replace u-v with a new edge u-w with randomly-chosen existing node w. In contrast with newman_watts_strogatz_graph(), the random rewiring does not increase the number of edges. :Parameters: - `n`: the number of nodes - `k`: each node is connected to k neighbors in the ring topology - `p`: the probability of rewiring an edge - `seed`: seed for random number generator (default=None) """ if seed is not None: random.seed(seed) G=empty_graph(n) G.name="watts_strogatz_graph(%s,%s,%s)"%(n,k,p) nlist = G.nodes() fromv = nlist # connect the k/2 neighbors for n in range(1, k/2+1): tov = fromv[n:] + fromv[0:n] # the first n are now last for i in range(len(fromv)): G.add_edge(fromv[i], tov[i]) # for each edge u-v, with probability p, randomly replace with # edge u-w e = G.edges() for (u, v) in e: if random.random() < p: newv = random.choice(nlist) # avoid self-loops and reject if edge u-newv exists # is that the correct WS model? while newv == u or G.has_edge(u, newv): newv = random.choice(nlist) G.delete_edge(u,v) # conserve number of edges G.add_edge(u,newv) return G
def fast_gnp_random_graph(n, p, seed=None): """ Return a random graph G_{n,p}. The G_{n,p} graph choses each of the possible [n(n-1)]/2 edges with probability p. Sometimes called Erdős-Rényi graph, or binomial graph. :Parameters: - `n`: the number of nodes - `p`: probability for edge creation - `seed`: seed for random number generator (default=None) This algorithm is O(n+m) where m is the expected number of edges m=p*n*(n-1)/2. It should be faster than gnp_random_graph when p is small, and the expected number of edges is small, (sparse graph). See: Batagelj and Brandes, "Efficient generation of large random networks", Phys. Rev. E, 71, 036113, 2005. """ G = empty_graph(n) G.name = "fast_gnp_random_graph(%s,%s)" % (n, p) if not seed is None: random.seed(seed) v = 1 # Nodes in graph are from 0,n-1 (this is the second node index). w = -1 lp = math.log(1.0 - p) while v < n: lr = math.log(1.0 - random.random()) w = w + 1 + int(lr / lp) while w >= v and v < n: w = w - v v = v + 1 if v < n: G.add_edge(v, w) return G
def fast_gnp_random_graph(n, p, seed=None): """ Return a random graph G_{n,p}. The G_{n,p} graph choses each of the possible [n(n-1)]/2 edges with probability p. Sometimes called Erdős-Rényi graph, or binomial graph. :Parameters: - `n`: the number of nodes - `p`: probability for edge creation - `seed`: seed for random number generator (default=None) This algorithm is O(n+m) where m is the expected number of edges m=p*n*(n-1)/2. It should be faster than gnp_random_graph when p is small, and the expected number of edges is small, (sparse graph). See: Batagelj and Brandes, "Efficient generation of large random networks", Phys. Rev. E, 71, 036113, 2005. """ G=empty_graph(n) G.name="fast_gnp_random_graph(%s,%s)"%(n,p) if not seed is None: random.seed(seed) v=1 # Nodes in graph are from 0,n-1 (this is the second node index). w=-1 lp=math.log(1.0-p) while v<n: lr=math.log(1.0-random.random()) w=w+1+int(lr/lp) while w>=v and v<n: w=w-v v=v+1 if v<n: G.add_edge(v,w) return G
def gnr_graph(n, p, seed=None): """Return the GNR (growing network with redirection) digraph with n nodes and redirection probability p. The graph is built by adding nodes one at a time with a link to one previously added node. The previous target node is chosen uniformly at random. With probabiliy p the link is instead "redirected" to the successor node of the target. The graph is always a (directed) tree. Example: >>> D=nx.gnr_graph(10,0.5) # the GNR graph >>> G=D.to_undirected() # the undirected version Reference:: @article{krapivsky-2001-organization, title = {Organization of Growing Random Networks}, author = {P. L. Krapivsky and S. Redner}, journal = {Phys. Rev. E}, volume = {63}, pages = {066123}, year = {2001}, } """ G = empty_graph(1, create_using=networkx_v099.DiGraph()) G.name = "gnr_graph(%s,%s)" % (n, p) if not seed is None: random.seed(seed) if n == 1: return G for source in range(1, n): target = random.randrange(0, source) if random.random() < p and target != 0: target = G.successors(target)[0] G.add_edge(source, target) return G
def gnr_graph(n,p,seed=None): """Return the GNR (growing network with redirection) digraph with n nodes and redirection probability p. The graph is built by adding nodes one at a time with a link to one previously added node. The previous target node is chosen uniformly at random. With probabiliy p the link is instead "redirected" to the successor node of the target. The graph is always a (directed) tree. Example: >>> D=nx.gnr_graph(10,0.5) # the GNR graph >>> G=D.to_undirected() # the undirected version Reference:: @article{krapivsky-2001-organization, title = {Organization of Growing Random Networks}, author = {P. L. Krapivsky and S. Redner}, journal = {Phys. Rev. E}, volume = {63}, pages = {066123}, year = {2001}, } """ G=empty_graph(1,create_using=networkx_v099.DiGraph()) G.name="gnr_graph(%s,%s)"%(n,p) if not seed is None: random.seed(seed) if n==1: return G for source in range(1,n): target=random.randrange(0,source) if random.random() < p and target !=0: target=G.successors(target)[0] G.add_edge(source,target) return G
def gnm_random_graph(n, m, seed=None): """ Return the random graph G_{n,m}. Gives a graph picked randomly out of the set of all graphs with n nodes and m edges. :Parameters: - `n`: the number of nodes - `m`: the number of edges - `seed`: seed for random number generator (default=None) """ G=empty_graph(n) G.name="gnm_random_graph(%s,%s)"%(n,m) if seed is not None: random.seed(seed) if n==1: return G if m>=n*(n-1)/2: return complete_graph(n) nlist=G.nodes() edge_count=0 while edge_count < m: # generate random edge,u,v u = random.choice(nlist) v = random.choice(nlist) if u==v or G.has_edge(u,v): continue # is this faster? # (u,v)=random.sample(nlist,2) # if G.has_edge(u,v): # continue else: G.add_edge(u,v) edge_count=edge_count+1 return G
def gnm_random_graph(n, m, seed=None): """ Return the random graph G_{n,m}. Gives a graph picked randomly out of the set of all graphs with n nodes and m edges. :Parameters: - `n`: the number of nodes - `m`: the number of edges - `seed`: seed for random number generator (default=None) """ G = empty_graph(n) G.name = "gnm_random_graph(%s,%s)" % (n, m) if seed is not None: random.seed(seed) if n == 1: return G if m >= n * (n - 1) / 2: return complete_graph(n) nlist = G.nodes() edge_count = 0 while edge_count < m: # generate random edge,u,v u = random.choice(nlist) v = random.choice(nlist) if u == v or G.has_edge(u, v): continue # is this faster? # (u,v)=random.sample(nlist,2) # if G.has_edge(u,v): # continue else: G.add_edge(u, v) edge_count = edge_count + 1 return G
def newman_watts_strogatz_graph(n, k, p, seed=None): """ Return a Newman-Watts-Strogatz small world graph. First create a ring over n nodes. Then each node in the ring is connected with its k nearest neighbors (k-1 neighbors if k is odd). Then shortcuts are created by adding new edges as follows: for each edge u-v in the underlying "n-ring with k nearest neighbors" with probability p add a new edge u-w with randomly-chosen existing node w. In contrast with watts_strogatz_graph(), no edges are removed. Parameters ---------- n : int The number of nodes k : int Each node is connected to k nearest neighbors in ring topology p : float The probability of adding a new edge for each edge seed : int seed for random number generator (default=None) Notes ----- @ARTICLE{newman-1999-263, author = {M.~E.~J. Newman and D.~J. Watts}, title = {Renormalization group analysis of the small-world network model}, journal = {Physics Letters A}, volume = {263}, pages = {341}, url = {http://www.citebase.org/abstract?id=oai:arXiv.org:cond-mat/9903357}, year = {1999} } """ if seed is not None: random.seed(seed) G = empty_graph(n) G.name = "newman_watts_strogatz_graph(%s,%s,%s)" % (n, k, p) nlist = G.nodes() fromv = nlist # connect the k/2 neighbors for n in range(1, k / 2 + 1): tov = fromv[n:] + fromv[0:n] # the first n are now last for i in range(len(fromv)): G.add_edge(fromv[i], tov[i]) # for each edge u-v, with probability p, randomly select existing # node w and add new edge u-w e = G.edges() for (u, v) in e: if random.random() < p: w = random.choice(nlist) # no self-loops and reject if edge u-w exists # is that the correct NWS model? while w == u or G.has_edge(u, w): w = random.choice(nlist) G.add_edge(u, w) return G
def random_regular_graph(d, n, seed=None): """Return a random regular graph of n nodes each with degree d, G_{n,d}. Return False if unsuccessful. n*d must be even Nodes are numbered 0...n-1. To get a uniform sample from the space of random graphs you should chose d<n^{1/3}. For algorith see Kim and Vu's paper. Reference:: @inproceedings{kim-2003-generating, author = {Jeong Han Kim and Van H. Vu}, title = {Generating random regular graphs}, booktitle = {Proceedings of the thirty-fifth ACM symposium on Theory of computing}, year = {2003}, isbn = {1-58113-674-9}, pages = {213--222}, location = {San Diego, CA, USA}, doi = {http://doi.acm.org/10.1145/780542.780576}, publisher = {ACM Press}, } The algorithm is based on an earlier paper:: @misc{ steger-1999-generating, author = "A. Steger and N. Wormald", title = "Generating random regular graphs quickly", text = "Probability and Computing 8 (1999), 377-396.", year = "1999", url = "citeseer.ist.psu.edu/steger99generating.html", } """ # helper subroutine to check for suitable edges def _suitable(stubs): for s in stubs: for t in stubs: if not seen_edges[s].has_key(t) and s != t: return True # else no suitable possible edges return False if not n * d % 2 == 0: raise networkx_v099.NetworkXError, "n * d must be even" if seed is not None: random.seed(seed) G = empty_graph(n) G.name = "random_regular_graph(%s,%s)" % (d, n) # keep track of the edges we have seen seen_edges = {} [seen_edges.setdefault(v, {}) for v in range(n)] vv = [[v] * d for v in range(n)] # List of degree-repeated vertex numbers stubs = reduce(lambda x, y: x + y, vv) # flatten the list of lists to a list while len(stubs) > 0: source = random.choice(stubs) target = random.choice(stubs) if source != target and not seen_edges[source].has_key(target): stubs.remove(source) stubs.remove(target) seen_edges[source][target] = 1 seen_edges[target][source] = 1 G.add_edge(source, target) else: # further check to see if suitable s = _suitable(stubs) if not s: return False return G
def powerlaw_cluster_graph(n, m, p, seed=None): """ Holme and Kim algorithm for growing graphs with powerlaw degree distribution and approximate average clustering. :Parameters: - `n`: the number of nodes - `m`: the number of random edges to add for each new node - `p`: probability of adding a triangle after adding a random edge - `seed`: seed for random number generator (default=None) Reference:: @Article{growing-holme-2002, author = {P. Holme and B. J. Kim}, title = {Growing scale-free networks with tunable clustering}, journal = {Phys. Rev. E}, year = {2002}, volume = {65}, number = {2}, pages = {026107}, } The average clustering has a hard time getting above a certain cutoff that depends on m. This cutoff is often quite low. Note that the transitivity (fraction of triangles to possible triangles) seems to go down with network size. It is essentially the Barabási-Albert growth model with an extra step that each random edge is followed by a chance of making an edge to one of its neighbors too (and thus a triangle). This algorithm improves on B-A in the sense that it enables a higher average clustering to be attained if desired. It seems possible to have a disconnected graph with this algorithm since the initial m nodes may not be all linked to a new node on the first iteration like the BA model. """ if m < 1 or n < m: raise networkx_v099.NetworkXError,\ "NetworkXError must have m>1 and m<n, m=%d,n=%d"%(m,n) if p > 1 or p < 0: raise networkx_v099.NetworkXError,\ "NetworkXError p must be in [0,1], p=%f"%(p) if seed is not None: random.seed(seed) G=empty_graph(m) # add m initial nodes (m0 in barabasi-speak) G.name="Powerlaw-Cluster Graph" repeated_nodes=G.nodes() # list of existing nodes to sample from # with nodes repeated once for each adjacent edge source=m # next node is m while source<n: # Now add the other n-1 nodes possible_targets=random.sample(repeated_nodes,m) # do one preferential attachment for new node target=possible_targets.pop() G.add_edge(source,target) repeated_nodes.append(target) # add one node to list for each new link count=1 while count<m: # add m-1 more new links if random.random()<p: # clustering step: add triangle neighborhood=[nbr for nbr in G.neighbors(target) \ if not G.has_edge(source,nbr) \ and not nbr==source] if neighborhood: # if there is a neighbor without a link nbr=random.choice(neighborhood) G.add_edge(source,nbr) # add triangle repeated_nodes.append(nbr) count=count+1 continue # go to top of while loop # else do preferential attachment step if above fails target=possible_targets.pop() G.add_edge(source,target) repeated_nodes.append(target) count=count+1 repeated_nodes.extend([source]*m) # add source node to list m times source += 1 return G
def powerlaw_cluster_graph(n, m, p, seed=None): """ Holme and Kim algorithm for growing graphs with powerlaw degree distribution and approximate average clustering. :Parameters: - `n`: the number of nodes - `m`: the number of random edges to add for each new node - `p`: probability of adding a triangle after adding a random edge - `seed`: seed for random number generator (default=None) Reference:: @Article{growing-holme-2002, author = {P. Holme and B. J. Kim}, title = {Growing scale-free networks with tunable clustering}, journal = {Phys. Rev. E}, year = {2002}, volume = {65}, number = {2}, pages = {026107}, } The average clustering has a hard time getting above a certain cutoff that depends on m. This cutoff is often quite low. Note that the transitivity (fraction of triangles to possible triangles) seems to go down with network size. It is essentially the Barabási-Albert growth model with an extra step that each random edge is followed by a chance of making an edge to one of its neighbors too (and thus a triangle). This algorithm improves on B-A in the sense that it enables a higher average clustering to be attained if desired. It seems possible to have a disconnected graph with this algorithm since the initial m nodes may not be all linked to a new node on the first iteration like the BA model. """ if m < 1 or n < m: raise networkx_v099.NetworkXError,\ "NetworkXError must have m>1 and m<n, m=%d,n=%d"%(m,n) if p > 1 or p < 0: raise networkx_v099.NetworkXError,\ "NetworkXError p must be in [0,1], p=%f"%(p) if seed is not None: random.seed(seed) G = empty_graph(m) # add m initial nodes (m0 in barabasi-speak) G.name = "Powerlaw-Cluster Graph" repeated_nodes = G.nodes() # list of existing nodes to sample from # with nodes repeated once for each adjacent edge source = m # next node is m while source < n: # Now add the other n-1 nodes possible_targets = random.sample(repeated_nodes, m) # do one preferential attachment for new node target = possible_targets.pop() G.add_edge(source, target) repeated_nodes.append(target) # add one node to list for each new link count = 1 while count < m: # add m-1 more new links if random.random() < p: # clustering step: add triangle neighborhood=[nbr for nbr in G.neighbors(target) \ if not G.has_edge(source,nbr) \ and not nbr==source] if neighborhood: # if there is a neighbor without a link nbr = random.choice(neighborhood) G.add_edge(source, nbr) # add triangle repeated_nodes.append(nbr) count = count + 1 continue # go to top of while loop # else do preferential attachment step if above fails target = possible_targets.pop() G.add_edge(source, target) repeated_nodes.append(target) count = count + 1 repeated_nodes.extend([source] * m) # add source node to list m times source += 1 return G
def random_regular_graph(d, n, seed=None): """Return a random regular graph of n nodes each with degree d, G_{n,d}. Return False if unsuccessful. n*d must be even Nodes are numbered 0...n-1. To get a uniform sample from the space of random graphs you should chose d<n^{1/3}. For algorith see Kim and Vu's paper. Reference:: @inproceedings{kim-2003-generating, author = {Jeong Han Kim and Van H. Vu}, title = {Generating random regular graphs}, booktitle = {Proceedings of the thirty-fifth ACM symposium on Theory of computing}, year = {2003}, isbn = {1-58113-674-9}, pages = {213--222}, location = {San Diego, CA, USA}, doi = {http://doi.acm.org/10.1145/780542.780576}, publisher = {ACM Press}, } The algorithm is based on an earlier paper:: @misc{ steger-1999-generating, author = "A. Steger and N. Wormald", title = "Generating random regular graphs quickly", text = "Probability and Computing 8 (1999), 377-396.", year = "1999", url = "citeseer.ist.psu.edu/steger99generating.html", } """ # helper subroutine to check for suitable edges def _suitable(stubs): for s in stubs: for t in stubs: if not seen_edges[s].has_key(t) and s!=t: return True # else no suitable possible edges return False if not n*d%2==0: raise networkx_v099.NetworkXError, "n * d must be even" if seed is not None: random.seed(seed) G=empty_graph(n) G.name="random_regular_graph(%s,%s)"%(d,n) # keep track of the edges we have seen seen_edges={} [seen_edges.setdefault(v,{}) for v in range(n)] vv=[ [v]*d for v in range(n)] # List of degree-repeated vertex numbers stubs=reduce(lambda x,y: x+y ,vv) # flatten the list of lists to a list while len(stubs) > 0: source=random.choice(stubs) target=random.choice(stubs) if source!=target and not seen_edges[source].has_key(target): stubs.remove(source) stubs.remove(target) seen_edges[source][target]=1 seen_edges[target][source]=1 G.add_edge(source,target) else: # further check to see if suitable s=_suitable(stubs) if not s: return False return G
def newman_watts_strogatz_graph(n, k, p, seed=None): """ Return a Newman-Watts-Strogatz small world graph. First create a ring over n nodes. Then each node in the ring is connected with its k nearest neighbors (k-1 neighbors if k is odd). Then shortcuts are created by adding new edges as follows: for each edge u-v in the underlying "n-ring with k nearest neighbors" with probability p add a new edge u-w with randomly-chosen existing node w. In contrast with watts_strogatz_graph(), no edges are removed. Parameters ---------- n : int The number of nodes k : int Each node is connected to k nearest neighbors in ring topology p : float The probability of adding a new edge for each edge seed : int seed for random number generator (default=None) Notes ----- @ARTICLE{newman-1999-263, author = {M.~E.~J. Newman and D.~J. Watts}, title = {Renormalization group analysis of the small-world network model}, journal = {Physics Letters A}, volume = {263}, pages = {341}, url = {http://www.citebase.org/abstract?id=oai:arXiv.org:cond-mat/9903357}, year = {1999} } """ if seed is not None: random.seed(seed) G=empty_graph(n) G.name="newman_watts_strogatz_graph(%s,%s,%s)"%(n,k,p) nlist = G.nodes() fromv = nlist # connect the k/2 neighbors for n in range(1, k/2+1): tov = fromv[n:] + fromv[0:n] # the first n are now last for i in range(len(fromv)): G.add_edge(fromv[i], tov[i]) # for each edge u-v, with probability p, randomly select existing # node w and add new edge u-w e = G.edges() for (u, v) in e: if random.random() < p: w = random.choice(nlist) # no self-loops and reject if edge u-w exists # is that the correct NWS model? while w == u or G.has_edge(u, w): w = random.choice(nlist) G.add_edge(u,w) return G