dinitz_alg.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. """
  2. Dinitz' algorithm for maximum flow problems.
  3. """
  4. from collections import deque
  5. import networkx as nx
  6. from networkx.algorithms.flow.utils import build_residual_network
  7. from networkx.utils import pairwise
  8. __all__ = ["dinitz"]
  9. def dinitz(G, s, t, capacity="capacity", residual=None, value_only=False, cutoff=None):
  10. """Find a maximum single-commodity flow using Dinitz' algorithm.
  11. This function returns the residual network resulting after computing
  12. the maximum flow. See below for details about the conventions
  13. NetworkX uses for defining residual networks.
  14. This algorithm has a running time of $O(n^2 m)$ for $n$ nodes and $m$
  15. edges [1]_.
  16. Parameters
  17. ----------
  18. G : NetworkX graph
  19. Edges of the graph are expected to have an attribute called
  20. 'capacity'. If this attribute is not present, the edge is
  21. considered to have infinite capacity.
  22. s : node
  23. Source node for the flow.
  24. t : node
  25. Sink node for the flow.
  26. capacity : string
  27. Edges of the graph G are expected to have an attribute capacity
  28. that indicates how much flow the edge can support. If this
  29. attribute is not present, the edge is considered to have
  30. infinite capacity. Default value: 'capacity'.
  31. residual : NetworkX graph
  32. Residual network on which the algorithm is to be executed. If None, a
  33. new residual network is created. Default value: None.
  34. value_only : bool
  35. If True compute only the value of the maximum flow. This parameter
  36. will be ignored by this algorithm because it is not applicable.
  37. cutoff : integer, float
  38. If specified, the algorithm will terminate when the flow value reaches
  39. or exceeds the cutoff. In this case, it may be unable to immediately
  40. determine a minimum cut. Default value: None.
  41. Returns
  42. -------
  43. R : NetworkX DiGraph
  44. Residual network after computing the maximum flow.
  45. Raises
  46. ------
  47. NetworkXError
  48. The algorithm does not support MultiGraph and MultiDiGraph. If
  49. the input graph is an instance of one of these two classes, a
  50. NetworkXError is raised.
  51. NetworkXUnbounded
  52. If the graph has a path of infinite capacity, the value of a
  53. feasible flow on the graph is unbounded above and the function
  54. raises a NetworkXUnbounded.
  55. See also
  56. --------
  57. :meth:`maximum_flow`
  58. :meth:`minimum_cut`
  59. :meth:`preflow_push`
  60. :meth:`shortest_augmenting_path`
  61. Notes
  62. -----
  63. The residual network :samp:`R` from an input graph :samp:`G` has the
  64. same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
  65. of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
  66. self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
  67. in :samp:`G`.
  68. For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
  69. is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
  70. in :samp:`G` or zero otherwise. If the capacity is infinite,
  71. :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
  72. that does not affect the solution of the problem. This value is stored in
  73. :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
  74. :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
  75. satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
  76. The flow value, defined as the total flow into :samp:`t`, the sink, is
  77. stored in :samp:`R.graph['flow_value']`. If :samp:`cutoff` is not
  78. specified, reachability to :samp:`t` using only edges :samp:`(u, v)` such
  79. that :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
  80. :samp:`s`-:samp:`t` cut.
  81. Examples
  82. --------
  83. >>> from networkx.algorithms.flow import dinitz
  84. The functions that implement flow algorithms and output a residual
  85. network, such as this one, are not imported to the base NetworkX
  86. namespace, so you have to explicitly import them from the flow package.
  87. >>> G = nx.DiGraph()
  88. >>> G.add_edge("x", "a", capacity=3.0)
  89. >>> G.add_edge("x", "b", capacity=1.0)
  90. >>> G.add_edge("a", "c", capacity=3.0)
  91. >>> G.add_edge("b", "c", capacity=5.0)
  92. >>> G.add_edge("b", "d", capacity=4.0)
  93. >>> G.add_edge("d", "e", capacity=2.0)
  94. >>> G.add_edge("c", "y", capacity=2.0)
  95. >>> G.add_edge("e", "y", capacity=3.0)
  96. >>> R = dinitz(G, "x", "y")
  97. >>> flow_value = nx.maximum_flow_value(G, "x", "y")
  98. >>> flow_value
  99. 3.0
  100. >>> flow_value == R.graph["flow_value"]
  101. True
  102. References
  103. ----------
  104. .. [1] Dinitz' Algorithm: The Original Version and Even's Version.
  105. 2006. Yefim Dinitz. In Theoretical Computer Science. Lecture
  106. Notes in Computer Science. Volume 3895. pp 218-240.
  107. https://doi.org/10.1007/11685654_10
  108. """
  109. R = dinitz_impl(G, s, t, capacity, residual, cutoff)
  110. R.graph["algorithm"] = "dinitz"
  111. return R
  112. def dinitz_impl(G, s, t, capacity, residual, cutoff):
  113. if s not in G:
  114. raise nx.NetworkXError(f"node {str(s)} not in graph")
  115. if t not in G:
  116. raise nx.NetworkXError(f"node {str(t)} not in graph")
  117. if s == t:
  118. raise nx.NetworkXError("source and sink are the same node")
  119. if residual is None:
  120. R = build_residual_network(G, capacity)
  121. else:
  122. R = residual
  123. # Initialize/reset the residual network.
  124. for u in R:
  125. for e in R[u].values():
  126. e["flow"] = 0
  127. # Use an arbitrary high value as infinite. It is computed
  128. # when building the residual network.
  129. INF = R.graph["inf"]
  130. if cutoff is None:
  131. cutoff = INF
  132. R_succ = R.succ
  133. R_pred = R.pred
  134. def breath_first_search():
  135. parents = {}
  136. queue = deque([s])
  137. while queue:
  138. if t in parents:
  139. break
  140. u = queue.popleft()
  141. for v in R_succ[u]:
  142. attr = R_succ[u][v]
  143. if v not in parents and attr["capacity"] - attr["flow"] > 0:
  144. parents[v] = u
  145. queue.append(v)
  146. return parents
  147. def depth_first_search(parents):
  148. """Build a path using DFS starting from the sink"""
  149. path = []
  150. u = t
  151. flow = INF
  152. while u != s:
  153. path.append(u)
  154. v = parents[u]
  155. flow = min(flow, R_pred[u][v]["capacity"] - R_pred[u][v]["flow"])
  156. u = v
  157. path.append(s)
  158. # Augment the flow along the path found
  159. if flow > 0:
  160. for u, v in pairwise(path):
  161. R_pred[u][v]["flow"] += flow
  162. R_pred[v][u]["flow"] -= flow
  163. return flow
  164. flow_value = 0
  165. while flow_value < cutoff:
  166. parents = breath_first_search()
  167. if t not in parents:
  168. break
  169. this_flow = depth_first_search(parents)
  170. if this_flow * 2 > INF:
  171. raise nx.NetworkXUnbounded("Infinite capacity path, flow unbounded above.")
  172. flow_value += this_flow
  173. R.graph["flow_value"] = flow_value
  174. return R