comparison env/lib/python3.9/site-packages/networkx/algorithms/flow/shortestaugmentingpath.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """
2 Shortest augmenting path algorithm for maximum flow problems.
3 """
4
5 from collections import deque
6 import networkx as nx
7 from .utils import build_residual_network, CurrentEdge
8 from .edmondskarp import edmonds_karp_core
9
10 __all__ = ["shortest_augmenting_path"]
11
12
13 def shortest_augmenting_path_impl(G, s, t, capacity, residual, two_phase, cutoff):
14 """Implementation of the shortest augmenting path algorithm.
15 """
16 if s not in G:
17 raise nx.NetworkXError(f"node {str(s)} not in graph")
18 if t not in G:
19 raise nx.NetworkXError(f"node {str(t)} not in graph")
20 if s == t:
21 raise nx.NetworkXError("source and sink are the same node")
22
23 if residual is None:
24 R = build_residual_network(G, capacity)
25 else:
26 R = residual
27
28 R_nodes = R.nodes
29 R_pred = R.pred
30 R_succ = R.succ
31
32 # Initialize/reset the residual network.
33 for u in R:
34 for e in R_succ[u].values():
35 e["flow"] = 0
36
37 # Initialize heights of the nodes.
38 heights = {t: 0}
39 q = deque([(t, 0)])
40 while q:
41 u, height = q.popleft()
42 height += 1
43 for v, attr in R_pred[u].items():
44 if v not in heights and attr["flow"] < attr["capacity"]:
45 heights[v] = height
46 q.append((v, height))
47
48 if s not in heights:
49 # t is not reachable from s in the residual network. The maximum flow
50 # must be zero.
51 R.graph["flow_value"] = 0
52 return R
53
54 n = len(G)
55 m = R.size() / 2
56
57 # Initialize heights and 'current edge' data structures of the nodes.
58 for u in R:
59 R_nodes[u]["height"] = heights[u] if u in heights else n
60 R_nodes[u]["curr_edge"] = CurrentEdge(R_succ[u])
61
62 # Initialize counts of nodes in each level.
63 counts = [0] * (2 * n - 1)
64 for u in R:
65 counts[R_nodes[u]["height"]] += 1
66
67 inf = R.graph["inf"]
68
69 def augment(path):
70 """Augment flow along a path from s to t.
71 """
72 # Determine the path residual capacity.
73 flow = inf
74 it = iter(path)
75 u = next(it)
76 for v in it:
77 attr = R_succ[u][v]
78 flow = min(flow, attr["capacity"] - attr["flow"])
79 u = v
80 if flow * 2 > inf:
81 raise nx.NetworkXUnbounded("Infinite capacity path, flow unbounded above.")
82 # Augment flow along the path.
83 it = iter(path)
84 u = next(it)
85 for v in it:
86 R_succ[u][v]["flow"] += flow
87 R_succ[v][u]["flow"] -= flow
88 u = v
89 return flow
90
91 def relabel(u):
92 """Relabel a node to create an admissible edge.
93 """
94 height = n - 1
95 for v, attr in R_succ[u].items():
96 if attr["flow"] < attr["capacity"]:
97 height = min(height, R_nodes[v]["height"])
98 return height + 1
99
100 if cutoff is None:
101 cutoff = float("inf")
102
103 # Phase 1: Look for shortest augmenting paths using depth-first search.
104
105 flow_value = 0
106 path = [s]
107 u = s
108 d = n if not two_phase else int(min(m ** 0.5, 2 * n ** (2.0 / 3)))
109 done = R_nodes[s]["height"] >= d
110 while not done:
111 height = R_nodes[u]["height"]
112 curr_edge = R_nodes[u]["curr_edge"]
113 # Depth-first search for the next node on the path to t.
114 while True:
115 v, attr = curr_edge.get()
116 if height == R_nodes[v]["height"] + 1 and attr["flow"] < attr["capacity"]:
117 # Advance to the next node following an admissible edge.
118 path.append(v)
119 u = v
120 break
121 try:
122 curr_edge.move_to_next()
123 except StopIteration:
124 counts[height] -= 1
125 if counts[height] == 0:
126 # Gap heuristic: If relabeling causes a level to become
127 # empty, a minimum cut has been identified. The algorithm
128 # can now be terminated.
129 R.graph["flow_value"] = flow_value
130 return R
131 height = relabel(u)
132 if u == s and height >= d:
133 if not two_phase:
134 # t is disconnected from s in the residual network. No
135 # more augmenting paths exist.
136 R.graph["flow_value"] = flow_value
137 return R
138 else:
139 # t is at least d steps away from s. End of phase 1.
140 done = True
141 break
142 counts[height] += 1
143 R_nodes[u]["height"] = height
144 if u != s:
145 # After relabeling, the last edge on the path is no longer
146 # admissible. Retreat one step to look for an alternative.
147 path.pop()
148 u = path[-1]
149 break
150 if u == t:
151 # t is reached. Augment flow along the path and reset it for a new
152 # depth-first search.
153 flow_value += augment(path)
154 if flow_value >= cutoff:
155 R.graph["flow_value"] = flow_value
156 return R
157 path = [s]
158 u = s
159
160 # Phase 2: Look for shortest augmenting paths using breadth-first search.
161 flow_value += edmonds_karp_core(R, s, t, cutoff - flow_value)
162
163 R.graph["flow_value"] = flow_value
164 return R
165
166
167 def shortest_augmenting_path(
168 G,
169 s,
170 t,
171 capacity="capacity",
172 residual=None,
173 value_only=False,
174 two_phase=False,
175 cutoff=None,
176 ):
177 r"""Find a maximum single-commodity flow using the shortest augmenting path
178 algorithm.
179
180 This function returns the residual network resulting after computing
181 the maximum flow. See below for details about the conventions
182 NetworkX uses for defining residual networks.
183
184 This algorithm has a running time of $O(n^2 m)$ for $n$ nodes and $m$
185 edges.
186
187
188 Parameters
189 ----------
190 G : NetworkX graph
191 Edges of the graph are expected to have an attribute called
192 'capacity'. If this attribute is not present, the edge is
193 considered to have infinite capacity.
194
195 s : node
196 Source node for the flow.
197
198 t : node
199 Sink node for the flow.
200
201 capacity : string
202 Edges of the graph G are expected to have an attribute capacity
203 that indicates how much flow the edge can support. If this
204 attribute is not present, the edge is considered to have
205 infinite capacity. Default value: 'capacity'.
206
207 residual : NetworkX graph
208 Residual network on which the algorithm is to be executed. If None, a
209 new residual network is created. Default value: None.
210
211 value_only : bool
212 If True compute only the value of the maximum flow. This parameter
213 will be ignored by this algorithm because it is not applicable.
214
215 two_phase : bool
216 If True, a two-phase variant is used. The two-phase variant improves
217 the running time on unit-capacity networks from $O(nm)$ to
218 $O(\min(n^{2/3}, m^{1/2}) m)$. Default value: False.
219
220 cutoff : integer, float
221 If specified, the algorithm will terminate when the flow value reaches
222 or exceeds the cutoff. In this case, it may be unable to immediately
223 determine a minimum cut. Default value: None.
224
225 Returns
226 -------
227 R : NetworkX DiGraph
228 Residual network after computing the maximum flow.
229
230 Raises
231 ------
232 NetworkXError
233 The algorithm does not support MultiGraph and MultiDiGraph. If
234 the input graph is an instance of one of these two classes, a
235 NetworkXError is raised.
236
237 NetworkXUnbounded
238 If the graph has a path of infinite capacity, the value of a
239 feasible flow on the graph is unbounded above and the function
240 raises a NetworkXUnbounded.
241
242 See also
243 --------
244 :meth:`maximum_flow`
245 :meth:`minimum_cut`
246 :meth:`edmonds_karp`
247 :meth:`preflow_push`
248
249 Notes
250 -----
251 The residual network :samp:`R` from an input graph :samp:`G` has the
252 same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
253 of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
254 self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
255 in :samp:`G`.
256
257 For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
258 is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
259 in :samp:`G` or zero otherwise. If the capacity is infinite,
260 :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
261 that does not affect the solution of the problem. This value is stored in
262 :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
263 :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
264 satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
265
266 The flow value, defined as the total flow into :samp:`t`, the sink, is
267 stored in :samp:`R.graph['flow_value']`. If :samp:`cutoff` is not
268 specified, reachability to :samp:`t` using only edges :samp:`(u, v)` such
269 that :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
270 :samp:`s`-:samp:`t` cut.
271
272 Examples
273 --------
274 >>> from networkx.algorithms.flow import shortest_augmenting_path
275
276 The functions that implement flow algorithms and output a residual
277 network, such as this one, are not imported to the base NetworkX
278 namespace, so you have to explicitly import them from the flow package.
279
280 >>> G = nx.DiGraph()
281 >>> G.add_edge("x", "a", capacity=3.0)
282 >>> G.add_edge("x", "b", capacity=1.0)
283 >>> G.add_edge("a", "c", capacity=3.0)
284 >>> G.add_edge("b", "c", capacity=5.0)
285 >>> G.add_edge("b", "d", capacity=4.0)
286 >>> G.add_edge("d", "e", capacity=2.0)
287 >>> G.add_edge("c", "y", capacity=2.0)
288 >>> G.add_edge("e", "y", capacity=3.0)
289 >>> R = shortest_augmenting_path(G, "x", "y")
290 >>> flow_value = nx.maximum_flow_value(G, "x", "y")
291 >>> flow_value
292 3.0
293 >>> flow_value == R.graph["flow_value"]
294 True
295
296 """
297 R = shortest_augmenting_path_impl(G, s, t, capacity, residual, two_phase, cutoff)
298 R.graph["algorithm"] = "shortest_augmenting_path"
299 return R