def test_gl1(self): G = read_graph("gl1") s = 1 t = len(G) R = build_residual_network(G, "capacity") kwargs = dict(residual=R) for flow_func in flow_funcs: validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs), flow_func)
def test_wlm3(self): G = read_graph("wlm3") s = 1 t = len(G) R = build_residual_network(G, "capacity") kwargs = dict(residual=R) for flow_func in flow_funcs: validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs), flow_func)
def test_gw1(self): G = read_graph('gw1') s = 1 t = len(G) R = build_residual_network(G, 'capacity') kwargs = dict(residual=R) for flow_func in flow_funcs: validate_flows(G, s, t, 1202018, flow_func(G, s, t, **kwargs), flow_func)
def test_complete_graph(self): N = 50 G = nx.complete_graph(N) nx.set_edge_attributes(G, "capacity", 5) R = build_residual_network(G, "capacity") kwargs = dict(residual=R) for flow_func in flow_funcs: kwargs["flow_func"] = flow_func flow_value = nx.maximum_flow_value(G, 1, 2, **kwargs) assert_equal(flow_value, 5 * (N - 1), msg=msg.format(flow_func.__name__))
def test_pyramid(self): N = 10 # N = 100 # this gives a graph with 5051 nodes G = gen_pyramid(N) R = build_residual_network(G, "capacity") kwargs = dict(residual=R) for flow_func in flow_funcs: kwargs["flow_func"] = flow_func flow_value = nx.maximum_flow_value(G, (0, 0), "t", **kwargs) assert_almost_equal(flow_value, 1.0, msg=msg.format(flow_func.__name__))
def test_wlm3(self): G = read_graph('wlm3') s = 1 t = len(G) R = build_residual_network(G, 'capacity') kwargs = dict(residual=R) # do one flow_func to save time flow_func = flow_funcs[0] validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs), flow_func)
def test_reusing_residual(self): G = self.G fv = 3.0 s, t = 'x', 'y' R = build_residual_network(G, 'capacity') for interface_func in interface_funcs: for flow_func in flow_funcs: for i in range(3): result = interface_func(G, 'x', 'y', flow_func=flow_func, residual=R) if interface_func in max_min_funcs: result = result[0] assert_equal(fv, result, msg=msgi.format(flow_func.__name__, interface_func.__name__))
def average_node_connectivity(G, flow_func=None): r"""Returns the average connectivity of a graph G. The average connectivity `\bar{\kappa}` of a graph G is the average of local node connectivity over all pairs of nodes of G [1]_ . .. math:: \bar{\kappa}(G) = \frac{\sum_{u,v} \kappa_{G}(u,v)}{{n \choose 2}} Parameters ---------- G : NetworkX graph Undirected graph flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See :meth:`local_node_connectivity` for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- K : float Average node connectivity See also -------- :meth:`local_node_connectivity` :meth:`node_connectivity` :meth:`edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Beineke, L., O. Oellermann, and R. Pippert (2002). The average connectivity of a graph. Discrete mathematics 252(1-3), 31-45. http://www.sciencedirect.com/science/article/pii/S0012365X01001807 """ if G.is_directed(): iter_func = itertools.permutations else: iter_func = itertools.combinations # Reuse the auxiliary digraph and the residual network H = build_auxiliary_node_connectivity(G) R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) num, den = 0, 0 for u, v in iter_func(G, 2): num += local_node_connectivity(G, u, v, **kwargs) den += 1 if den == 0: # Null Graph return 0 return num / den
def node_connectivity(G, s=None, t=None, flow_func=None): r"""Returns node connectivity for a graph or digraph G. Node connectivity is equal to the minimum number of nodes that must be removed to disconnect G or render it trivial. If source and target nodes are provided, this function returns the local node connectivity: the minimum number of nodes that must be removed to break all paths from source to target in G. Parameters ---------- G : NetworkX graph Undirected graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- K : integer Node connectivity of G, or local node connectivity if source and target are provided. Examples -------- >>> # Platonic icosahedral graph is 5-node-connected >>> G = nx.icosahedral_graph() >>> nx.node_connectivity(G) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> nx.node_connectivity(G, flow_func=shortest_augmenting_path) 5 If you specify a pair of nodes (source and target) as parameters, this function returns the value of local node connectivity. >>> nx.node_connectivity(G, 3, 7) 5 If you need to perform several local computations among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`local_node_connectivity` for details. Notes ----- This is a flow based implementation of node connectivity. The algorithm works by solving `O((n-\delta-1+\delta(\delta-1)/2))` maximum flow problems on an auxiliary digraph. Where `\delta` is the minimum degree of G. For details about the auxiliary digraph and the computation of local node connectivity see :meth:`local_node_connectivity`. This implementation is based on algorithm 11 in [1]_. See also -------- :meth:`local_node_connectivity` :meth:`edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError('Both source and target must be specified.') # Local node connectivity if s is not None and t is not None: if s not in G: raise nx.NetworkXError('node %s not in graph' % s) if t not in G: raise nx.NetworkXError('node %s not in graph' % t) return local_node_connectivity(G, s, t, flow_func=flow_func) # Global node connectivity if G.is_directed(): if not nx.is_weakly_connected(G): return 0 iter_func = itertools.permutations # It is necessary to consider both predecessors # and successors for directed graphs def neighbors(v): return itertools.chain.from_iterable([G.predecessors_iter(v), G.successors_iter(v)]) else: if not nx.is_connected(G): return 0 iter_func = itertools.combinations neighbors = G.neighbors_iter # Reuse the auxiliary digraph and the residual network H = build_auxiliary_node_connectivity(G) R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) # Pick a node with minimum degree degree = G.degree() minimum_degree = min(degree.values()) v = next(n for n, d in degree.items() if d == minimum_degree) # Node connectivity is bounded by degree. K = minimum_degree # compute local node connectivity with all its non-neighbors nodes for w in set(G) - set(neighbors(v)) - set([v]): kwargs['cutoff'] = K K = min(K, local_node_connectivity(G, v, w, **kwargs)) # Also for non adjacent pairs of neighbors of v for x, y in iter_func(neighbors(v), 2): if y in G[x]: continue kwargs['cutoff'] = K K = min(K, local_node_connectivity(G, x, y, **kwargs)) return K
def all_node_cuts(G, k=None, flow_func=None): r"""Returns all minimum k cutsets of an undirected graph G. This implementation is based on Kanevsky's algorithm [1]_ for finding all minimum-size node cut-sets of an undirected graph G; ie the set (or sets) of nodes of cardinality equal to the node connectivity of G. Thus if removed, would break G into two or more connected components. Parameters ---------- G : NetworkX graph Undirected graph k : Integer Node connectivity of the input graph. If k is None, then it is computed. Default value: None. flow_func : function Function to perform the underlying flow computations. Default value edmonds_karp. This function performs better in sparse graphs with right tailed degree distributions. shortest_augmenting_path will perform better in denser graphs. Returns ------- cuts : a generator of node cutsets Each node cutset has cardinality equal to the node connectivity of the input graph. Examples -------- >>> # A two-dimensional grid graph has 4 cutsets of cardinality 2 >>> G = nx.grid_2d_graph(5, 5) >>> cutsets = list(nx.all_node_cuts(G)) >>> len(cutsets) 4 >>> all(2 == len(cutset) for cutset in cutsets) True >>> nx.node_connectivity(G) 2 Notes ----- This implementation is based on the sequential algorithm for finding all minimum-size separating vertex sets in a graph [1]_. The main idea is to compute minimum cuts using local maximum flow computations among a set of nodes of highest degree and all other non-adjacent nodes in the Graph. Once we find a minimum cut, we add an edge between the high degree node and the target node of the local maximum flow computation to make sure that we will not find that minimum cut again. See also -------- node_connectivity edmonds_karp shortest_augmenting_path References ---------- .. [1] Kanevsky, A. (1993). Finding all minimum-size separating vertex sets in a graph. Networks 23(6), 533--541. http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract """ if not nx.is_connected(G): raise nx.NetworkXError('Input graph is disconnected.') # Address some corner cases first. # For cycle graphs if G.order() == G.size(): if all(2 == d for n, d in G.degree()): seen = set() for u in G: for v in nx.non_neighbors(G, u): if (u, v) not in seen and (v, u) not in seen: yield {v, u} seen.add((v, u)) return # For complete Graphs if nx.density(G) == 1: for cut_set in combinations(G, len(G) - 1): yield set(cut_set) return # Initialize data structures. # Keep track of the cuts already computed so we do not repeat them. seen = [] # Even-Tarjan reduction is what we call auxiliary digraph # for node connectivity. H = build_auxiliary_node_connectivity(G) mapping = H.graph['mapping'] R = build_residual_network(H, 'capacity') kwargs = dict(capacity='capacity', residual=R) # Define default flow function if flow_func is None: flow_func = default_flow_func if flow_func is shortest_augmenting_path: kwargs['two_phase'] = True # Begin the actual algorithm # step 1: Find node connectivity k of G if k is None: k = nx.node_connectivity(G, flow_func=flow_func) # step 2: # Find k nodes with top degree, call it X: X = {n for n, d in sorted(G.degree(), key=itemgetter(1), reverse=True)[:k]} # Check if X is a k-node-cutset if _is_separating_set(G, X): seen.append(X) yield X for x in X: # step 3: Compute local connectivity flow of x with all other # non adjacent nodes in G non_adjacent = set(G) - X - set(G[x]) for v in non_adjacent: # step 4: compute maximum flow in an Even-Tarjan reduction H of G # and step:5 build the associated residual network R R = flow_func(H, '%sB' % mapping[x], '%sA' % mapping[v], **kwargs) flow_value = R.graph['flow_value'] if flow_value == k: # Remove saturated edges form the residual network saturated_edges = [(u, w, d) for (u, w, d) in R.edges(data=True) if d['capacity'] == d['flow']] R.remove_edges_from(saturated_edges) # step 6: shrink the strongly connected components of # residual flow network R and call it L L = nx.condensation(R) cmap = L.graph['mapping'] # step 7: Compute antichains of L; they map to closed sets in H # Any edge in H that links a closed set is part of a cutset for antichain in nx.antichains(L): # Nodes in an antichain of the condensation graph of # the residual network map to a closed set of nodes that # define a node partition of the auxiliary digraph H. S = {n for n, scc in cmap.items() if scc in antichain} # Find the cutset that links the node partition (S,~S) in H cutset = set() for u in S: cutset.update((u, w) for w in H[u] if w not in S) # The edges in H that form the cutset are internal edges # (ie edges that represent a node of the original graph G) node_cut = {H.nodes[n]['id'] for edge in cutset for n in edge} if len(node_cut) == k: if node_cut not in seen: yield node_cut seen.append(node_cut) # Add an edge (x, v) to make sure that we do not # find this cutset again. This is equivalent # of adding the edge in the input graph # G.add_edge(x, v) and then regenerate H and R: # Add edges to the auxiliary digraph. H.add_edge('%sB' % mapping[x], '%sA' % mapping[v], capacity=1) H.add_edge('%sB' % mapping[v], '%sA' % mapping[x], capacity=1) # Add edges to the residual network. R.add_edge('%sB' % mapping[x], '%sA' % mapping[v], capacity=1) R.add_edge('%sA' % mapping[v], '%sB' % mapping[x], capacity=1) break # Add again the saturated edges to reuse the residual network R.add_edges_from(saturated_edges)
def all_pairs_node_connectivity(G, nbunch=None, flow_func=None): """Compute node connectivity between all pairs of nodes of G. Parameters ---------- G : NetworkX graph Undirected graph nbunch: container Container of nodes. If provided node connectivity will be computed only over pairs of nodes in nbunch. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- all_pairs : dict A dictionary with node connectivity between all pairs of nodes in G, or in nbunch if provided. See also -------- :meth:`local_node_connectivity` :meth:`edge_connectivity` :meth:`local_edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` """ if nbunch is None: nbunch = G else: nbunch = set(nbunch) directed = G.is_directed() if directed: iter_func = itertools.permutations else: iter_func = itertools.combinations all_pairs = {n: {} for n in nbunch} # Reuse auxiliary digraph and residual network H = build_auxiliary_node_connectivity(G) mapping = H.graph['mapping'] R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) for u, v in iter_func(nbunch, 2): K = local_node_connectivity(G, u, v, **kwargs) all_pairs[u][v] = K if not directed: all_pairs[v][u] = K return all_pairs
def minimum_edge_cut(G, s=None, t=None, flow_func=None): r"""Returns a set of edges of minimum cardinality that disconnects G. If source and target nodes are provided, this function returns the set of edges of minimum cardinality that, if removed, would break all paths among source and target in G. If not, it returns a set of edges of minimum cardinality that disconnects G. Parameters ---------- G : NetworkX graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- cutset : set Set of edges that, if removed, would disconnect G. If source and target nodes are provided, the set contians the edges that if removed, would destroy all paths between source and target. Examples -------- >>> # Platonic icosahedral graph has edge connectivity 5 >>> G = nx.icosahedral_graph() >>> len(nx.minimum_edge_cut(G)) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> len(nx.minimum_edge_cut(G, flow_func=shortest_augmenting_path)) 5 If you specify a pair of nodes (source and target) as parameters, this function returns the value of local edge connectivity. >>> nx.edge_connectivity(G, 3, 7) 5 If you need to perform several local computations among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`local_edge_connectivity` for details. Notes ----- This is a flow based implementation of minimum edge cut. For undirected graphs the algorithm works by finding a 'small' dominating set of nodes of G (see algorithm 7 in [1]_) and computing the maximum flow between an arbitrary node in the dominating set and the rest of nodes in it. This is an implementation of algorithm 6 in [1]_. For directed graphs, the algorithm does n calls to the max flow function. The function raises an error if the directed graph is not weakly connected and returns an empty set if it is weakly connected. It is an implementation of algorithm 8 in [1]_. See also -------- :meth:`minimum_st_edge_cut` :meth:`minimum_node_cut` :meth:`stoer_wagner` :meth:`node_connectivity` :meth:`edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError("Both source and target must be specified.") # reuse auxiliary digraph and residual network H = build_auxiliary_edge_connectivity(G) R = build_residual_network(H, "capacity") kwargs = dict(flow_func=flow_func, residual=R, auxiliary=H) # Local minimum edge cut if s and t are not None if s is not None and t is not None: if s not in G: raise nx.NetworkXError("node %s not in graph" % s) if t not in G: raise nx.NetworkXError("node %s not in graph" % t) return minimum_st_edge_cut(H, s, t, **kwargs) # Global minimum edge cut # Analog to the algoritm for global edge connectivity if G.is_directed(): # Based on algorithm 8 in [1] if not nx.is_weakly_connected(G): raise nx.NetworkXError("Input graph is not connected") # Initial cutset is all edges of a node with minimum degree node = min(G, key=G.degree) min_cut = set(G.edges(node)) nodes = list(G) n = len(nodes) for i in range(n): try: this_cut = minimum_st_edge_cut(H, nodes[i], nodes[i + 1], **kwargs) if len(this_cut) <= len(min_cut): min_cut = this_cut except IndexError: # Last node! this_cut = minimum_st_edge_cut(H, nodes[i], nodes[0], **kwargs) if len(this_cut) <= len(min_cut): min_cut = this_cut return min_cut else: # undirected # Based on algorithm 6 in [1] if not nx.is_connected(G): raise nx.NetworkXError("Input graph is not connected") # Initial cutset is all edges of a node with minimum degree node = min(G, key=G.degree) min_cut = set(G.edges(node)) # A dominating set is \lambda-covering # We need a dominating set with at least two nodes for node in G: D = nx.dominating_set(G, start_with=node) v = D.pop() if D: break else: # in complete graphs the dominating set will always be of one node # thus we return min_cut, which now contains the edges of a node # with minimum degree return min_cut for w in D: this_cut = minimum_st_edge_cut(H, v, w, **kwargs) if len(this_cut) <= len(min_cut): min_cut = this_cut return min_cut
def minimum_node_cut(G, s=None, t=None, flow_func=None): r"""Returns a set of nodes of minimum cardinality that disconnects G. If source and target nodes are provided, this function returns the set of nodes of minimum cardinality that, if removed, would destroy all paths among source and target in G. If not, it returns a set of nodes of minimum cardinality that disconnects G. Parameters ---------- G : NetworkX graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- cutset : set Set of nodes that, if removed, would disconnect G. If source and target nodes are provided, the set contians the nodes that if removed, would destroy all paths between source and target. Examples -------- >>> # Platonic icosahedral graph has node connectivity 5 >>> G = nx.icosahedral_graph() >>> node_cut = nx.minimum_node_cut(G) >>> len(node_cut) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> node_cut == nx.minimum_node_cut(G, flow_func=shortest_augmenting_path) True If you specify a pair of nodes (source and target) as parameters, this function returns a local st node cut. >>> len(nx.minimum_node_cut(G, 3, 7)) 5 If you need to perform several local st cuts among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`minimum_st_node_cut` for details. Notes ----- This is a flow based implementation of minimum node cut. The algorithm is based in solving a number of maximum flow computations to determine the capacity of the minimum cut on an auxiliary directed network that corresponds to the minimum node cut of G. It handles both directed and undirected graphs. This implementation is based on algorithm 11 in [1]_. See also -------- :meth:`minimum_st_node_cut` :meth:`minimum_cut` :meth:`minimum_edge_cut` :meth:`stoer_wagner` :meth:`node_connectivity` :meth:`edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError("Both source and target must be specified.") # Local minimum node cut. if s is not None and t is not None: if s not in G: raise nx.NetworkXError("node %s not in graph" % s) if t not in G: raise nx.NetworkXError("node %s not in graph" % t) return minimum_st_node_cut(G, s, t, flow_func=flow_func) # Global minimum node cut. # Analog to the algoritm 11 for global node connectivity in [1]. if G.is_directed(): if not nx.is_weakly_connected(G): raise nx.NetworkXError("Input graph is not connected") iter_func = itertools.permutations def neighbors(v): return itertools.chain.from_iterable([G.predecessors(v), G.successors(v)]) else: if not nx.is_connected(G): raise nx.NetworkXError("Input graph is not connected") iter_func = itertools.combinations neighbors = G.neighbors # Reuse the auxiliary digraph and the residual network. H = build_auxiliary_node_connectivity(G) R = build_residual_network(H, "capacity") kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) # Choose a node with minimum degree. v = min(G, key=G.degree) # Initial node cutset is all neighbors of the node with minimum degree. min_cut = set(G[v]) # Compute st node cuts between v and all its non-neighbors nodes in G. for w in set(G) - set(neighbors(v)) - set([v]): this_cut = minimum_st_node_cut(G, v, w, **kwargs) if len(min_cut) >= len(this_cut): min_cut = this_cut # Also for non adjacent pairs of neighbors of v. for x, y in iter_func(neighbors(v), 2): if y in G[x]: continue this_cut = minimum_st_node_cut(G, x, y, **kwargs) if len(min_cut) >= len(this_cut): min_cut = this_cut return min_cut
def edge_connectivity(G, s=None, t=None, flow_func=None): r"""Returns the edge connectivity of the graph or digraph G. The edge connectivity is equal to the minimum number of edges that must be removed to disconnect G or render it trivial. If source and target nodes are provided, this function returns the local edge connectivity: the minimum number of edges that must be removed to break all paths from source to target in G. Parameters ---------- G : NetworkX graph Undirected or directed graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- K : integer Edge connectivity for G, or local edge connectivity if source and target were provided Examples -------- >>> # Platonic icosahedral graph is 5-edge-connected >>> G = nx.icosahedral_graph() >>> nx.edge_connectivity(G) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> nx.edge_connectivity(G, flow_func=shortest_augmenting_path) 5 If you specify a pair of nodes (source and target) as parameters, this function returns the value of local edge connectivity. >>> nx.edge_connectivity(G, 3, 7) 5 If you need to perform several local computations among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`local_edge_connectivity` for details. Notes ----- This is a flow based implementation of global edge connectivity. For undirected graphs the algorithm works by finding a 'small' dominating set of nodes of G (see algorithm 7 in [1]_ ) and computing local maximum flow (see :meth:`local_edge_connectivity`) between an arbitrary node in the dominating set and the rest of nodes in it. This is an implementation of algorithm 6 in [1]_ . For directed graphs, the algorithm does n calls to the maximum flow function. This is an implementation of algorithm 8 in [1]_ . See also -------- :meth:`local_edge_connectivity` :meth:`local_node_connectivity` :meth:`node_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` :meth:`k_edge_components` :meth:`k_edge_subgraphs` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError('Both source and target must be specified.') # Local edge connectivity if s is not None and t is not None: if s not in G: raise nx.NetworkXError('node %s not in graph' % s) if t not in G: raise nx.NetworkXError('node %s not in graph' % t) return local_edge_connectivity(G, s, t, flow_func=flow_func) # Global edge connectivity # reuse auxiliary digraph and residual network H = build_auxiliary_edge_connectivity(G) R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) if G.is_directed(): # Algorithm 8 in [1] if not nx.is_weakly_connected(G): return 0 # initial value for \lambda is minimum degree L = min(d for n, d in G.degree()) nodes = list(G) n = len(nodes) for i in range(n): kwargs['cutoff'] = L try: L = min( L, local_edge_connectivity(G, nodes[i], nodes[i + 1], **kwargs)) except IndexError: # last node! L = min( L, local_edge_connectivity(G, nodes[i], nodes[0], **kwargs)) return L else: # undirected # Algorithm 6 in [1] if not nx.is_connected(G): return 0 # initial value for \lambda is minimum degree L = min(d for n, d in G.degree()) # A dominating set is \lambda-covering # We need a dominating set with at least two nodes for node in G: D = nx.dominating_set(G, start_with=node) v = D.pop() if D: break else: # in complete graphs the dominating sets will always be of one node # thus we return min degree return L for w in D: kwargs['cutoff'] = L L = min(L, local_edge_connectivity(G, v, w, **kwargs)) return L
def minimum_node_cut(G, s=None, t=None, flow_func=None): r"""Returns a set of nodes of minimum cardinality that disconnects G. If source and target nodes are provided, this function returns the set of nodes of minimum cardinality that, if removed, would destroy all paths among source and target in G. If not, it returns a set of nodes of minimum cardinality that disconnects G. Parameters ---------- G : NetworkX graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- cutset : set Set of nodes that, if removed, would disconnect G. If source and target nodes are provided, the set contians the nodes that if removed, would destroy all paths between source and target. Examples -------- >>> # Platonic icosahedral graph has node connectivity 5 >>> G = nx.icosahedral_graph() >>> node_cut = nx.minimum_node_cut(G) >>> len(node_cut) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> node_cut == nx.minimum_node_cut(G, flow_func=shortest_augmenting_path) True If you specify a pair of nodes (source and target) as parameters, this function returns a local st node cut. >>> len(nx.minimum_node_cut(G, 3, 7)) 5 If you need to perform several local st cuts among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`minimum_st_node_cut` for details. Notes ----- This is a flow based implementation of minimum node cut. The algorithm is based in solving a number of maximum flow computations to determine the capacity of the minimum cut on an auxiliary directed network that corresponds to the minimum node cut of G. It handles both directed and undirected graphs. This implementation is based on algorithm 11 in [1]_. See also -------- :meth:`minimum_st_node_cut` :meth:`minimum_cut` :meth:`minimum_edge_cut` :meth:`stoer_wagner` :meth:`node_connectivity` :meth:`edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError('Both source and target must be specified.') # Local minimum node cut. if s is not None and t is not None: if s not in G: raise nx.NetworkXError('node %s not in graph' % s) if t not in G: raise nx.NetworkXError('node %s not in graph' % t) return minimum_st_node_cut(G, s, t, flow_func=flow_func) # Global minimum node cut. # Analog to the algoritm 11 for global node connectivity in [1]. if G.is_directed(): if not nx.is_weakly_connected(G): raise nx.NetworkXError('Input graph is not connected') iter_func = itertools.permutations def neighbors(v): return itertools.chain.from_iterable( [G.predecessors(v), G.successors(v)]) else: if not nx.is_connected(G): raise nx.NetworkXError('Input graph is not connected') iter_func = itertools.combinations neighbors = G.neighbors # Reuse the auxiliary digraph and the residual network. H = build_auxiliary_node_connectivity(G) R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) # Choose a node with minimum degree. v = min(G, key=G.degree) # Initial node cutset is all neighbors of the node with minimum degree. min_cut = set(G[v]) # Compute st node cuts between v and all its non-neighbors nodes in G. for w in set(G) - set(neighbors(v)) - set([v]): this_cut = minimum_st_node_cut(G, v, w, **kwargs) if len(min_cut) >= len(this_cut): min_cut = this_cut # Also for non adjacent pairs of neighbors of v. for x, y in iter_func(neighbors(v), 2): if y in G[x]: continue this_cut = minimum_st_node_cut(G, x, y, **kwargs) if len(min_cut) >= len(this_cut): min_cut = this_cut return min_cut
def all_node_cuts(G, k=None, flow_func=None): r"""Returns all minimum k cutsets of an undirected graph G. This implementation is based on Kanevsky's algorithm [1]_ for finding all minimum-size node cut-sets of an undirected graph G; ie the set (or sets) of nodes of cardinality equal to the node connectivity of G. Thus if removed, would break G into two or more connected components. Parameters ---------- G : NetworkX graph Undirected graph k : Integer Node connectivity of the input graph. If k is None, then it is computed. Default value: None. flow_func : function Function to perform the underlying flow computations. Default value edmonds_karp. This function performs better in sparse graphs with right tailed degree distributions. shortest_augmenting_path will perform better in denser graphs. Returns ------- cuts : a generator of node cutsets Each node cutset has cardinality equal to the node connectivity of the input graph. Examples -------- >>> # A two-dimensional grid graph has 4 cutsets of cardinality 2 >>> G = nx.grid_2d_graph(5, 5) >>> cutsets = list(nx.all_node_cuts(G)) >>> len(cutsets) 4 >>> all(2 == len(cutset) for cutset in cutsets) True >>> nx.node_connectivity(G) 2 Notes ----- This implementation is based on the sequential algorithm for finding all minimum-size separating vertex sets in a graph [1]_. The main idea is to compute minimum cuts using local maximum flow computations among a set of nodes of highest degree and all other non-adjacent nodes in the Graph. Once we find a minimum cut, we add an edge between the high degree node and the target node of the local maximum flow computation to make sure that we will not find that minimum cut again. See also -------- node_connectivity edmonds_karp shortest_augmenting_path References ---------- .. [1] Kanevsky, A. (1993). Finding all minimum-size separating vertex sets in a graph. Networks 23(6), 533--541. http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract """ if not nx.is_connected(G): raise nx.NetworkXError('Input graph is disconnected.') # Initialize data structures. # Keep track of the cuts already computed so we do not repeat them. seen = [] # Even-Tarjan reduction is what we call auxiliary digraph # for node connectivity. H = build_auxiliary_node_connectivity(G) mapping = H.graph['mapping'] R = build_residual_network(H, 'capacity') kwargs = dict(capacity='capacity', residual=R) # Define default flow function if flow_func is None: flow_func = default_flow_func if flow_func is shortest_augmenting_path: kwargs['two_phase'] = True # Begin the actual algorithm # step 1: Find node connectivity k of G if k is None: k = nx.node_connectivity(G, flow_func=flow_func) # step 2: # Find k nodes with top degree, call it X: X = {n for n, d in sorted(G.degree(), key=itemgetter(1), reverse=True)[:k]} # Check if X is a k-node-cutset if _is_separating_set(G, X): seen.append(X) yield X for x in X: # step 3: Compute local connectivity flow of x with all other # non adjacent nodes in G non_adjacent = set(G) - X - set(G[x]) for v in non_adjacent: # step 4: compute maximum flow in an Even-Tarjan reduction H of G # and step:5 build the associated residual network R R = flow_func(H, '%sB' % mapping[x], '%sA' % mapping[v], **kwargs) flow_value = R.graph['flow_value'] if flow_value == k: ## Remove saturated edges form the residual network saturated_edges = [(u, w, d) for (u, w, d) in R.edges(data=True) if d['capacity'] == d['flow']] R.remove_edges_from(saturated_edges) # step 6: shrink the strongly connected components of # residual flow network R and call it L L = nx.condensation(R) cmap = L.graph['mapping'] # step 7: Compute antichains of L; they map to closed sets in H # Any edge in H that links a closed set is part of a cutset for antichain in nx.antichains(L): # Nodes in an antichain of the condensation graph of # the residual network map to a closed set of nodes that # define a node partition of the auxiliary digraph H. S = {n for n, scc in cmap.items() if scc in antichain} # Find the cutset that links the node partition (S,~S) in H cutset = set() for u in S: cutset.update((u, w) for w in H[u] if w not in S) # The edges in H that form the cutset are internal edges # (ie edges that represent a node of the original graph G) node_cut = { H.node[n]['id'] for edge in cutset for n in edge } if len(node_cut) == k: if node_cut not in seen: yield node_cut seen.append(node_cut) # Add an edge (x, v) to make sure that we do not # find this cutset again. This is equivalent # of adding the edge in the input graph # G.add_edge(x, v) and then regenerate H and R: # Add edges to the auxiliary digraph. H.add_edge('%sB' % mapping[x], '%sA' % mapping[v], capacity=1) H.add_edge('%sB' % mapping[v], '%sA' % mapping[x], capacity=1) # Add edges to the residual network. R.add_edge('%sB' % mapping[x], '%sA' % mapping[v], capacity=1) R.add_edge('%sA' % mapping[v], '%sB' % mapping[x], capacity=1) break # Add again the saturated edges to reuse the residual network R.add_edges_from(saturated_edges)
def minimum_edge_cut(G, s=None, t=None, flow_func=None): r"""Returns a set of edges of minimum cardinality that disconnects G. If source and target nodes are provided, this function returns the set of edges of minimum cardinality that, if removed, would break all paths among source and target in G. If not, it returns a set of edges of minimum cardinality that disconnects G. Parameters ---------- G : NetworkX graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- cutset : set Set of edges that, if removed, would disconnect G. If source and target nodes are provided, the set contians the edges that if removed, would destroy all paths between source and target. Examples -------- >>> # Platonic icosahedral graph has edge connectivity 5 >>> G = nx.icosahedral_graph() >>> len(nx.minimum_edge_cut(G)) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> len(nx.minimum_edge_cut(G, flow_func=shortest_augmenting_path)) 5 If you specify a pair of nodes (source and target) as parameters, this function returns the value of local edge connectivity. >>> nx.edge_connectivity(G, 3, 7) 5 If you need to perform several local computations among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`local_edge_connectivity` for details. Notes ----- This is a flow based implementation of minimum edge cut. For undirected graphs the algorithm works by finding a 'small' dominating set of nodes of G (see algorithm 7 in [1]_) and computing the maximum flow between an arbitrary node in the dominating set and the rest of nodes in it. This is an implementation of algorithm 6 in [1]_. For directed graphs, the algorithm does n calls to the max flow function. The function raises an error if the directed graph is not weakly connected and returns an empty set if it is weakly connected. It is an implementation of algorithm 8 in [1]_. See also -------- :meth:`minimum_st_edge_cut` :meth:`minimum_node_cut` :meth:`stoer_wagner` :meth:`node_connectivity` :meth:`edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError('Both source and target must be specified.') # reuse auxiliary digraph and residual network H = build_auxiliary_edge_connectivity(G) R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, residual=R, auxiliary=H) # Local minimum edge cut if s and t are not None if s is not None and t is not None: if s not in G: raise nx.NetworkXError('node %s not in graph' % s) if t not in G: raise nx.NetworkXError('node %s not in graph' % t) return minimum_st_edge_cut(H, s, t, **kwargs) # Global minimum edge cut # Analog to the algoritm for global edge connectivity if G.is_directed(): # Based on algorithm 8 in [1] if not nx.is_weakly_connected(G): raise nx.NetworkXError('Input graph is not connected') # Initial cutset is all edges of a node with minimum degree node = min(G, key=G.degree) min_cut = set(G.edges(node)) nodes = list(G) n = len(nodes) for i in range(n): try: this_cut = minimum_st_edge_cut(H, nodes[i], nodes[i + 1], **kwargs) if len(this_cut) <= len(min_cut): min_cut = this_cut except IndexError: # Last node! this_cut = minimum_st_edge_cut(H, nodes[i], nodes[0], **kwargs) if len(this_cut) <= len(min_cut): min_cut = this_cut return min_cut else: # undirected # Based on algorithm 6 in [1] if not nx.is_connected(G): raise nx.NetworkXError('Input graph is not connected') # Initial cutset is all edges of a node with minimum degree node = min(G, key=G.degree) min_cut = set(G.edges(node)) # A dominating set is \lambda-covering # We need a dominating set with at least two nodes for node in G: D = nx.dominating_set(G, start_with=node) v = D.pop() if D: break else: # in complete graphs the dominating set will always be of one node # thus we return min_cut, which now contains the edges of a node # with minimum degree return min_cut for w in D: this_cut = minimum_st_edge_cut(H, v, w, **kwargs) if len(this_cut) <= len(min_cut): min_cut = this_cut return min_cut
def edge_connectivity(G, s=None, t=None, flow_func=None): r"""Returns the edge connectivity of the graph or digraph G. The edge connectivity is equal to the minimum number of edges that must be removed to disconnect G or render it trivial. If source and target nodes are provided, this function returns the local edge connectivity: the minimum number of edges that must be removed to break all paths from source to target in G. Parameters ---------- G : NetworkX graph Undirected or directed graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- K : integer Edge connectivity for G, or local edge connectivity if source and target were provided Examples -------- >>> # Platonic icosahedral graph is 5-edge-connected >>> G = nx.icosahedral_graph() >>> nx.edge_connectivity(G) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> nx.edge_connectivity(G, flow_func=shortest_augmenting_path) 5 If you specify a pair of nodes (source and target) as parameters, this function returns the value of local edge connectivity. >>> nx.edge_connectivity(G, 3, 7) 5 If you need to perform several local computations among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`local_edge_connectivity` for details. Notes ----- This is a flow based implementation of global edge connectivity. For undirected graphs the algorithm works by finding a 'small' dominating set of nodes of G (see algorithm 7 in [1]_ ) and computing local maximum flow (see :meth:`local_edge_connectivity`) between an arbitrary node in the dominating set and the rest of nodes in it. This is an implementation of algorithm 6 in [1]_ . For directed graphs, the algorithm does n calls to the maximum flow function. This is an implementation of algorithm 8 in [1]_ . See also -------- :meth:`local_edge_connectivity` :meth:`local_node_connectivity` :meth:`node_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError('Both source and target must be specified.') # Local edge connectivity if s is not None and t is not None: if s not in G: raise nx.NetworkXError('node %s not in graph' % s) if t not in G: raise nx.NetworkXError('node %s not in graph' % t) return local_edge_connectivity(G, s, t, flow_func=flow_func) # Global edge connectivity # reuse auxiliary digraph and residual network H = build_auxiliary_edge_connectivity(G) R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) if G.is_directed(): # Algorithm 8 in [1] if not nx.is_weakly_connected(G): return 0 # initial value for \lambda is minimum degree L = min(G.degree().values()) nodes = G.nodes() n = len(nodes) for i in range(n): kwargs['cutoff'] = L try: L = min(L, local_edge_connectivity(G, nodes[i], nodes[i+1], **kwargs)) except IndexError: # last node! L = min(L, local_edge_connectivity(G, nodes[i], nodes[0], **kwargs)) return L else: # undirected # Algorithm 6 in [1] if not nx.is_connected(G): return 0 # initial value for \lambda is minimum degree L = min(G.degree().values()) # A dominating set is \lambda-covering # We need a dominating set with at least two nodes for node in G: D = nx.dominating_set(G, start_with=node) v = D.pop() if D: break else: # in complete graphs the dominating sets will always be of one node # thus we return min degree return L for w in D: kwargs['cutoff'] = L L = min(L, local_edge_connectivity(G, v, w, **kwargs)) return L
def node_connectivity(G, s=None, t=None, flow_func=None): """Returns node connectivity for a graph or digraph G. Node connectivity is equal to the minimum number of nodes that must be removed to disconnect G or render it trivial. If source and target nodes are provided, this function returns the local node connectivity: the minimum number of nodes that must be removed to break all paths from source to target in G. Parameters ---------- G : NetworkX graph Undirected graph s : node Source node. Optional. Default value: None. t : node Target node. Optional. Default value: None. flow_func : function A function for computing the maximum flow among a pair of nodes. The function has to accept at least three parameters: a Digraph, a source node, and a target node. And return a residual network that follows NetworkX conventions (see :meth:`maximum_flow` for details). If flow_func is None, the default maximum flow function (:meth:`edmonds_karp`) is used. See below for details. The choice of the default function may change from version to version and should not be relied on. Default value: None. Returns ------- K : integer Node connectivity of G, or local node connectivity if source and target are provided. Examples -------- >>> # Platonic icosahedral graph is 5-node-connected >>> G = nx.icosahedral_graph() >>> nx.node_connectivity(G) 5 You can use alternative flow algorithms for the underlying maximum flow computation. In dense networks the algorithm :meth:`shortest_augmenting_path` will usually perform better than the default :meth:`edmonds_karp`, which is faster for sparse networks with highly skewed degree distributions. Alternative flow functions have to be explicitly imported from the flow package. >>> from networkx.algorithms.flow import shortest_augmenting_path >>> nx.node_connectivity(G, flow_func=shortest_augmenting_path) 5 If you specify a pair of nodes (source and target) as parameters, this function returns the value of local node connectivity. >>> nx.node_connectivity(G, 3, 7) 5 If you need to perform several local computations among different pairs of nodes on the same graph, it is recommended that you reuse the data structures used in the maximum flow computations. See :meth:`local_node_connectivity` for details. Notes ----- This is a flow based implementation of node connectivity. The algorithm works by solving `O((n-\delta-1+\delta(\delta-1)/2))` maximum flow problems on an auxiliary digraph. Where `\delta` is the minimum degree of G. For details about the auxiliary digraph and the computation of local node connectivity see :meth:`local_node_connectivity`. This implementation is based on algorithm 11 in [1]_. See also -------- :meth:`local_node_connectivity` :meth:`edge_connectivity` :meth:`maximum_flow` :meth:`edmonds_karp` :meth:`preflow_push` :meth:`shortest_augmenting_path` References ---------- .. [1] Abdol-Hossein Esfahanian. Connectivity Algorithms. http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf """ if (s is not None and t is None) or (s is None and t is not None): raise nx.NetworkXError('Both source and target must be specified.') # Local node connectivity if s is not None and t is not None: if s not in G: raise nx.NetworkXError('node %s not in graph' % s) if t not in G: raise nx.NetworkXError('node %s not in graph' % t) return local_node_connectivity(G, s, t, flow_func=flow_func) # Global node connectivity if G.is_directed(): if not nx.is_weakly_connected(G): return 0 iter_func = itertools.permutations # It is necessary to consider both predecessors # and successors for directed graphs def neighbors(v): return itertools.chain.from_iterable( [G.predecessors(v), G.successors(v)]) else: if not nx.is_connected(G): return 0 iter_func = itertools.combinations neighbors = G.neighbors # Reuse the auxiliary digraph and the residual network H = build_auxiliary_node_connectivity(G) R = build_residual_network(H, 'capacity') kwargs = dict(flow_func=flow_func, auxiliary=H, residual=R) # Pick a node with minimum degree # Node connectivity is bounded by degree. v, K = min(G.degree(), key=itemgetter(1)) # compute local node connectivity with all its non-neighbors nodes for w in set(G) - set(neighbors(v)) - set([v]): kwargs['cutoff'] = K K = min(K, local_node_connectivity(G, v, w, **kwargs)) # Also for non adjacent pairs of neighbors of v for x, y in iter_func(neighbors(v), 2): if y in G[x]: continue kwargs['cutoff'] = K K = min(K, local_node_connectivity(G, x, y, **kwargs)) return K