dominance.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. """
  2. Dominance algorithms.
  3. """
  4. from functools import reduce
  5. import networkx as nx
  6. from networkx.utils import not_implemented_for
  7. __all__ = ["immediate_dominators", "dominance_frontiers"]
  8. @not_implemented_for("undirected")
  9. def immediate_dominators(G, start):
  10. """Returns the immediate dominators of all nodes of a directed graph.
  11. Parameters
  12. ----------
  13. G : a DiGraph or MultiDiGraph
  14. The graph where dominance is to be computed.
  15. start : node
  16. The start node of dominance computation.
  17. Returns
  18. -------
  19. idom : dict keyed by nodes
  20. A dict containing the immediate dominators of each node reachable from
  21. `start`.
  22. Raises
  23. ------
  24. NetworkXNotImplemented
  25. If `G` is undirected.
  26. NetworkXError
  27. If `start` is not in `G`.
  28. Notes
  29. -----
  30. Except for `start`, the immediate dominators are the parents of their
  31. corresponding nodes in the dominator tree.
  32. Examples
  33. --------
  34. >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)])
  35. >>> sorted(nx.immediate_dominators(G, 1).items())
  36. [(1, 1), (2, 1), (3, 1), (4, 3), (5, 1)]
  37. References
  38. ----------
  39. .. [1] K. D. Cooper, T. J. Harvey, and K. Kennedy.
  40. A simple, fast dominance algorithm.
  41. Software Practice & Experience, 4:110, 2001.
  42. """
  43. if start not in G:
  44. raise nx.NetworkXError("start is not in G")
  45. idom = {start: start}
  46. order = list(nx.dfs_postorder_nodes(G, start))
  47. dfn = {u: i for i, u in enumerate(order)}
  48. order.pop()
  49. order.reverse()
  50. def intersect(u, v):
  51. while u != v:
  52. while dfn[u] < dfn[v]:
  53. u = idom[u]
  54. while dfn[u] > dfn[v]:
  55. v = idom[v]
  56. return u
  57. changed = True
  58. while changed:
  59. changed = False
  60. for u in order:
  61. new_idom = reduce(intersect, (v for v in G.pred[u] if v in idom))
  62. if u not in idom or idom[u] != new_idom:
  63. idom[u] = new_idom
  64. changed = True
  65. return idom
  66. def dominance_frontiers(G, start):
  67. """Returns the dominance frontiers of all nodes of a directed graph.
  68. Parameters
  69. ----------
  70. G : a DiGraph or MultiDiGraph
  71. The graph where dominance is to be computed.
  72. start : node
  73. The start node of dominance computation.
  74. Returns
  75. -------
  76. df : dict keyed by nodes
  77. A dict containing the dominance frontiers of each node reachable from
  78. `start` as lists.
  79. Raises
  80. ------
  81. NetworkXNotImplemented
  82. If `G` is undirected.
  83. NetworkXError
  84. If `start` is not in `G`.
  85. Examples
  86. --------
  87. >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)])
  88. >>> sorted((u, sorted(df)) for u, df in nx.dominance_frontiers(G, 1).items())
  89. [(1, []), (2, [5]), (3, [5]), (4, [5]), (5, [])]
  90. References
  91. ----------
  92. .. [1] K. D. Cooper, T. J. Harvey, and K. Kennedy.
  93. A simple, fast dominance algorithm.
  94. Software Practice & Experience, 4:110, 2001.
  95. """
  96. idom = nx.immediate_dominators(G, start)
  97. df = {u: set() for u in idom}
  98. for u in idom:
  99. if len(G.pred[u]) >= 2:
  100. for v in G.pred[u]:
  101. if v in idom:
  102. while v != idom[u]:
  103. df[v].add(u)
  104. v = idom[v]
  105. return df