connectivity.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. """
  2. Flow based connectivity algorithms
  3. """
  4. import itertools
  5. from operator import itemgetter
  6. import networkx as nx
  7. # Define the default maximum flow function to use in all flow based
  8. # connectivity algorithms.
  9. from networkx.algorithms.flow import (
  10. boykov_kolmogorov,
  11. build_residual_network,
  12. dinitz,
  13. edmonds_karp,
  14. shortest_augmenting_path,
  15. )
  16. default_flow_func = edmonds_karp
  17. from .utils import build_auxiliary_edge_connectivity, build_auxiliary_node_connectivity
  18. __all__ = [
  19. "average_node_connectivity",
  20. "local_node_connectivity",
  21. "node_connectivity",
  22. "local_edge_connectivity",
  23. "edge_connectivity",
  24. "all_pairs_node_connectivity",
  25. ]
  26. def local_node_connectivity(
  27. G, s, t, flow_func=None, auxiliary=None, residual=None, cutoff=None
  28. ):
  29. r"""Computes local node connectivity for nodes s and t.
  30. Local node connectivity for two non adjacent nodes s and t is the
  31. minimum number of nodes that must be removed (along with their incident
  32. edges) to disconnect them.
  33. This is a flow based implementation of node connectivity. We compute the
  34. maximum flow on an auxiliary digraph build from the original input
  35. graph (see below for details).
  36. Parameters
  37. ----------
  38. G : NetworkX graph
  39. Undirected graph
  40. s : node
  41. Source node
  42. t : node
  43. Target node
  44. flow_func : function
  45. A function for computing the maximum flow among a pair of nodes.
  46. The function has to accept at least three parameters: a Digraph,
  47. a source node, and a target node. And return a residual network
  48. that follows NetworkX conventions (see :meth:`maximum_flow` for
  49. details). If flow_func is None, the default maximum flow function
  50. (:meth:`edmonds_karp`) is used. See below for details. The choice
  51. of the default function may change from version to version and
  52. should not be relied on. Default value: None.
  53. auxiliary : NetworkX DiGraph
  54. Auxiliary digraph to compute flow based node connectivity. It has
  55. to have a graph attribute called mapping with a dictionary mapping
  56. node names in G and in the auxiliary digraph. If provided
  57. it will be reused instead of recreated. Default value: None.
  58. residual : NetworkX DiGraph
  59. Residual network to compute maximum flow. If provided it will be
  60. reused instead of recreated. Default value: None.
  61. cutoff : integer, float, or None (default: None)
  62. If specified, the maximum flow algorithm will terminate when the
  63. flow value reaches or exceeds the cutoff. This only works for flows
  64. that support the cutoff parameter (most do) and is ignored otherwise.
  65. Returns
  66. -------
  67. K : integer
  68. local node connectivity for nodes s and t
  69. Examples
  70. --------
  71. This function is not imported in the base NetworkX namespace, so you
  72. have to explicitly import it from the connectivity package:
  73. >>> from networkx.algorithms.connectivity import local_node_connectivity
  74. We use in this example the platonic icosahedral graph, which has node
  75. connectivity 5.
  76. >>> G = nx.icosahedral_graph()
  77. >>> local_node_connectivity(G, 0, 6)
  78. 5
  79. If you need to compute local connectivity on several pairs of
  80. nodes in the same graph, it is recommended that you reuse the
  81. data structures that NetworkX uses in the computation: the
  82. auxiliary digraph for node connectivity, and the residual
  83. network for the underlying maximum flow computation.
  84. Example of how to compute local node connectivity among
  85. all pairs of nodes of the platonic icosahedral graph reusing
  86. the data structures.
  87. >>> import itertools
  88. >>> # You also have to explicitly import the function for
  89. >>> # building the auxiliary digraph from the connectivity package
  90. >>> from networkx.algorithms.connectivity import build_auxiliary_node_connectivity
  91. ...
  92. >>> H = build_auxiliary_node_connectivity(G)
  93. >>> # And the function for building the residual network from the
  94. >>> # flow package
  95. >>> from networkx.algorithms.flow import build_residual_network
  96. >>> # Note that the auxiliary digraph has an edge attribute named capacity
  97. >>> R = build_residual_network(H, "capacity")
  98. >>> result = dict.fromkeys(G, dict())
  99. >>> # Reuse the auxiliary digraph and the residual network by passing them
  100. >>> # as parameters
  101. >>> for u, v in itertools.combinations(G, 2):
  102. ... k = local_node_connectivity(G, u, v, auxiliary=H, residual=R)
  103. ... result[u][v] = k
  104. ...
  105. >>> all(result[u][v] == 5 for u, v in itertools.combinations(G, 2))
  106. True
  107. You can also use alternative flow algorithms for computing node
  108. connectivity. For instance, in dense networks the algorithm
  109. :meth:`shortest_augmenting_path` will usually perform better than
  110. the default :meth:`edmonds_karp` which is faster for sparse
  111. networks with highly skewed degree distributions. Alternative flow
  112. functions have to be explicitly imported from the flow package.
  113. >>> from networkx.algorithms.flow import shortest_augmenting_path
  114. >>> local_node_connectivity(G, 0, 6, flow_func=shortest_augmenting_path)
  115. 5
  116. Notes
  117. -----
  118. This is a flow based implementation of node connectivity. We compute the
  119. maximum flow using, by default, the :meth:`edmonds_karp` algorithm (see:
  120. :meth:`maximum_flow`) on an auxiliary digraph build from the original
  121. input graph:
  122. For an undirected graph G having `n` nodes and `m` edges we derive a
  123. directed graph H with `2n` nodes and `2m+n` arcs by replacing each
  124. original node `v` with two nodes `v_A`, `v_B` linked by an (internal)
  125. arc in H. Then for each edge (`u`, `v`) in G we add two arcs
  126. (`u_B`, `v_A`) and (`v_B`, `u_A`) in H. Finally we set the attribute
  127. capacity = 1 for each arc in H [1]_ .
  128. For a directed graph G having `n` nodes and `m` arcs we derive a
  129. directed graph H with `2n` nodes and `m+n` arcs by replacing each
  130. original node `v` with two nodes `v_A`, `v_B` linked by an (internal)
  131. arc (`v_A`, `v_B`) in H. Then for each arc (`u`, `v`) in G we add one arc
  132. (`u_B`, `v_A`) in H. Finally we set the attribute capacity = 1 for
  133. each arc in H.
  134. This is equal to the local node connectivity because the value of
  135. a maximum s-t-flow is equal to the capacity of a minimum s-t-cut.
  136. See also
  137. --------
  138. :meth:`local_edge_connectivity`
  139. :meth:`node_connectivity`
  140. :meth:`minimum_node_cut`
  141. :meth:`maximum_flow`
  142. :meth:`edmonds_karp`
  143. :meth:`preflow_push`
  144. :meth:`shortest_augmenting_path`
  145. References
  146. ----------
  147. .. [1] Kammer, Frank and Hanjo Taubig. Graph Connectivity. in Brandes and
  148. Erlebach, 'Network Analysis: Methodological Foundations', Lecture
  149. Notes in Computer Science, Volume 3418, Springer-Verlag, 2005.
  150. http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
  151. """
  152. if flow_func is None:
  153. flow_func = default_flow_func
  154. if auxiliary is None:
  155. H = build_auxiliary_node_connectivity(G)
  156. else:
  157. H = auxiliary
  158. mapping = H.graph.get("mapping", None)
  159. if mapping is None:
  160. raise nx.NetworkXError("Invalid auxiliary digraph.")
  161. kwargs = {"flow_func": flow_func, "residual": residual}
  162. if flow_func is shortest_augmenting_path:
  163. kwargs["cutoff"] = cutoff
  164. kwargs["two_phase"] = True
  165. elif flow_func is edmonds_karp:
  166. kwargs["cutoff"] = cutoff
  167. elif flow_func is dinitz:
  168. kwargs["cutoff"] = cutoff
  169. elif flow_func is boykov_kolmogorov:
  170. kwargs["cutoff"] = cutoff
  171. return nx.maximum_flow_value(H, f"{mapping[s]}B", f"{mapping[t]}A", **kwargs)
  172. def node_connectivity(G, s=None, t=None, flow_func=None):
  173. r"""Returns node connectivity for a graph or digraph G.
  174. Node connectivity is equal to the minimum number of nodes that
  175. must be removed to disconnect G or render it trivial. If source
  176. and target nodes are provided, this function returns the local node
  177. connectivity: the minimum number of nodes that must be removed to break
  178. all paths from source to target in G.
  179. Parameters
  180. ----------
  181. G : NetworkX graph
  182. Undirected graph
  183. s : node
  184. Source node. Optional. Default value: None.
  185. t : node
  186. Target node. Optional. Default value: None.
  187. flow_func : function
  188. A function for computing the maximum flow among a pair of nodes.
  189. The function has to accept at least three parameters: a Digraph,
  190. a source node, and a target node. And return a residual network
  191. that follows NetworkX conventions (see :meth:`maximum_flow` for
  192. details). If flow_func is None, the default maximum flow function
  193. (:meth:`edmonds_karp`) is used. See below for details. The
  194. choice of the default function may change from version
  195. to version and should not be relied on. Default value: None.
  196. Returns
  197. -------
  198. K : integer
  199. Node connectivity of G, or local node connectivity if source
  200. and target are provided.
  201. Examples
  202. --------
  203. >>> # Platonic icosahedral graph is 5-node-connected
  204. >>> G = nx.icosahedral_graph()
  205. >>> nx.node_connectivity(G)
  206. 5
  207. You can use alternative flow algorithms for the underlying maximum
  208. flow computation. In dense networks the algorithm
  209. :meth:`shortest_augmenting_path` will usually perform better
  210. than the default :meth:`edmonds_karp`, which is faster for
  211. sparse networks with highly skewed degree distributions. Alternative
  212. flow functions have to be explicitly imported from the flow package.
  213. >>> from networkx.algorithms.flow import shortest_augmenting_path
  214. >>> nx.node_connectivity(G, flow_func=shortest_augmenting_path)
  215. 5
  216. If you specify a pair of nodes (source and target) as parameters,
  217. this function returns the value of local node connectivity.
  218. >>> nx.node_connectivity(G, 3, 7)
  219. 5
  220. If you need to perform several local computations among different
  221. pairs of nodes on the same graph, it is recommended that you reuse
  222. the data structures used in the maximum flow computations. See
  223. :meth:`local_node_connectivity` for details.
  224. Notes
  225. -----
  226. This is a flow based implementation of node connectivity. The
  227. algorithm works by solving $O((n-\delta-1+\delta(\delta-1)/2))$
  228. maximum flow problems on an auxiliary digraph. Where $\delta$
  229. is the minimum degree of G. For details about the auxiliary
  230. digraph and the computation of local node connectivity see
  231. :meth:`local_node_connectivity`. This implementation is based
  232. on algorithm 11 in [1]_.
  233. See also
  234. --------
  235. :meth:`local_node_connectivity`
  236. :meth:`edge_connectivity`
  237. :meth:`maximum_flow`
  238. :meth:`edmonds_karp`
  239. :meth:`preflow_push`
  240. :meth:`shortest_augmenting_path`
  241. References
  242. ----------
  243. .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms.
  244. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
  245. """
  246. if (s is not None and t is None) or (s is None and t is not None):
  247. raise nx.NetworkXError("Both source and target must be specified.")
  248. # Local node connectivity
  249. if s is not None and t is not None:
  250. if s not in G:
  251. raise nx.NetworkXError(f"node {s} not in graph")
  252. if t not in G:
  253. raise nx.NetworkXError(f"node {t} not in graph")
  254. return local_node_connectivity(G, s, t, flow_func=flow_func)
  255. # Global node connectivity
  256. if G.is_directed():
  257. if not nx.is_weakly_connected(G):
  258. return 0
  259. iter_func = itertools.permutations
  260. # It is necessary to consider both predecessors
  261. # and successors for directed graphs
  262. def neighbors(v):
  263. return itertools.chain.from_iterable([G.predecessors(v), G.successors(v)])
  264. else:
  265. if not nx.is_connected(G):
  266. return 0
  267. iter_func = itertools.combinations
  268. neighbors = G.neighbors
  269. # Reuse the auxiliary digraph and the residual network
  270. H = build_auxiliary_node_connectivity(G)
  271. R = build_residual_network(H, "capacity")
  272. kwargs = {"flow_func": flow_func, "auxiliary": H, "residual": R}
  273. # Pick a node with minimum degree
  274. # Node connectivity is bounded by degree.
  275. v, K = min(G.degree(), key=itemgetter(1))
  276. # compute local node connectivity with all its non-neighbors nodes
  277. for w in set(G) - set(neighbors(v)) - {v}:
  278. kwargs["cutoff"] = K
  279. K = min(K, local_node_connectivity(G, v, w, **kwargs))
  280. # Also for non adjacent pairs of neighbors of v
  281. for x, y in iter_func(neighbors(v), 2):
  282. if y in G[x]:
  283. continue
  284. kwargs["cutoff"] = K
  285. K = min(K, local_node_connectivity(G, x, y, **kwargs))
  286. return K
  287. def average_node_connectivity(G, flow_func=None):
  288. r"""Returns the average connectivity of a graph G.
  289. The average connectivity `\bar{\kappa}` of a graph G is the average
  290. of local node connectivity over all pairs of nodes of G [1]_ .
  291. .. math::
  292. \bar{\kappa}(G) = \frac{\sum_{u,v} \kappa_{G}(u,v)}{{n \choose 2}}
  293. Parameters
  294. ----------
  295. G : NetworkX graph
  296. Undirected graph
  297. flow_func : function
  298. A function for computing the maximum flow among a pair of nodes.
  299. The function has to accept at least three parameters: a Digraph,
  300. a source node, and a target node. And return a residual network
  301. that follows NetworkX conventions (see :meth:`maximum_flow` for
  302. details). If flow_func is None, the default maximum flow function
  303. (:meth:`edmonds_karp`) is used. See :meth:`local_node_connectivity`
  304. for details. The choice of the default function may change from
  305. version to version and should not be relied on. Default value: None.
  306. Returns
  307. -------
  308. K : float
  309. Average node connectivity
  310. See also
  311. --------
  312. :meth:`local_node_connectivity`
  313. :meth:`node_connectivity`
  314. :meth:`edge_connectivity`
  315. :meth:`maximum_flow`
  316. :meth:`edmonds_karp`
  317. :meth:`preflow_push`
  318. :meth:`shortest_augmenting_path`
  319. References
  320. ----------
  321. .. [1] Beineke, L., O. Oellermann, and R. Pippert (2002). The average
  322. connectivity of a graph. Discrete mathematics 252(1-3), 31-45.
  323. http://www.sciencedirect.com/science/article/pii/S0012365X01001807
  324. """
  325. if G.is_directed():
  326. iter_func = itertools.permutations
  327. else:
  328. iter_func = itertools.combinations
  329. # Reuse the auxiliary digraph and the residual network
  330. H = build_auxiliary_node_connectivity(G)
  331. R = build_residual_network(H, "capacity")
  332. kwargs = {"flow_func": flow_func, "auxiliary": H, "residual": R}
  333. num, den = 0, 0
  334. for u, v in iter_func(G, 2):
  335. num += local_node_connectivity(G, u, v, **kwargs)
  336. den += 1
  337. if den == 0: # Null Graph
  338. return 0
  339. return num / den
  340. def all_pairs_node_connectivity(G, nbunch=None, flow_func=None):
  341. """Compute node connectivity between all pairs of nodes of G.
  342. Parameters
  343. ----------
  344. G : NetworkX graph
  345. Undirected graph
  346. nbunch: container
  347. Container of nodes. If provided node connectivity will be computed
  348. only over pairs of nodes in nbunch.
  349. flow_func : function
  350. A function for computing the maximum flow among a pair of nodes.
  351. The function has to accept at least three parameters: a Digraph,
  352. a source node, and a target node. And return a residual network
  353. that follows NetworkX conventions (see :meth:`maximum_flow` for
  354. details). If flow_func is None, the default maximum flow function
  355. (:meth:`edmonds_karp`) is used. See below for details. The
  356. choice of the default function may change from version
  357. to version and should not be relied on. Default value: None.
  358. Returns
  359. -------
  360. all_pairs : dict
  361. A dictionary with node connectivity between all pairs of nodes
  362. in G, or in nbunch if provided.
  363. See also
  364. --------
  365. :meth:`local_node_connectivity`
  366. :meth:`edge_connectivity`
  367. :meth:`local_edge_connectivity`
  368. :meth:`maximum_flow`
  369. :meth:`edmonds_karp`
  370. :meth:`preflow_push`
  371. :meth:`shortest_augmenting_path`
  372. """
  373. if nbunch is None:
  374. nbunch = G
  375. else:
  376. nbunch = set(nbunch)
  377. directed = G.is_directed()
  378. if directed:
  379. iter_func = itertools.permutations
  380. else:
  381. iter_func = itertools.combinations
  382. all_pairs = {n: {} for n in nbunch}
  383. # Reuse auxiliary digraph and residual network
  384. H = build_auxiliary_node_connectivity(G)
  385. mapping = H.graph["mapping"]
  386. R = build_residual_network(H, "capacity")
  387. kwargs = {"flow_func": flow_func, "auxiliary": H, "residual": R}
  388. for u, v in iter_func(nbunch, 2):
  389. K = local_node_connectivity(G, u, v, **kwargs)
  390. all_pairs[u][v] = K
  391. if not directed:
  392. all_pairs[v][u] = K
  393. return all_pairs
  394. def local_edge_connectivity(
  395. G, s, t, flow_func=None, auxiliary=None, residual=None, cutoff=None
  396. ):
  397. r"""Returns local edge connectivity for nodes s and t in G.
  398. Local edge connectivity for two nodes s and t is the minimum number
  399. of edges that must be removed to disconnect them.
  400. This is a flow based implementation of edge connectivity. We compute the
  401. maximum flow on an auxiliary digraph build from the original
  402. network (see below for details). This is equal to the local edge
  403. connectivity because the value of a maximum s-t-flow is equal to the
  404. capacity of a minimum s-t-cut (Ford and Fulkerson theorem) [1]_ .
  405. Parameters
  406. ----------
  407. G : NetworkX graph
  408. Undirected or directed graph
  409. s : node
  410. Source node
  411. t : node
  412. Target node
  413. flow_func : function
  414. A function for computing the maximum flow among a pair of nodes.
  415. The function has to accept at least three parameters: a Digraph,
  416. a source node, and a target node. And return a residual network
  417. that follows NetworkX conventions (see :meth:`maximum_flow` for
  418. details). If flow_func is None, the default maximum flow function
  419. (:meth:`edmonds_karp`) is used. See below for details. The
  420. choice of the default function may change from version
  421. to version and should not be relied on. Default value: None.
  422. auxiliary : NetworkX DiGraph
  423. Auxiliary digraph for computing flow based edge connectivity. If
  424. provided it will be reused instead of recreated. Default value: None.
  425. residual : NetworkX DiGraph
  426. Residual network to compute maximum flow. If provided it will be
  427. reused instead of recreated. Default value: None.
  428. cutoff : integer, float, or None (default: None)
  429. If specified, the maximum flow algorithm will terminate when the
  430. flow value reaches or exceeds the cutoff. This only works for flows
  431. that support the cutoff parameter (most do) and is ignored otherwise.
  432. Returns
  433. -------
  434. K : integer
  435. local edge connectivity for nodes s and t.
  436. Examples
  437. --------
  438. This function is not imported in the base NetworkX namespace, so you
  439. have to explicitly import it from the connectivity package:
  440. >>> from networkx.algorithms.connectivity import local_edge_connectivity
  441. We use in this example the platonic icosahedral graph, which has edge
  442. connectivity 5.
  443. >>> G = nx.icosahedral_graph()
  444. >>> local_edge_connectivity(G, 0, 6)
  445. 5
  446. If you need to compute local connectivity on several pairs of
  447. nodes in the same graph, it is recommended that you reuse the
  448. data structures that NetworkX uses in the computation: the
  449. auxiliary digraph for edge connectivity, and the residual
  450. network for the underlying maximum flow computation.
  451. Example of how to compute local edge connectivity among
  452. all pairs of nodes of the platonic icosahedral graph reusing
  453. the data structures.
  454. >>> import itertools
  455. >>> # You also have to explicitly import the function for
  456. >>> # building the auxiliary digraph from the connectivity package
  457. >>> from networkx.algorithms.connectivity import build_auxiliary_edge_connectivity
  458. >>> H = build_auxiliary_edge_connectivity(G)
  459. >>> # And the function for building the residual network from the
  460. >>> # flow package
  461. >>> from networkx.algorithms.flow import build_residual_network
  462. >>> # Note that the auxiliary digraph has an edge attribute named capacity
  463. >>> R = build_residual_network(H, "capacity")
  464. >>> result = dict.fromkeys(G, dict())
  465. >>> # Reuse the auxiliary digraph and the residual network by passing them
  466. >>> # as parameters
  467. >>> for u, v in itertools.combinations(G, 2):
  468. ... k = local_edge_connectivity(G, u, v, auxiliary=H, residual=R)
  469. ... result[u][v] = k
  470. >>> all(result[u][v] == 5 for u, v in itertools.combinations(G, 2))
  471. True
  472. You can also use alternative flow algorithms for computing edge
  473. connectivity. For instance, in dense networks the algorithm
  474. :meth:`shortest_augmenting_path` will usually perform better than
  475. the default :meth:`edmonds_karp` which is faster for sparse
  476. networks with highly skewed degree distributions. Alternative flow
  477. functions have to be explicitly imported from the flow package.
  478. >>> from networkx.algorithms.flow import shortest_augmenting_path
  479. >>> local_edge_connectivity(G, 0, 6, flow_func=shortest_augmenting_path)
  480. 5
  481. Notes
  482. -----
  483. This is a flow based implementation of edge connectivity. We compute the
  484. maximum flow using, by default, the :meth:`edmonds_karp` algorithm on an
  485. auxiliary digraph build from the original input graph:
  486. If the input graph is undirected, we replace each edge (`u`,`v`) with
  487. two reciprocal arcs (`u`, `v`) and (`v`, `u`) and then we set the attribute
  488. 'capacity' for each arc to 1. If the input graph is directed we simply
  489. add the 'capacity' attribute. This is an implementation of algorithm 1
  490. in [1]_.
  491. The maximum flow in the auxiliary network is equal to the local edge
  492. connectivity because the value of a maximum s-t-flow is equal to the
  493. capacity of a minimum s-t-cut (Ford and Fulkerson theorem).
  494. See also
  495. --------
  496. :meth:`edge_connectivity`
  497. :meth:`local_node_connectivity`
  498. :meth:`node_connectivity`
  499. :meth:`maximum_flow`
  500. :meth:`edmonds_karp`
  501. :meth:`preflow_push`
  502. :meth:`shortest_augmenting_path`
  503. References
  504. ----------
  505. .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms.
  506. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
  507. """
  508. if flow_func is None:
  509. flow_func = default_flow_func
  510. if auxiliary is None:
  511. H = build_auxiliary_edge_connectivity(G)
  512. else:
  513. H = auxiliary
  514. kwargs = {"flow_func": flow_func, "residual": residual}
  515. if flow_func is shortest_augmenting_path:
  516. kwargs["cutoff"] = cutoff
  517. kwargs["two_phase"] = True
  518. elif flow_func is edmonds_karp:
  519. kwargs["cutoff"] = cutoff
  520. elif flow_func is dinitz:
  521. kwargs["cutoff"] = cutoff
  522. elif flow_func is boykov_kolmogorov:
  523. kwargs["cutoff"] = cutoff
  524. return nx.maximum_flow_value(H, s, t, **kwargs)
  525. def edge_connectivity(G, s=None, t=None, flow_func=None, cutoff=None):
  526. r"""Returns the edge connectivity of the graph or digraph G.
  527. The edge connectivity is equal to the minimum number of edges that
  528. must be removed to disconnect G or render it trivial. If source
  529. and target nodes are provided, this function returns the local edge
  530. connectivity: the minimum number of edges that must be removed to
  531. break all paths from source to target in G.
  532. Parameters
  533. ----------
  534. G : NetworkX graph
  535. Undirected or directed graph
  536. s : node
  537. Source node. Optional. Default value: None.
  538. t : node
  539. Target node. Optional. Default value: None.
  540. flow_func : function
  541. A function for computing the maximum flow among a pair of nodes.
  542. The function has to accept at least three parameters: a Digraph,
  543. a source node, and a target node. And return a residual network
  544. that follows NetworkX conventions (see :meth:`maximum_flow` for
  545. details). If flow_func is None, the default maximum flow function
  546. (:meth:`edmonds_karp`) is used. See below for details. The
  547. choice of the default function may change from version
  548. to version and should not be relied on. Default value: None.
  549. cutoff : integer, float, or None (default: None)
  550. If specified, the maximum flow algorithm will terminate when the
  551. flow value reaches or exceeds the cutoff. This only works for flows
  552. that support the cutoff parameter (most do) and is ignored otherwise.
  553. Returns
  554. -------
  555. K : integer
  556. Edge connectivity for G, or local edge connectivity if source
  557. and target were provided
  558. Examples
  559. --------
  560. >>> # Platonic icosahedral graph is 5-edge-connected
  561. >>> G = nx.icosahedral_graph()
  562. >>> nx.edge_connectivity(G)
  563. 5
  564. You can use alternative flow algorithms for the underlying
  565. maximum flow computation. In dense networks the algorithm
  566. :meth:`shortest_augmenting_path` will usually perform better
  567. than the default :meth:`edmonds_karp`, which is faster for
  568. sparse networks with highly skewed degree distributions.
  569. Alternative flow functions have to be explicitly imported
  570. from the flow package.
  571. >>> from networkx.algorithms.flow import shortest_augmenting_path
  572. >>> nx.edge_connectivity(G, flow_func=shortest_augmenting_path)
  573. 5
  574. If you specify a pair of nodes (source and target) as parameters,
  575. this function returns the value of local edge connectivity.
  576. >>> nx.edge_connectivity(G, 3, 7)
  577. 5
  578. If you need to perform several local computations among different
  579. pairs of nodes on the same graph, it is recommended that you reuse
  580. the data structures used in the maximum flow computations. See
  581. :meth:`local_edge_connectivity` for details.
  582. Notes
  583. -----
  584. This is a flow based implementation of global edge connectivity.
  585. For undirected graphs the algorithm works by finding a 'small'
  586. dominating set of nodes of G (see algorithm 7 in [1]_ ) and
  587. computing local maximum flow (see :meth:`local_edge_connectivity`)
  588. between an arbitrary node in the dominating set and the rest of
  589. nodes in it. This is an implementation of algorithm 6 in [1]_ .
  590. For directed graphs, the algorithm does n calls to the maximum
  591. flow function. This is an implementation of algorithm 8 in [1]_ .
  592. See also
  593. --------
  594. :meth:`local_edge_connectivity`
  595. :meth:`local_node_connectivity`
  596. :meth:`node_connectivity`
  597. :meth:`maximum_flow`
  598. :meth:`edmonds_karp`
  599. :meth:`preflow_push`
  600. :meth:`shortest_augmenting_path`
  601. :meth:`k_edge_components`
  602. :meth:`k_edge_subgraphs`
  603. References
  604. ----------
  605. .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms.
  606. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf
  607. """
  608. if (s is not None and t is None) or (s is None and t is not None):
  609. raise nx.NetworkXError("Both source and target must be specified.")
  610. # Local edge connectivity
  611. if s is not None and t is not None:
  612. if s not in G:
  613. raise nx.NetworkXError(f"node {s} not in graph")
  614. if t not in G:
  615. raise nx.NetworkXError(f"node {t} not in graph")
  616. return local_edge_connectivity(G, s, t, flow_func=flow_func, cutoff=cutoff)
  617. # Global edge connectivity
  618. # reuse auxiliary digraph and residual network
  619. H = build_auxiliary_edge_connectivity(G)
  620. R = build_residual_network(H, "capacity")
  621. kwargs = {"flow_func": flow_func, "auxiliary": H, "residual": R}
  622. if G.is_directed():
  623. # Algorithm 8 in [1]
  624. if not nx.is_weakly_connected(G):
  625. return 0
  626. # initial value for \lambda is minimum degree
  627. L = min(d for n, d in G.degree())
  628. nodes = list(G)
  629. n = len(nodes)
  630. if cutoff is not None:
  631. L = min(cutoff, L)
  632. for i in range(n):
  633. kwargs["cutoff"] = L
  634. try:
  635. L = min(L, local_edge_connectivity(G, nodes[i], nodes[i + 1], **kwargs))
  636. except IndexError: # last node!
  637. L = min(L, local_edge_connectivity(G, nodes[i], nodes[0], **kwargs))
  638. return L
  639. else: # undirected
  640. # Algorithm 6 in [1]
  641. if not nx.is_connected(G):
  642. return 0
  643. # initial value for \lambda is minimum degree
  644. L = min(d for n, d in G.degree())
  645. if cutoff is not None:
  646. L = min(cutoff, L)
  647. # A dominating set is \lambda-covering
  648. # We need a dominating set with at least two nodes
  649. for node in G:
  650. D = nx.dominating_set(G, start_with=node)
  651. v = D.pop()
  652. if D:
  653. break
  654. else:
  655. # in complete graphs the dominating sets will always be of one node
  656. # thus we return min degree
  657. return L
  658. for w in D:
  659. kwargs["cutoff"] = L
  660. L = min(L, local_edge_connectivity(G, v, w, **kwargs))
  661. return L