random_clustered.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. """Generate graphs with given degree and triangle sequence.
  2. """
  3. import networkx as nx
  4. from networkx.utils import py_random_state
  5. __all__ = ["random_clustered_graph"]
  6. @py_random_state(2)
  7. def random_clustered_graph(joint_degree_sequence, create_using=None, seed=None):
  8. r"""Generate a random graph with the given joint independent edge degree and
  9. triangle degree sequence.
  10. This uses a configuration model-like approach to generate a random graph
  11. (with parallel edges and self-loops) by randomly assigning edges to match
  12. the given joint degree sequence.
  13. The joint degree sequence is a list of pairs of integers of the form
  14. $[(d_{1,i}, d_{1,t}), \dotsc, (d_{n,i}, d_{n,t})]$. According to this list,
  15. vertex $u$ is a member of $d_{u,t}$ triangles and has $d_{u, i}$ other
  16. edges. The number $d_{u,t}$ is the *triangle degree* of $u$ and the number
  17. $d_{u,i}$ is the *independent edge degree*.
  18. Parameters
  19. ----------
  20. joint_degree_sequence : list of integer pairs
  21. Each list entry corresponds to the independent edge degree and
  22. triangle degree of a node.
  23. create_using : NetworkX graph constructor, optional (default MultiGraph)
  24. Graph type to create. If graph instance, then cleared before populated.
  25. seed : integer, random_state, or None (default)
  26. Indicator of random number generation state.
  27. See :ref:`Randomness<randomness>`.
  28. Returns
  29. -------
  30. G : MultiGraph
  31. A graph with the specified degree sequence. Nodes are labeled
  32. starting at 0 with an index corresponding to the position in
  33. deg_sequence.
  34. Raises
  35. ------
  36. NetworkXError
  37. If the independent edge degree sequence sum is not even
  38. or the triangle degree sequence sum is not divisible by 3.
  39. Notes
  40. -----
  41. As described by Miller [1]_ (see also Newman [2]_ for an equivalent
  42. description).
  43. A non-graphical degree sequence (not realizable by some simple
  44. graph) is allowed since this function returns graphs with self
  45. loops and parallel edges. An exception is raised if the
  46. independent degree sequence does not have an even sum or the
  47. triangle degree sequence sum is not divisible by 3.
  48. This configuration model-like construction process can lead to
  49. duplicate edges and loops. You can remove the self-loops and
  50. parallel edges (see below) which will likely result in a graph
  51. that doesn't have the exact degree sequence specified. This
  52. "finite-size effect" decreases as the size of the graph increases.
  53. References
  54. ----------
  55. .. [1] Joel C. Miller. "Percolation and epidemics in random clustered
  56. networks". In: Physical review. E, Statistical, nonlinear, and soft
  57. matter physics 80 (2 Part 1 August 2009).
  58. .. [2] M. E. J. Newman. "Random Graphs with Clustering".
  59. In: Physical Review Letters 103 (5 July 2009)
  60. Examples
  61. --------
  62. >>> deg = [(1, 0), (1, 0), (1, 0), (2, 0), (1, 0), (2, 1), (0, 1), (0, 1)]
  63. >>> G = nx.random_clustered_graph(deg)
  64. To remove parallel edges:
  65. >>> G = nx.Graph(G)
  66. To remove self loops:
  67. >>> G.remove_edges_from(nx.selfloop_edges(G))
  68. """
  69. # In Python 3, zip() returns an iterator. Make this into a list.
  70. joint_degree_sequence = list(joint_degree_sequence)
  71. N = len(joint_degree_sequence)
  72. G = nx.empty_graph(N, create_using, default=nx.MultiGraph)
  73. if G.is_directed():
  74. raise nx.NetworkXError("Directed Graph not supported")
  75. ilist = []
  76. tlist = []
  77. for n in G:
  78. degrees = joint_degree_sequence[n]
  79. for icount in range(degrees[0]):
  80. ilist.append(n)
  81. for tcount in range(degrees[1]):
  82. tlist.append(n)
  83. if len(ilist) % 2 != 0 or len(tlist) % 3 != 0:
  84. raise nx.NetworkXError("Invalid degree sequence")
  85. seed.shuffle(ilist)
  86. seed.shuffle(tlist)
  87. while ilist:
  88. G.add_edge(ilist.pop(), ilist.pop())
  89. while tlist:
  90. n1 = tlist.pop()
  91. n2 = tlist.pop()
  92. n3 = tlist.pop()
  93. G.add_edges_from([(n1, n2), (n1, n3), (n2, n3)])
  94. return G