preflowpush.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. """
  2. Highest-label preflow-push algorithm for maximum flow problems.
  3. """
  4. from collections import deque
  5. from itertools import islice
  6. import networkx as nx
  7. from ...utils import arbitrary_element
  8. from .utils import (
  9. CurrentEdge,
  10. GlobalRelabelThreshold,
  11. Level,
  12. build_residual_network,
  13. detect_unboundedness,
  14. )
  15. __all__ = ["preflow_push"]
  16. def preflow_push_impl(G, s, t, capacity, residual, global_relabel_freq, value_only):
  17. """Implementation of the highest-label preflow-push algorithm."""
  18. if s not in G:
  19. raise nx.NetworkXError(f"node {str(s)} not in graph")
  20. if t not in G:
  21. raise nx.NetworkXError(f"node {str(t)} not in graph")
  22. if s == t:
  23. raise nx.NetworkXError("source and sink are the same node")
  24. if global_relabel_freq is None:
  25. global_relabel_freq = 0
  26. if global_relabel_freq < 0:
  27. raise nx.NetworkXError("global_relabel_freq must be nonnegative.")
  28. if residual is None:
  29. R = build_residual_network(G, capacity)
  30. else:
  31. R = residual
  32. detect_unboundedness(R, s, t)
  33. R_nodes = R.nodes
  34. R_pred = R.pred
  35. R_succ = R.succ
  36. # Initialize/reset the residual network.
  37. for u in R:
  38. R_nodes[u]["excess"] = 0
  39. for e in R_succ[u].values():
  40. e["flow"] = 0
  41. def reverse_bfs(src):
  42. """Perform a reverse breadth-first search from src in the residual
  43. network.
  44. """
  45. heights = {src: 0}
  46. q = deque([(src, 0)])
  47. while q:
  48. u, height = q.popleft()
  49. height += 1
  50. for v, attr in R_pred[u].items():
  51. if v not in heights and attr["flow"] < attr["capacity"]:
  52. heights[v] = height
  53. q.append((v, height))
  54. return heights
  55. # Initialize heights of the nodes.
  56. heights = reverse_bfs(t)
  57. if s not in heights:
  58. # t is not reachable from s in the residual network. The maximum flow
  59. # must be zero.
  60. R.graph["flow_value"] = 0
  61. return R
  62. n = len(R)
  63. # max_height represents the height of the highest level below level n with
  64. # at least one active node.
  65. max_height = max(heights[u] for u in heights if u != s)
  66. heights[s] = n
  67. grt = GlobalRelabelThreshold(n, R.size(), global_relabel_freq)
  68. # Initialize heights and 'current edge' data structures of the nodes.
  69. for u in R:
  70. R_nodes[u]["height"] = heights[u] if u in heights else n + 1
  71. R_nodes[u]["curr_edge"] = CurrentEdge(R_succ[u])
  72. def push(u, v, flow):
  73. """Push flow units of flow from u to v."""
  74. R_succ[u][v]["flow"] += flow
  75. R_succ[v][u]["flow"] -= flow
  76. R_nodes[u]["excess"] -= flow
  77. R_nodes[v]["excess"] += flow
  78. # The maximum flow must be nonzero now. Initialize the preflow by
  79. # saturating all edges emanating from s.
  80. for u, attr in R_succ[s].items():
  81. flow = attr["capacity"]
  82. if flow > 0:
  83. push(s, u, flow)
  84. # Partition nodes into levels.
  85. levels = [Level() for i in range(2 * n)]
  86. for u in R:
  87. if u != s and u != t:
  88. level = levels[R_nodes[u]["height"]]
  89. if R_nodes[u]["excess"] > 0:
  90. level.active.add(u)
  91. else:
  92. level.inactive.add(u)
  93. def activate(v):
  94. """Move a node from the inactive set to the active set of its level."""
  95. if v != s and v != t:
  96. level = levels[R_nodes[v]["height"]]
  97. if v in level.inactive:
  98. level.inactive.remove(v)
  99. level.active.add(v)
  100. def relabel(u):
  101. """Relabel a node to create an admissible edge."""
  102. grt.add_work(len(R_succ[u]))
  103. return (
  104. min(
  105. R_nodes[v]["height"]
  106. for v, attr in R_succ[u].items()
  107. if attr["flow"] < attr["capacity"]
  108. )
  109. + 1
  110. )
  111. def discharge(u, is_phase1):
  112. """Discharge a node until it becomes inactive or, during phase 1 (see
  113. below), its height reaches at least n. The node is known to have the
  114. largest height among active nodes.
  115. """
  116. height = R_nodes[u]["height"]
  117. curr_edge = R_nodes[u]["curr_edge"]
  118. # next_height represents the next height to examine after discharging
  119. # the current node. During phase 1, it is capped to below n.
  120. next_height = height
  121. levels[height].active.remove(u)
  122. while True:
  123. v, attr = curr_edge.get()
  124. if height == R_nodes[v]["height"] + 1 and attr["flow"] < attr["capacity"]:
  125. flow = min(R_nodes[u]["excess"], attr["capacity"] - attr["flow"])
  126. push(u, v, flow)
  127. activate(v)
  128. if R_nodes[u]["excess"] == 0:
  129. # The node has become inactive.
  130. levels[height].inactive.add(u)
  131. break
  132. try:
  133. curr_edge.move_to_next()
  134. except StopIteration:
  135. # We have run off the end of the adjacency list, and there can
  136. # be no more admissible edges. Relabel the node to create one.
  137. height = relabel(u)
  138. if is_phase1 and height >= n - 1:
  139. # Although the node is still active, with a height at least
  140. # n - 1, it is now known to be on the s side of the minimum
  141. # s-t cut. Stop processing it until phase 2.
  142. levels[height].active.add(u)
  143. break
  144. # The first relabel operation after global relabeling may not
  145. # increase the height of the node since the 'current edge' data
  146. # structure is not rewound. Use height instead of (height - 1)
  147. # in case other active nodes at the same level are missed.
  148. next_height = height
  149. R_nodes[u]["height"] = height
  150. return next_height
  151. def gap_heuristic(height):
  152. """Apply the gap heuristic."""
  153. # Move all nodes at levels (height + 1) to max_height to level n + 1.
  154. for level in islice(levels, height + 1, max_height + 1):
  155. for u in level.active:
  156. R_nodes[u]["height"] = n + 1
  157. for u in level.inactive:
  158. R_nodes[u]["height"] = n + 1
  159. levels[n + 1].active.update(level.active)
  160. level.active.clear()
  161. levels[n + 1].inactive.update(level.inactive)
  162. level.inactive.clear()
  163. def global_relabel(from_sink):
  164. """Apply the global relabeling heuristic."""
  165. src = t if from_sink else s
  166. heights = reverse_bfs(src)
  167. if not from_sink:
  168. # s must be reachable from t. Remove t explicitly.
  169. del heights[t]
  170. max_height = max(heights.values())
  171. if from_sink:
  172. # Also mark nodes from which t is unreachable for relabeling. This
  173. # serves the same purpose as the gap heuristic.
  174. for u in R:
  175. if u not in heights and R_nodes[u]["height"] < n:
  176. heights[u] = n + 1
  177. else:
  178. # Shift the computed heights because the height of s is n.
  179. for u in heights:
  180. heights[u] += n
  181. max_height += n
  182. del heights[src]
  183. for u, new_height in heights.items():
  184. old_height = R_nodes[u]["height"]
  185. if new_height != old_height:
  186. if u in levels[old_height].active:
  187. levels[old_height].active.remove(u)
  188. levels[new_height].active.add(u)
  189. else:
  190. levels[old_height].inactive.remove(u)
  191. levels[new_height].inactive.add(u)
  192. R_nodes[u]["height"] = new_height
  193. return max_height
  194. # Phase 1: Find the maximum preflow by pushing as much flow as possible to
  195. # t.
  196. height = max_height
  197. while height > 0:
  198. # Discharge active nodes in the current level.
  199. while True:
  200. level = levels[height]
  201. if not level.active:
  202. # All active nodes in the current level have been discharged.
  203. # Move to the next lower level.
  204. height -= 1
  205. break
  206. # Record the old height and level for the gap heuristic.
  207. old_height = height
  208. old_level = level
  209. u = arbitrary_element(level.active)
  210. height = discharge(u, True)
  211. if grt.is_reached():
  212. # Global relabeling heuristic: Recompute the exact heights of
  213. # all nodes.
  214. height = global_relabel(True)
  215. max_height = height
  216. grt.clear_work()
  217. elif not old_level.active and not old_level.inactive:
  218. # Gap heuristic: If the level at old_height is empty (a 'gap'),
  219. # a minimum cut has been identified. All nodes with heights
  220. # above old_height can have their heights set to n + 1 and not
  221. # be further processed before a maximum preflow is found.
  222. gap_heuristic(old_height)
  223. height = old_height - 1
  224. max_height = height
  225. else:
  226. # Update the height of the highest level with at least one
  227. # active node.
  228. max_height = max(max_height, height)
  229. # A maximum preflow has been found. The excess at t is the maximum flow
  230. # value.
  231. if value_only:
  232. R.graph["flow_value"] = R_nodes[t]["excess"]
  233. return R
  234. # Phase 2: Convert the maximum preflow into a maximum flow by returning the
  235. # excess to s.
  236. # Relabel all nodes so that they have accurate heights.
  237. height = global_relabel(False)
  238. grt.clear_work()
  239. # Continue to discharge the active nodes.
  240. while height > n:
  241. # Discharge active nodes in the current level.
  242. while True:
  243. level = levels[height]
  244. if not level.active:
  245. # All active nodes in the current level have been discharged.
  246. # Move to the next lower level.
  247. height -= 1
  248. break
  249. u = arbitrary_element(level.active)
  250. height = discharge(u, False)
  251. if grt.is_reached():
  252. # Global relabeling heuristic.
  253. height = global_relabel(False)
  254. grt.clear_work()
  255. R.graph["flow_value"] = R_nodes[t]["excess"]
  256. return R
  257. def preflow_push(
  258. G, s, t, capacity="capacity", residual=None, global_relabel_freq=1, value_only=False
  259. ):
  260. r"""Find a maximum single-commodity flow using the highest-label
  261. preflow-push algorithm.
  262. This function returns the residual network resulting after computing
  263. the maximum flow. See below for details about the conventions
  264. NetworkX uses for defining residual networks.
  265. This algorithm has a running time of $O(n^2 \sqrt{m})$ for $n$ nodes and
  266. $m$ edges.
  267. Parameters
  268. ----------
  269. G : NetworkX graph
  270. Edges of the graph are expected to have an attribute called
  271. 'capacity'. If this attribute is not present, the edge is
  272. considered to have infinite capacity.
  273. s : node
  274. Source node for the flow.
  275. t : node
  276. Sink node for the flow.
  277. capacity : string
  278. Edges of the graph G are expected to have an attribute capacity
  279. that indicates how much flow the edge can support. If this
  280. attribute is not present, the edge is considered to have
  281. infinite capacity. Default value: 'capacity'.
  282. residual : NetworkX graph
  283. Residual network on which the algorithm is to be executed. If None, a
  284. new residual network is created. Default value: None.
  285. global_relabel_freq : integer, float
  286. Relative frequency of applying the global relabeling heuristic to speed
  287. up the algorithm. If it is None, the heuristic is disabled. Default
  288. value: 1.
  289. value_only : bool
  290. If False, compute a maximum flow; otherwise, compute a maximum preflow
  291. which is enough for computing the maximum flow value. Default value:
  292. False.
  293. Returns
  294. -------
  295. R : NetworkX DiGraph
  296. Residual network after computing the maximum flow.
  297. Raises
  298. ------
  299. NetworkXError
  300. The algorithm does not support MultiGraph and MultiDiGraph. If
  301. the input graph is an instance of one of these two classes, a
  302. NetworkXError is raised.
  303. NetworkXUnbounded
  304. If the graph has a path of infinite capacity, the value of a
  305. feasible flow on the graph is unbounded above and the function
  306. raises a NetworkXUnbounded.
  307. See also
  308. --------
  309. :meth:`maximum_flow`
  310. :meth:`minimum_cut`
  311. :meth:`edmonds_karp`
  312. :meth:`shortest_augmenting_path`
  313. Notes
  314. -----
  315. The residual network :samp:`R` from an input graph :samp:`G` has the
  316. same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
  317. of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
  318. self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
  319. in :samp:`G`. For each node :samp:`u` in :samp:`R`,
  320. :samp:`R.nodes[u]['excess']` represents the difference between flow into
  321. :samp:`u` and flow out of :samp:`u`.
  322. For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
  323. is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
  324. in :samp:`G` or zero otherwise. If the capacity is infinite,
  325. :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
  326. that does not affect the solution of the problem. This value is stored in
  327. :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
  328. :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
  329. satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
  330. The flow value, defined as the total flow into :samp:`t`, the sink, is
  331. stored in :samp:`R.graph['flow_value']`. Reachability to :samp:`t` using
  332. only edges :samp:`(u, v)` such that
  333. :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
  334. :samp:`s`-:samp:`t` cut.
  335. Examples
  336. --------
  337. >>> from networkx.algorithms.flow import preflow_push
  338. The functions that implement flow algorithms and output a residual
  339. network, such as this one, are not imported to the base NetworkX
  340. namespace, so you have to explicitly import them from the flow package.
  341. >>> G = nx.DiGraph()
  342. >>> G.add_edge("x", "a", capacity=3.0)
  343. >>> G.add_edge("x", "b", capacity=1.0)
  344. >>> G.add_edge("a", "c", capacity=3.0)
  345. >>> G.add_edge("b", "c", capacity=5.0)
  346. >>> G.add_edge("b", "d", capacity=4.0)
  347. >>> G.add_edge("d", "e", capacity=2.0)
  348. >>> G.add_edge("c", "y", capacity=2.0)
  349. >>> G.add_edge("e", "y", capacity=3.0)
  350. >>> R = preflow_push(G, "x", "y")
  351. >>> flow_value = nx.maximum_flow_value(G, "x", "y")
  352. >>> flow_value == R.graph["flow_value"]
  353. True
  354. >>> # preflow_push also stores the maximum flow value
  355. >>> # in the excess attribute of the sink node t
  356. >>> flow_value == R.nodes["y"]["excess"]
  357. True
  358. >>> # For some problems, you might only want to compute a
  359. >>> # maximum preflow.
  360. >>> R = preflow_push(G, "x", "y", value_only=True)
  361. >>> flow_value == R.graph["flow_value"]
  362. True
  363. >>> flow_value == R.nodes["y"]["excess"]
  364. True
  365. """
  366. R = preflow_push_impl(G, s, t, capacity, residual, global_relabel_freq, value_only)
  367. R.graph["algorithm"] = "preflow_push"
  368. return R