Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/flow/edmondskarp.py: 9%

92 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

1""" 

2Edmonds-Karp algorithm for maximum flow problems. 

3""" 

4 

5import networkx as nx 

6from networkx.algorithms.flow.utils import build_residual_network 

7 

8__all__ = ["edmonds_karp"] 

9 

10 

11@nx._dispatch( 

12 graphs="R", 

13 preserve_edge_attrs={"R": {"capacity": float("inf"), "flow": 0}}, 

14 preserve_graph_attrs=True, 

15) 

16def edmonds_karp_core(R, s, t, cutoff): 

17 """Implementation of the Edmonds-Karp algorithm.""" 

18 R_nodes = R.nodes 

19 R_pred = R.pred 

20 R_succ = R.succ 

21 

22 inf = R.graph["inf"] 

23 

24 def augment(path): 

25 """Augment flow along a path from s to t.""" 

26 # Determine the path residual capacity. 

27 flow = inf 

28 it = iter(path) 

29 u = next(it) 

30 for v in it: 

31 attr = R_succ[u][v] 

32 flow = min(flow, attr["capacity"] - attr["flow"]) 

33 u = v 

34 if flow * 2 > inf: 

35 raise nx.NetworkXUnbounded("Infinite capacity path, flow unbounded above.") 

36 # Augment flow along the path. 

37 it = iter(path) 

38 u = next(it) 

39 for v in it: 

40 R_succ[u][v]["flow"] += flow 

41 R_succ[v][u]["flow"] -= flow 

42 u = v 

43 return flow 

44 

45 def bidirectional_bfs(): 

46 """Bidirectional breadth-first search for an augmenting path.""" 

47 pred = {s: None} 

48 q_s = [s] 

49 succ = {t: None} 

50 q_t = [t] 

51 while True: 

52 q = [] 

53 if len(q_s) <= len(q_t): 

54 for u in q_s: 

55 for v, attr in R_succ[u].items(): 

56 if v not in pred and attr["flow"] < attr["capacity"]: 

57 pred[v] = u 

58 if v in succ: 

59 return v, pred, succ 

60 q.append(v) 

61 if not q: 

62 return None, None, None 

63 q_s = q 

64 else: 

65 for u in q_t: 

66 for v, attr in R_pred[u].items(): 

67 if v not in succ and attr["flow"] < attr["capacity"]: 

68 succ[v] = u 

69 if v in pred: 

70 return v, pred, succ 

71 q.append(v) 

72 if not q: 

73 return None, None, None 

74 q_t = q 

75 

76 # Look for shortest augmenting paths using breadth-first search. 

77 flow_value = 0 

78 while flow_value < cutoff: 

79 v, pred, succ = bidirectional_bfs() 

80 if pred is None: 

81 break 

82 path = [v] 

83 # Trace a path from s to v. 

84 u = v 

85 while u != s: 

86 u = pred[u] 

87 path.append(u) 

88 path.reverse() 

89 # Trace a path from v to t. 

90 u = v 

91 while u != t: 

92 u = succ[u] 

93 path.append(u) 

94 flow_value += augment(path) 

95 

96 return flow_value 

97 

98 

99def edmonds_karp_impl(G, s, t, capacity, residual, cutoff): 

100 """Implementation of the Edmonds-Karp algorithm.""" 

101 if s not in G: 

102 raise nx.NetworkXError(f"node {str(s)} not in graph") 

103 if t not in G: 

104 raise nx.NetworkXError(f"node {str(t)} not in graph") 

105 if s == t: 

106 raise nx.NetworkXError("source and sink are the same node") 

107 

108 if residual is None: 

109 R = build_residual_network(G, capacity) 

110 else: 

111 R = residual 

112 

113 # Initialize/reset the residual network. 

114 for u in R: 

115 for e in R[u].values(): 

116 e["flow"] = 0 

117 

118 if cutoff is None: 

119 cutoff = float("inf") 

120 R.graph["flow_value"] = edmonds_karp_core(R, s, t, cutoff) 

121 

122 return R 

123 

124 

125@nx._dispatch( 

126 graphs={"G": 0, "residual?": 4}, 

127 edge_attrs={"capacity": float("inf")}, 

128 preserve_edge_attrs={"residual": {"capacity": float("inf")}}, 

129 preserve_graph_attrs={"residual"}, 

130) 

131def edmonds_karp( 

132 G, s, t, capacity="capacity", residual=None, value_only=False, cutoff=None 

133): 

134 """Find a maximum single-commodity flow using the Edmonds-Karp algorithm. 

135 

136 This function returns the residual network resulting after computing 

137 the maximum flow. See below for details about the conventions 

138 NetworkX uses for defining residual networks. 

139 

140 This algorithm has a running time of $O(n m^2)$ for $n$ nodes and $m$ 

141 edges. 

142 

143 

144 Parameters 

145 ---------- 

146 G : NetworkX graph 

147 Edges of the graph are expected to have an attribute called 

148 'capacity'. If this attribute is not present, the edge is 

149 considered to have infinite capacity. 

150 

151 s : node 

152 Source node for the flow. 

153 

154 t : node 

155 Sink node for the flow. 

156 

157 capacity : string 

158 Edges of the graph G are expected to have an attribute capacity 

159 that indicates how much flow the edge can support. If this 

160 attribute is not present, the edge is considered to have 

161 infinite capacity. Default value: 'capacity'. 

162 

163 residual : NetworkX graph 

164 Residual network on which the algorithm is to be executed. If None, a 

165 new residual network is created. Default value: None. 

166 

167 value_only : bool 

168 If True compute only the value of the maximum flow. This parameter 

169 will be ignored by this algorithm because it is not applicable. 

170 

171 cutoff : integer, float 

172 If specified, the algorithm will terminate when the flow value reaches 

173 or exceeds the cutoff. In this case, it may be unable to immediately 

174 determine a minimum cut. Default value: None. 

175 

176 Returns 

177 ------- 

178 R : NetworkX DiGraph 

179 Residual network after computing the maximum flow. 

180 

181 Raises 

182 ------ 

183 NetworkXError 

184 The algorithm does not support MultiGraph and MultiDiGraph. If 

185 the input graph is an instance of one of these two classes, a 

186 NetworkXError is raised. 

187 

188 NetworkXUnbounded 

189 If the graph has a path of infinite capacity, the value of a 

190 feasible flow on the graph is unbounded above and the function 

191 raises a NetworkXUnbounded. 

192 

193 See also 

194 -------- 

195 :meth:`maximum_flow` 

196 :meth:`minimum_cut` 

197 :meth:`preflow_push` 

198 :meth:`shortest_augmenting_path` 

199 

200 Notes 

201 ----- 

202 The residual network :samp:`R` from an input graph :samp:`G` has the 

203 same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair 

204 of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a 

205 self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists 

206 in :samp:`G`. 

207 

208 For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']` 

209 is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists 

210 in :samp:`G` or zero otherwise. If the capacity is infinite, 

211 :samp:`R[u][v]['capacity']` will have a high arbitrary finite value 

212 that does not affect the solution of the problem. This value is stored in 

213 :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`, 

214 :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and 

215 satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`. 

216 

217 The flow value, defined as the total flow into :samp:`t`, the sink, is 

218 stored in :samp:`R.graph['flow_value']`. If :samp:`cutoff` is not 

219 specified, reachability to :samp:`t` using only edges :samp:`(u, v)` such 

220 that :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum 

221 :samp:`s`-:samp:`t` cut. 

222 

223 Examples 

224 -------- 

225 >>> from networkx.algorithms.flow import edmonds_karp 

226 

227 The functions that implement flow algorithms and output a residual 

228 network, such as this one, are not imported to the base NetworkX 

229 namespace, so you have to explicitly import them from the flow package. 

230 

231 >>> G = nx.DiGraph() 

232 >>> G.add_edge("x", "a", capacity=3.0) 

233 >>> G.add_edge("x", "b", capacity=1.0) 

234 >>> G.add_edge("a", "c", capacity=3.0) 

235 >>> G.add_edge("b", "c", capacity=5.0) 

236 >>> G.add_edge("b", "d", capacity=4.0) 

237 >>> G.add_edge("d", "e", capacity=2.0) 

238 >>> G.add_edge("c", "y", capacity=2.0) 

239 >>> G.add_edge("e", "y", capacity=3.0) 

240 >>> R = edmonds_karp(G, "x", "y") 

241 >>> flow_value = nx.maximum_flow_value(G, "x", "y") 

242 >>> flow_value 

243 3.0 

244 >>> flow_value == R.graph["flow_value"] 

245 True 

246 

247 """ 

248 R = edmonds_karp_impl(G, s, t, capacity, residual, cutoff) 

249 R.graph["algorithm"] = "edmonds_karp" 

250 return R