connectivity.py 29 KB

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