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