123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- """
- Flow based cut algorithms
- """
- import itertools
- import networkx as nx
- # Define the default maximum flow function to use in all flow based
- # cut algorithms.
- from networkx.algorithms.flow import build_residual_network, edmonds_karp
- default_flow_func = edmonds_karp
- from .utils import build_auxiliary_edge_connectivity, build_auxiliary_node_connectivity
- __all__ = [
- "minimum_st_node_cut",
- "minimum_node_cut",
- "minimum_st_edge_cut",
- "minimum_edge_cut",
- ]
- def minimum_st_edge_cut(G, s, t, flow_func=None, auxiliary=None, residual=None):
- """Returns the edges of the cut-set of a minimum (s, t)-cut.
- This function returns the set of edges of minimum cardinality that,
- if removed, would destroy all paths among source and target in G.
- Edge weights are not considered. See :meth:`minimum_cut` for
- computing minimum cuts considering edge weights.
- Parameters
- ----------
- G : NetworkX graph
- s : node
- Source node for the flow.
- t : node
- Sink node for the flow.
- auxiliary : NetworkX DiGraph
- Auxiliary digraph to compute flow based node connectivity. It has
- to have a graph attribute called mapping with a dictionary mapping
- node names in G and in the auxiliary digraph. If provided
- it will be reused instead of recreated. 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 :meth:`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.
- residual : NetworkX DiGraph
- Residual network to compute maximum flow. If provided it will be
- reused instead of recreated. Default value: None.
- Returns
- -------
- cutset : set
- Set of edges that, if removed from the graph, will disconnect it.
- See also
- --------
- :meth:`minimum_cut`
- :meth:`minimum_node_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`
- Examples
- --------
- This function is not imported in the base NetworkX namespace, so you
- have to explicitly import it from the connectivity package:
- >>> from networkx.algorithms.connectivity import minimum_st_edge_cut
- We use in this example the platonic icosahedral graph, which has edge
- connectivity 5.
- >>> G = nx.icosahedral_graph()
- >>> len(minimum_st_edge_cut(G, 0, 6))
- 5
- If you need to compute local edge cuts on several pairs of
- nodes in the same graph, it is recommended that you reuse the
- data structures that NetworkX uses in the computation: the
- auxiliary digraph for edge connectivity, and the residual
- network for the underlying maximum flow computation.
- Example of how to compute local edge cuts among all pairs of
- nodes of the platonic icosahedral graph reusing the data
- structures.
- >>> import itertools
- >>> # You also have to explicitly import the function for
- >>> # building the auxiliary digraph from the connectivity package
- >>> from networkx.algorithms.connectivity import build_auxiliary_edge_connectivity
- >>> H = build_auxiliary_edge_connectivity(G)
- >>> # And the function for building the residual network from the
- >>> # flow package
- >>> from networkx.algorithms.flow import build_residual_network
- >>> # Note that the auxiliary digraph has an edge attribute named capacity
- >>> R = build_residual_network(H, "capacity")
- >>> result = dict.fromkeys(G, dict())
- >>> # Reuse the auxiliary digraph and the residual network by passing them
- >>> # as parameters
- >>> for u, v in itertools.combinations(G, 2):
- ... k = len(minimum_st_edge_cut(G, u, v, auxiliary=H, residual=R))
- ... result[u][v] = k
- >>> all(result[u][v] == 5 for u, v in itertools.combinations(G, 2))
- True
- You can also use alternative flow algorithms for computing edge
- cuts. For instance, 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(minimum_st_edge_cut(G, 0, 6, flow_func=shortest_augmenting_path))
- 5
- """
- if flow_func is None:
- flow_func = default_flow_func
- if auxiliary is None:
- H = build_auxiliary_edge_connectivity(G)
- else:
- H = auxiliary
- kwargs = {"capacity": "capacity", "flow_func": flow_func, "residual": residual}
- cut_value, partition = nx.minimum_cut(H, s, t, **kwargs)
- reachable, non_reachable = partition
- # Any edge in the original graph linking the two sets in the
- # partition is part of the edge cutset
- cutset = set()
- for u, nbrs in ((n, G[n]) for n in reachable):
- cutset.update((u, v) for v in nbrs if v in non_reachable)
- return cutset
- def minimum_st_node_cut(G, s, t, flow_func=None, auxiliary=None, residual=None):
- r"""Returns a set of nodes of minimum cardinality that disconnect source
- from target in G.
- This function returns the set of nodes of minimum cardinality that,
- if removed, would destroy all paths among source and target in G.
- Parameters
- ----------
- G : NetworkX graph
- s : node
- Source node.
- t : node
- Target node.
- 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.
- auxiliary : NetworkX DiGraph
- Auxiliary digraph to compute flow based node connectivity. It has
- to have a graph attribute called mapping with a dictionary mapping
- node names in G and in the auxiliary digraph. If provided
- it will be reused instead of recreated. Default value: None.
- residual : NetworkX DiGraph
- Residual network to compute maximum flow. If provided it will be
- reused instead of recreated. Default value: None.
- Returns
- -------
- cutset : set
- Set of nodes that, if removed, would destroy all paths between
- source and target in G.
- Examples
- --------
- This function is not imported in the base NetworkX namespace, so you
- have to explicitly import it from the connectivity package:
- >>> from networkx.algorithms.connectivity import minimum_st_node_cut
- We use in this example the platonic icosahedral graph, which has node
- connectivity 5.
- >>> G = nx.icosahedral_graph()
- >>> len(minimum_st_node_cut(G, 0, 6))
- 5
- If you need to compute local st cuts between several pairs of
- nodes in the same graph, it is recommended that you reuse the
- data structures that NetworkX uses in the computation: the
- auxiliary digraph for node connectivity and node cuts, and the
- residual network for the underlying maximum flow computation.
- Example of how to compute local st node cuts reusing the data
- structures:
- >>> # You also have to explicitly import the function for
- >>> # building the auxiliary digraph from the connectivity package
- >>> from networkx.algorithms.connectivity import build_auxiliary_node_connectivity
- >>> H = build_auxiliary_node_connectivity(G)
- >>> # And the function for building the residual network from the
- >>> # flow package
- >>> from networkx.algorithms.flow import build_residual_network
- >>> # Note that the auxiliary digraph has an edge attribute named capacity
- >>> R = build_residual_network(H, "capacity")
- >>> # Reuse the auxiliary digraph and the residual network by passing them
- >>> # as parameters
- >>> len(minimum_st_node_cut(G, 0, 6, auxiliary=H, residual=R))
- 5
- You can also use alternative flow algorithms for computing minimum st
- node cuts. For instance, 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(minimum_st_node_cut(G, 0, 6, flow_func=shortest_augmenting_path))
- 5
- 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_node_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 auxiliary is None:
- H = build_auxiliary_node_connectivity(G)
- else:
- H = auxiliary
- mapping = H.graph.get("mapping", None)
- if mapping is None:
- raise nx.NetworkXError("Invalid auxiliary digraph.")
- if G.has_edge(s, t) or G.has_edge(t, s):
- return {}
- kwargs = {"flow_func": flow_func, "residual": residual, "auxiliary": H}
- # The edge cut in the auxiliary digraph corresponds to the node cut in the
- # original graph.
- edge_cut = minimum_st_edge_cut(H, f"{mapping[s]}B", f"{mapping[t]}A", **kwargs)
- # Each node in the original graph maps to two nodes of the auxiliary graph
- node_cut = {H.nodes[node]["id"] for edge in edge_cut for node in edge}
- return node_cut - {s, t}
- 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 contains 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(f"node {s} not in graph")
- if t not in G:
- raise nx.NetworkXError(f"node {t} not in graph")
- return minimum_st_node_cut(G, s, t, flow_func=flow_func)
- # Global minimum node cut.
- # Analog to the algorithm 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 = {"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)) - {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 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 contains 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 = {"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(f"node {s} not in graph")
- if t not in G:
- raise nx.NetworkXError(f"node {t} not in graph")
- return minimum_st_edge_cut(H, s, t, **kwargs)
- # Global minimum edge cut
- # Analog to the algorithm 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
|