edmondskarp.py 7.9 KB

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