rcm.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. """
  2. Cuthill-McKee ordering of graph nodes to produce sparse matrices
  3. """
  4. from collections import deque
  5. from operator import itemgetter
  6. import networkx as nx
  7. from ..utils import arbitrary_element
  8. __all__ = ["cuthill_mckee_ordering", "reverse_cuthill_mckee_ordering"]
  9. def cuthill_mckee_ordering(G, heuristic=None):
  10. """Generate an ordering (permutation) of the graph nodes to make
  11. a sparse matrix.
  12. Uses the Cuthill-McKee heuristic (based on breadth-first search) [1]_.
  13. Parameters
  14. ----------
  15. G : graph
  16. A NetworkX graph
  17. heuristic : function, optional
  18. Function to choose starting node for RCM algorithm. If None
  19. a node from a pseudo-peripheral pair is used. A user-defined function
  20. can be supplied that takes a graph object and returns a single node.
  21. Returns
  22. -------
  23. nodes : generator
  24. Generator of nodes in Cuthill-McKee ordering.
  25. Examples
  26. --------
  27. >>> from networkx.utils import cuthill_mckee_ordering
  28. >>> G = nx.path_graph(4)
  29. >>> rcm = list(cuthill_mckee_ordering(G))
  30. >>> A = nx.adjacency_matrix(G, nodelist=rcm)
  31. Smallest degree node as heuristic function:
  32. >>> def smallest_degree(G):
  33. ... return min(G, key=G.degree)
  34. >>> rcm = list(cuthill_mckee_ordering(G, heuristic=smallest_degree))
  35. See Also
  36. --------
  37. reverse_cuthill_mckee_ordering
  38. Notes
  39. -----
  40. The optimal solution the bandwidth reduction is NP-complete [2]_.
  41. References
  42. ----------
  43. .. [1] E. Cuthill and J. McKee.
  44. Reducing the bandwidth of sparse symmetric matrices,
  45. In Proc. 24th Nat. Conf. ACM, pages 157-172, 1969.
  46. http://doi.acm.org/10.1145/800195.805928
  47. .. [2] Steven S. Skiena. 1997. The Algorithm Design Manual.
  48. Springer-Verlag New York, Inc., New York, NY, USA.
  49. """
  50. for c in nx.connected_components(G):
  51. yield from connected_cuthill_mckee_ordering(G.subgraph(c), heuristic)
  52. def reverse_cuthill_mckee_ordering(G, heuristic=None):
  53. """Generate an ordering (permutation) of the graph nodes to make
  54. a sparse matrix.
  55. Uses the reverse Cuthill-McKee heuristic (based on breadth-first search)
  56. [1]_.
  57. Parameters
  58. ----------
  59. G : graph
  60. A NetworkX graph
  61. heuristic : function, optional
  62. Function to choose starting node for RCM algorithm. If None
  63. a node from a pseudo-peripheral pair is used. A user-defined function
  64. can be supplied that takes a graph object and returns a single node.
  65. Returns
  66. -------
  67. nodes : generator
  68. Generator of nodes in reverse Cuthill-McKee ordering.
  69. Examples
  70. --------
  71. >>> from networkx.utils import reverse_cuthill_mckee_ordering
  72. >>> G = nx.path_graph(4)
  73. >>> rcm = list(reverse_cuthill_mckee_ordering(G))
  74. >>> A = nx.adjacency_matrix(G, nodelist=rcm)
  75. Smallest degree node as heuristic function:
  76. >>> def smallest_degree(G):
  77. ... return min(G, key=G.degree)
  78. >>> rcm = list(reverse_cuthill_mckee_ordering(G, heuristic=smallest_degree))
  79. See Also
  80. --------
  81. cuthill_mckee_ordering
  82. Notes
  83. -----
  84. The optimal solution the bandwidth reduction is NP-complete [2]_.
  85. References
  86. ----------
  87. .. [1] E. Cuthill and J. McKee.
  88. Reducing the bandwidth of sparse symmetric matrices,
  89. In Proc. 24th Nat. Conf. ACM, pages 157-72, 1969.
  90. http://doi.acm.org/10.1145/800195.805928
  91. .. [2] Steven S. Skiena. 1997. The Algorithm Design Manual.
  92. Springer-Verlag New York, Inc., New York, NY, USA.
  93. """
  94. return reversed(list(cuthill_mckee_ordering(G, heuristic=heuristic)))
  95. def connected_cuthill_mckee_ordering(G, heuristic=None):
  96. # the cuthill mckee algorithm for connected graphs
  97. if heuristic is None:
  98. start = pseudo_peripheral_node(G)
  99. else:
  100. start = heuristic(G)
  101. visited = {start}
  102. queue = deque([start])
  103. while queue:
  104. parent = queue.popleft()
  105. yield parent
  106. nd = sorted(G.degree(set(G[parent]) - visited), key=itemgetter(1))
  107. children = [n for n, d in nd]
  108. visited.update(children)
  109. queue.extend(children)
  110. def pseudo_peripheral_node(G):
  111. # helper for cuthill-mckee to find a node in a "pseudo peripheral pair"
  112. # to use as good starting node
  113. u = arbitrary_element(G)
  114. lp = 0
  115. v = u
  116. while True:
  117. spl = dict(nx.shortest_path_length(G, v))
  118. l = max(spl.values())
  119. if l <= lp:
  120. break
  121. lp = l
  122. farthest = (n for n, dist in spl.items() if dist == l)
  123. v, deg = min(G.degree(farthest), key=itemgetter(1))
  124. return v