Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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
Example #5
0
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
Example #6
0
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
Example #7
0
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
Example #8
0
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
Example #9
0
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
Example #10
0
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
Example #11
0
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
Example #12
0
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
Example #13
0
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
Example #14
0
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
Example #15
0
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
Example #16
0
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
Example #17
0
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
Example #18
0
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
Example #19
0
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
Example #20
0
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
Example #21
0
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
Example #22
0
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            
Example #23
0
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
Example #24
0
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
Example #25
0
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
Example #26
0
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
Example #27
0
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
Example #28
0
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
Example #29
0
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
Example #30
0
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
Example #31
0
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
Example #32
0
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
Example #33
0
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
Example #34
0
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