cuts.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. """
  2. Flow based cut algorithms
  3. """
  4. import itertools
  5. import networkx as nx
  6. # Define the default maximum flow function to use in all flow based
  7. # cut algorithms.
  8. from networkx.algorithms.flow import build_residual_network, edmonds_karp
  9. default_flow_func = edmonds_karp
  10. from .utils import build_auxiliary_edge_connectivity, build_auxiliary_node_connectivity
  11. __all__ = [
  12. "minimum_st_node_cut",
  13. "minimum_node_cut",
  14. "minimum_st_edge_cut",
  15. "minimum_edge_cut",
  16. ]
  17. def minimum_st_edge_cut(G, s, t, flow_func=None, auxiliary=None, residual=None):
  18. """Returns the edges of the cut-set of a minimum (s, t)-cut.
  19. This function returns the set of edges of minimum cardinality that,
  20. if removed, would destroy all paths among source and target in G.
  21. Edge weights are not considered. See :meth:`minimum_cut` for
  22. computing minimum cuts considering edge weights.
  23. Parameters
  24. ----------
  25. G : NetworkX graph
  26. s : node
  27. Source node for the flow.
  28. t : node
  29. Sink node for the flow.
  30. auxiliary : NetworkX DiGraph
  31. Auxiliary digraph to compute flow based node connectivity. It has
  32. to have a graph attribute called mapping with a dictionary mapping
  33. node names in G and in the auxiliary digraph. If provided
  34. it will be reused instead of recreated. Default value: None.
  35. flow_func : function
  36. A function for computing the maximum flow among a pair of nodes.
  37. The function has to accept at least three parameters: a Digraph,
  38. a source node, and a target node. And return a residual network
  39. that follows NetworkX conventions (see :meth:`maximum_flow` for
  40. details). If flow_func is None, the default maximum flow function
  41. (:meth:`edmonds_karp`) is used. See :meth:`node_connectivity` for
  42. details. The choice of the default function may change from version
  43. to version and should not be relied on. Default value: None.
  44. residual : NetworkX DiGraph
  45. Residual network to compute maximum flow. If provided it will be
  46. reused instead of recreated. Default value: None.
  47. Returns
  48. -------
  49. cutset : set
  50. Set of edges that, if removed from the graph, will disconnect it.
  51. See also
  52. --------
  53. :meth:`minimum_cut`
  54. :meth:`minimum_node_cut`
  55. :meth:`minimum_edge_cut`
  56. :meth:`stoer_wagner`
  57. :meth:`node_connectivity`
  58. :meth:`edge_connectivity`
  59. :meth:`maximum_flow`
  60. :meth:`edmonds_karp`
  61. :meth:`preflow_push`
  62. :meth:`shortest_augmenting_path`
  63. Examples
  64. --------
  65. This function is not imported in the base NetworkX namespace, so you
  66. have to explicitly import it from the connectivity package:
  67. >>> from networkx.algorithms.connectivity import minimum_st_edge_cut
  68. We use in this example the platonic icosahedral graph, which has edge
  69. connectivity 5.
  70. >>> G = nx.icosahedral_graph()
  71. >>> len(minimum_st_edge_cut(G, 0, 6))
  72. 5
  73. If you need to compute local edge cuts on several pairs of
  74. nodes in the same graph, it is recommended that you reuse the
  75. data structures that NetworkX uses in the computation: the
  76. auxiliary digraph for edge connectivity, and the residual
  77. network for the underlying maximum flow computation.
  78. Example of how to compute local edge cuts among all pairs of
  79. nodes of the platonic icosahedral graph reusing the data
  80. structures.
  81. >>> import itertools
  82. >>> # You also have to explicitly import the function for
  83. >>> # building the auxiliary digraph from the connectivity package
  84. >>> from networkx.algorithms.connectivity import build_auxiliary_edge_connectivity
  85. >>> H = build_auxiliary_edge_connectivity(G)
  86. >>> # And the function for building the residual network from the
  87. >>> # flow package
  88. >>> from networkx.algorithms.flow import build_residual_network
  89. >>> # Note that the auxiliary digraph has an edge attribute named capacity
  90. >>> R = build_residual_network(H, "capacity")
  91. >>> result = dict.fromkeys(G, dict())
  92. >>> # Reuse the auxiliary digraph and the residual network by passing them
  93. >>> # as parameters
  94. >>> for u, v in itertools.combinations(G, 2):
  95. ... k = len(minimum_st_edge_cut(G, u, v, auxiliary=H, residual=R))
  96. ... result[u][v] = k
  97. >>> all(result[u][v] == 5 for u, v in itertools.combinations(G, 2))
  98. True
  99. You can also use alternative flow algorithms for computing edge
  100. cuts. For instance, in dense networks the algorithm
  101. :meth:`shortest_augmenting_path` will usually perform better than
  102. the default :meth:`edmonds_karp` which is faster for sparse
  103. networks with highly skewed degree distributions. Alternative flow
  104. functions have to be explicitly imported from the flow package.
  105. >>> from networkx.algorithms.flow import shortest_augmenting_path
  106. >>> len(minimum_st_edge_cut(G, 0, 6, flow_func=shortest_augmenting_path))
  107. 5
  108. """
  109. if flow_func is None:
  110. flow_func = default_flow_func
  111. if auxiliary is None:
  112. H = build_auxiliary_edge_connectivity(G)
  113. else:
  114. H = auxiliary
  115. kwargs = {"capacity": "capacity", "flow_func": flow_func, "residual": residual}
  116. cut_value, partition = nx.minimum_cut(H, s, t, **kwargs)
  117. reachable, non_reachable = partition
  118. # Any edge in the original graph linking the two sets in the
  119. # partition is part of the edge cutset
  120. cutset = set()
  121. for u, nbrs in ((n, G[n]) for n in reachable):
  122. cutset.update((u, v) for v in nbrs if v in non_reachable)
  123. return cutset
  124. def minimum_st_node_cut(G, s, t, flow_func=None, auxiliary=None, residual=None):
  125. r"""Returns a set of nodes of minimum cardinality that disconnect source
  126. from target in G.
  127. This function returns the set of nodes of minimum cardinality that,
  128. if removed, would destroy all paths among source and target in G.
  129. Parameters
  130. ----------
  131. G : NetworkX graph
  132. s : node
  133. Source node.
  134. t : node
  135. Target node.
  136. flow_func : function
  137. A function for computing the maximum flow among a pair of nodes.
  138. The function has to accept at least three parameters: a Digraph,
  139. a source node, and a target node. And return a residual network
  140. that follows NetworkX conventions (see :meth:`maximum_flow` for
  141. details). If flow_func is None, the default maximum flow function
  142. (:meth:`edmonds_karp`) is used. See below for details. The choice
  143. of the default function may change from version to version and
  144. should not be relied on. Default value: None.
  145. auxiliary : NetworkX DiGraph
  146. Auxiliary digraph to compute flow based node connectivity. It has
  147. to have a graph attribute called mapping with a dictionary mapping
  148. node names in G and in the auxiliary digraph. If provided
  149. it will be reused instead of recreated. Default value: None.
  150. residual : NetworkX DiGraph
  151. Residual network to compute maximum flow. If provided it will be
  152. reused instead of recreated. Default value: None.
  153. Returns
  154. -------
  155. cutset : set
  156. Set of nodes that, if removed, would destroy all paths between
  157. source and target in G.
  158. Examples
  159. --------
  160. This function is not imported in the base NetworkX namespace, so you
  161. have to explicitly import it from the connectivity package:
  162. >>> from networkx.algorithms.connectivity import minimum_st_node_cut
  163. We use in this example the platonic icosahedral graph, which has node
  164. connectivity 5.
  165. >>> G = nx.icosahedral_graph()
  166. >>> len(minimum_st_node_cut(G, 0, 6))
  167. 5
  168. If you need to compute local st cuts between several pairs of
  169. nodes in the same graph, it is recommended that you reuse the
  170. data structures that NetworkX uses in the computation: the
  171. auxiliary digraph for node connectivity and node cuts, and the
  172. residual network for the underlying maximum flow computation.
  173. Example of how to compute local st node cuts reusing the data
  174. structures:
  175. >>> # You also have to explicitly import the function for
  176. >>> # building the auxiliary digraph from the connectivity package
  177. >>> from networkx.algorithms.connectivity import build_auxiliary_node_connectivity
  178. >>> H = build_auxiliary_node_connectivity(G)
  179. >>> # And the function for building the residual network from the
  180. >>> # flow package
  181. >>> from networkx.algorithms.flow import build_residual_network
  182. >>> # Note that the auxiliary digraph has an edge attribute named capacity
  183. >>> R = build_residual_network(H, "capacity")
  184. >>> # Reuse the auxiliary digraph and the residual network by passing them
  185. >>> # as parameters
  186. >>> len(minimum_st_node_cut(G, 0, 6, auxiliary=H, residual=R))
  187. 5
  188. You can also use alternative flow algorithms for computing minimum st
  189. node cuts. For instance, in dense networks the algorithm
  190. :meth:`shortest_augmenting_path` will usually perform better than
  191. the default :meth:`edmonds_karp` which is faster for sparse
  192. networks with highly skewed degree distributions. Alternative flow
  193. functions have to be explicitly imported from the flow package.
  194. >>> from networkx.algorithms.flow import shortest_augmenting_path
  195. >>> len(minimum_st_node_cut(G, 0, 6, flow_func=shortest_augmenting_path))
  196. 5
  197. Notes
  198. -----
  199. This is a flow based implementation of minimum node cut. The algorithm
  200. is based in solving a number of maximum flow computations to determine
  201. the capacity of the minimum cut on an auxiliary directed network that
  202. corresponds to the minimum node cut of G. It handles both directed
  203. and undirected graphs. This implementation is based on algorithm 11
  204. in [1]_.
  205. See also
  206. --------
  207. :meth:`minimum_node_cut`
  208. :meth:`minimum_edge_cut`
  209. :meth:`stoer_wagner`
  210. :meth:`node_connectivity`
  211. :meth:`edge_connectivity`
  212. :meth:`maximum_flow`
  213. :meth:`edmonds_karp`
  214. :meth:`preflow_push`
  215. :meth:`shortest_augmenting_path`
  216. References
  217. ----------
  218. .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms.
  219. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
  220. """
  221. if auxiliary is None:
  222. H = build_auxiliary_node_connectivity(G)
  223. else:
  224. H = auxiliary
  225. mapping = H.graph.get("mapping", None)
  226. if mapping is None:
  227. raise nx.NetworkXError("Invalid auxiliary digraph.")
  228. if G.has_edge(s, t) or G.has_edge(t, s):
  229. return {}
  230. kwargs = {"flow_func": flow_func, "residual": residual, "auxiliary": H}
  231. # The edge cut in the auxiliary digraph corresponds to the node cut in the
  232. # original graph.
  233. edge_cut = minimum_st_edge_cut(H, f"{mapping[s]}B", f"{mapping[t]}A", **kwargs)
  234. # Each node in the original graph maps to two nodes of the auxiliary graph
  235. node_cut = {H.nodes[node]["id"] for edge in edge_cut for node in edge}
  236. return node_cut - {s, t}
  237. def minimum_node_cut(G, s=None, t=None, flow_func=None):
  238. r"""Returns a set of nodes of minimum cardinality that disconnects G.
  239. If source and target nodes are provided, this function returns the
  240. set of nodes of minimum cardinality that, if removed, would destroy
  241. all paths among source and target in G. If not, it returns a set
  242. of nodes of minimum cardinality that disconnects G.
  243. Parameters
  244. ----------
  245. G : NetworkX graph
  246. s : node
  247. Source node. Optional. Default value: None.
  248. t : node
  249. Target node. Optional. Default value: None.
  250. flow_func : function
  251. A function for computing the maximum flow among a pair of nodes.
  252. The function has to accept at least three parameters: a Digraph,
  253. a source node, and a target node. And return a residual network
  254. that follows NetworkX conventions (see :meth:`maximum_flow` for
  255. details). If flow_func is None, the default maximum flow function
  256. (:meth:`edmonds_karp`) is used. See below for details. The
  257. choice of the default function may change from version
  258. to version and should not be relied on. Default value: None.
  259. Returns
  260. -------
  261. cutset : set
  262. Set of nodes that, if removed, would disconnect G. If source
  263. and target nodes are provided, the set contains the nodes that
  264. if removed, would destroy all paths between source and target.
  265. Examples
  266. --------
  267. >>> # Platonic icosahedral graph has node connectivity 5
  268. >>> G = nx.icosahedral_graph()
  269. >>> node_cut = nx.minimum_node_cut(G)
  270. >>> len(node_cut)
  271. 5
  272. You can use alternative flow algorithms for the underlying maximum
  273. flow computation. In dense networks the algorithm
  274. :meth:`shortest_augmenting_path` will usually perform better
  275. than the default :meth:`edmonds_karp`, which is faster for
  276. sparse networks with highly skewed degree distributions. Alternative
  277. flow functions have to be explicitly imported from the flow package.
  278. >>> from networkx.algorithms.flow import shortest_augmenting_path
  279. >>> node_cut == nx.minimum_node_cut(G, flow_func=shortest_augmenting_path)
  280. True
  281. If you specify a pair of nodes (source and target) as parameters,
  282. this function returns a local st node cut.
  283. >>> len(nx.minimum_node_cut(G, 3, 7))
  284. 5
  285. If you need to perform several local st cuts among different
  286. pairs of nodes on the same graph, it is recommended that you reuse
  287. the data structures used in the maximum flow computations. See
  288. :meth:`minimum_st_node_cut` for details.
  289. Notes
  290. -----
  291. This is a flow based implementation of minimum node cut. The algorithm
  292. is based in solving a number of maximum flow computations to determine
  293. the capacity of the minimum cut on an auxiliary directed network that
  294. corresponds to the minimum node cut of G. It handles both directed
  295. and undirected graphs. This implementation is based on algorithm 11
  296. in [1]_.
  297. See also
  298. --------
  299. :meth:`minimum_st_node_cut`
  300. :meth:`minimum_cut`
  301. :meth:`minimum_edge_cut`
  302. :meth:`stoer_wagner`
  303. :meth:`node_connectivity`
  304. :meth:`edge_connectivity`
  305. :meth:`maximum_flow`
  306. :meth:`edmonds_karp`
  307. :meth:`preflow_push`
  308. :meth:`shortest_augmenting_path`
  309. References
  310. ----------
  311. .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms.
  312. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
  313. """
  314. if (s is not None and t is None) or (s is None and t is not None):
  315. raise nx.NetworkXError("Both source and target must be specified.")
  316. # Local minimum node cut.
  317. if s is not None and t is not None:
  318. if s not in G:
  319. raise nx.NetworkXError(f"node {s} not in graph")
  320. if t not in G:
  321. raise nx.NetworkXError(f"node {t} not in graph")
  322. return minimum_st_node_cut(G, s, t, flow_func=flow_func)
  323. # Global minimum node cut.
  324. # Analog to the algorithm 11 for global node connectivity in [1].
  325. if G.is_directed():
  326. if not nx.is_weakly_connected(G):
  327. raise nx.NetworkXError("Input graph is not connected")
  328. iter_func = itertools.permutations
  329. def neighbors(v):
  330. return itertools.chain.from_iterable([G.predecessors(v), G.successors(v)])
  331. else:
  332. if not nx.is_connected(G):
  333. raise nx.NetworkXError("Input graph is not connected")
  334. iter_func = itertools.combinations
  335. neighbors = G.neighbors
  336. # Reuse the auxiliary digraph and the residual network.
  337. H = build_auxiliary_node_connectivity(G)
  338. R = build_residual_network(H, "capacity")
  339. kwargs = {"flow_func": flow_func, "auxiliary": H, "residual": R}
  340. # Choose a node with minimum degree.
  341. v = min(G, key=G.degree)
  342. # Initial node cutset is all neighbors of the node with minimum degree.
  343. min_cut = set(G[v])
  344. # Compute st node cuts between v and all its non-neighbors nodes in G.
  345. for w in set(G) - set(neighbors(v)) - {v}:
  346. this_cut = minimum_st_node_cut(G, v, w, **kwargs)
  347. if len(min_cut) >= len(this_cut):
  348. min_cut = this_cut
  349. # Also for non adjacent pairs of neighbors of v.
  350. for x, y in iter_func(neighbors(v), 2):
  351. if y in G[x]:
  352. continue
  353. this_cut = minimum_st_node_cut(G, x, y, **kwargs)
  354. if len(min_cut) >= len(this_cut):
  355. min_cut = this_cut
  356. return min_cut
  357. def minimum_edge_cut(G, s=None, t=None, flow_func=None):
  358. r"""Returns a set of edges of minimum cardinality that disconnects G.
  359. If source and target nodes are provided, this function returns the
  360. set of edges of minimum cardinality that, if removed, would break
  361. all paths among source and target in G. If not, it returns a set of
  362. edges of minimum cardinality that disconnects G.
  363. Parameters
  364. ----------
  365. G : NetworkX graph
  366. s : node
  367. Source node. Optional. Default value: None.
  368. t : node
  369. Target node. Optional. Default value: None.
  370. flow_func : function
  371. A function for computing the maximum flow among a pair of nodes.
  372. The function has to accept at least three parameters: a Digraph,
  373. a source node, and a target node. And return a residual network
  374. that follows NetworkX conventions (see :meth:`maximum_flow` for
  375. details). If flow_func is None, the default maximum flow function
  376. (:meth:`edmonds_karp`) is used. See below for details. The
  377. choice of the default function may change from version
  378. to version and should not be relied on. Default value: None.
  379. Returns
  380. -------
  381. cutset : set
  382. Set of edges that, if removed, would disconnect G. If source
  383. and target nodes are provided, the set contains the edges that
  384. if removed, would destroy all paths between source and target.
  385. Examples
  386. --------
  387. >>> # Platonic icosahedral graph has edge connectivity 5
  388. >>> G = nx.icosahedral_graph()
  389. >>> len(nx.minimum_edge_cut(G))
  390. 5
  391. You can use alternative flow algorithms for the underlying
  392. maximum flow computation. In dense networks the algorithm
  393. :meth:`shortest_augmenting_path` will usually perform better
  394. than the default :meth:`edmonds_karp`, which is faster for
  395. sparse networks with highly skewed degree distributions.
  396. Alternative flow functions have to be explicitly imported
  397. from the flow package.
  398. >>> from networkx.algorithms.flow import shortest_augmenting_path
  399. >>> len(nx.minimum_edge_cut(G, flow_func=shortest_augmenting_path))
  400. 5
  401. If you specify a pair of nodes (source and target) as parameters,
  402. this function returns the value of local edge connectivity.
  403. >>> nx.edge_connectivity(G, 3, 7)
  404. 5
  405. If you need to perform several local computations among different
  406. pairs of nodes on the same graph, it is recommended that you reuse
  407. the data structures used in the maximum flow computations. See
  408. :meth:`local_edge_connectivity` for details.
  409. Notes
  410. -----
  411. This is a flow based implementation of minimum edge cut. For
  412. undirected graphs the algorithm works by finding a 'small' dominating
  413. set of nodes of G (see algorithm 7 in [1]_) and computing the maximum
  414. flow between an arbitrary node in the dominating set and the rest of
  415. nodes in it. This is an implementation of algorithm 6 in [1]_. For
  416. directed graphs, the algorithm does n calls to the max flow function.
  417. The function raises an error if the directed graph is not weakly
  418. connected and returns an empty set if it is weakly connected.
  419. It is an implementation of algorithm 8 in [1]_.
  420. See also
  421. --------
  422. :meth:`minimum_st_edge_cut`
  423. :meth:`minimum_node_cut`
  424. :meth:`stoer_wagner`
  425. :meth:`node_connectivity`
  426. :meth:`edge_connectivity`
  427. :meth:`maximum_flow`
  428. :meth:`edmonds_karp`
  429. :meth:`preflow_push`
  430. :meth:`shortest_augmenting_path`
  431. References
  432. ----------
  433. .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms.
  434. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
  435. """
  436. if (s is not None and t is None) or (s is None and t is not None):
  437. raise nx.NetworkXError("Both source and target must be specified.")
  438. # reuse auxiliary digraph and residual network
  439. H = build_auxiliary_edge_connectivity(G)
  440. R = build_residual_network(H, "capacity")
  441. kwargs = {"flow_func": flow_func, "residual": R, "auxiliary": H}
  442. # Local minimum edge cut if s and t are not None
  443. if s is not None and t is not None:
  444. if s not in G:
  445. raise nx.NetworkXError(f"node {s} not in graph")
  446. if t not in G:
  447. raise nx.NetworkXError(f"node {t} not in graph")
  448. return minimum_st_edge_cut(H, s, t, **kwargs)
  449. # Global minimum edge cut
  450. # Analog to the algorithm for global edge connectivity
  451. if G.is_directed():
  452. # Based on algorithm 8 in [1]
  453. if not nx.is_weakly_connected(G):
  454. raise nx.NetworkXError("Input graph is not connected")
  455. # Initial cutset is all edges of a node with minimum degree
  456. node = min(G, key=G.degree)
  457. min_cut = set(G.edges(node))
  458. nodes = list(G)
  459. n = len(nodes)
  460. for i in range(n):
  461. try:
  462. this_cut = minimum_st_edge_cut(H, nodes[i], nodes[i + 1], **kwargs)
  463. if len(this_cut) <= len(min_cut):
  464. min_cut = this_cut
  465. except IndexError: # Last node!
  466. this_cut = minimum_st_edge_cut(H, nodes[i], nodes[0], **kwargs)
  467. if len(this_cut) <= len(min_cut):
  468. min_cut = this_cut
  469. return min_cut
  470. else: # undirected
  471. # Based on algorithm 6 in [1]
  472. if not nx.is_connected(G):
  473. raise nx.NetworkXError("Input graph is not connected")
  474. # Initial cutset is all edges of a node with minimum degree
  475. node = min(G, key=G.degree)
  476. min_cut = set(G.edges(node))
  477. # A dominating set is \lambda-covering
  478. # We need a dominating set with at least two nodes
  479. for node in G:
  480. D = nx.dominating_set(G, start_with=node)
  481. v = D.pop()
  482. if D:
  483. break
  484. else:
  485. # in complete graphs the dominating set will always be of one node
  486. # thus we return min_cut, which now contains the edges of a node
  487. # with minimum degree
  488. return min_cut
  489. for w in D:
  490. this_cut = minimum_st_edge_cut(H, v, w, **kwargs)
  491. if len(this_cut) <= len(min_cut):
  492. min_cut = this_cut
  493. return min_cut