dominance.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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. @nx._dispatchable
  10. def immediate_dominators(G, start):
  11. """Returns the immediate dominators of all nodes of a directed graph.
  12. Parameters
  13. ----------
  14. G : a DiGraph or MultiDiGraph
  15. The graph where dominance is to be computed.
  16. start : node
  17. The start node of dominance computation.
  18. Returns
  19. -------
  20. idom : dict keyed by nodes
  21. A dict containing the immediate dominators of each node reachable from
  22. `start`, except for `start` itself.
  23. Raises
  24. ------
  25. NetworkXNotImplemented
  26. If `G` is undirected.
  27. NetworkXError
  28. If `start` is not in `G`.
  29. Notes
  30. -----
  31. The immediate dominators are the parents of their corresponding nodes in
  32. the dominator tree. Every node reachable from `start` has an immediate
  33. dominator, except for `start` itself.
  34. Examples
  35. --------
  36. >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)])
  37. >>> sorted(nx.immediate_dominators(G, 1).items())
  38. [(2, 1), (3, 1), (4, 3), (5, 1)]
  39. References
  40. ----------
  41. .. [1] Cooper, Keith D., Harvey, Timothy J. and Kennedy, Ken.
  42. "A simple, fast dominance algorithm." (2006).
  43. https://hdl.handle.net/1911/96345
  44. .. [2] Lengauer, Thomas; Tarjan, Robert Endre (July 1979).
  45. "A fast algorithm for finding dominators in a flowgraph".
  46. ACM Transactions on Programming Languages and Systems. 1 (1): 121--141.
  47. https://dl.acm.org/doi/10.1145/357062.357071
  48. """
  49. if start not in G:
  50. raise nx.NetworkXError("start is not in G")
  51. idom = {start: None}
  52. order = list(nx.dfs_postorder_nodes(G, start))
  53. dfn = {u: i for i, u in enumerate(order)}
  54. order.pop()
  55. order.reverse()
  56. def intersect(u, v):
  57. while u != v:
  58. while dfn[u] < dfn[v]:
  59. u = idom[u]
  60. while dfn[u] > dfn[v]:
  61. v = idom[v]
  62. return u
  63. changed = True
  64. while changed:
  65. changed = False
  66. for u in order:
  67. new_idom = reduce(intersect, (v for v in G.pred[u] if v in idom))
  68. if u not in idom or idom[u] != new_idom:
  69. idom[u] = new_idom
  70. changed = True
  71. del idom[start]
  72. return idom
  73. @not_implemented_for("undirected")
  74. @nx._dispatchable
  75. def dominance_frontiers(G, start):
  76. """Returns the dominance frontiers of all nodes of a directed graph.
  77. Parameters
  78. ----------
  79. G : a DiGraph or MultiDiGraph
  80. The graph where dominance is to be computed.
  81. start : node
  82. The start node of dominance computation.
  83. Returns
  84. -------
  85. df : dict keyed by nodes
  86. A dict containing the dominance frontiers of each node reachable from
  87. `start` as lists.
  88. Raises
  89. ------
  90. NetworkXNotImplemented
  91. If `G` is undirected.
  92. NetworkXError
  93. If `start` is not in `G`.
  94. Examples
  95. --------
  96. >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)])
  97. >>> sorted((u, sorted(df)) for u, df in nx.dominance_frontiers(G, 1).items())
  98. [(1, []), (2, [5]), (3, [5]), (4, [5]), (5, [])]
  99. References
  100. ----------
  101. .. [1] Cooper, Keith D., Harvey, Timothy J. and Kennedy, Ken.
  102. "A simple, fast dominance algorithm." (2006).
  103. https://hdl.handle.net/1911/96345
  104. """
  105. idom = nx.immediate_dominators(G, start) | {start: None}
  106. df = {u: set() for u in idom}
  107. for u in idom:
  108. if u == start or len(G.pred[u]) >= 2:
  109. for v in G.pred[u]:
  110. if v in idom:
  111. while v != idom[u]:
  112. df[v].add(u)
  113. v = idom[v]
  114. return df