| """ |
| Flow based cut algorithms |
| """ |
|
|
| import itertools |
|
|
| import networkx as nx |
|
|
| |
| |
| from networkx.algorithms.flow import build_residual_network, edmonds_karp |
|
|
| from .utils import build_auxiliary_edge_connectivity, build_auxiliary_node_connectivity |
|
|
| default_flow_func = edmonds_karp |
|
|
| __all__ = [ |
| "minimum_st_node_cut", |
| "minimum_node_cut", |
| "minimum_st_edge_cut", |
| "minimum_edge_cut", |
| ] |
|
|
|
|
| @nx._dispatchable( |
| graphs={"G": 0, "auxiliary?": 4}, |
| preserve_edge_attrs={"auxiliary": {"capacity": float("inf")}}, |
| preserve_graph_attrs={"auxiliary"}, |
| ) |
| 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 |
| |
| |
| 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 |
|
|
|
|
| @nx._dispatchable( |
| graphs={"G": 0, "auxiliary?": 4}, |
| preserve_node_attrs={"auxiliary": {"id": None}}, |
| preserve_graph_attrs={"auxiliary"}, |
| ) |
| 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. |
| |
| Returns an empty set if source and target are either in different |
| components or are directly connected by an edge, as no node removal |
| can destroy the 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_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 set() |
| kwargs = {"flow_func": flow_func, "residual": residual, "auxiliary": H} |
|
|
| |
| |
| edge_cut = minimum_st_edge_cut(H, f"{mapping[s]}B", f"{mapping[t]}A", **kwargs) |
| |
| node_cut = {H.nodes[node]["id"] for edge in edge_cut for node in edge} |
| return node_cut - {s, t} |
|
|
|
|
| @nx._dispatchable |
| 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.") |
|
|
| |
| 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) |
|
|
| |
| |
| 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 |
|
|
| |
| H = build_auxiliary_node_connectivity(G) |
| R = build_residual_network(H, "capacity") |
| kwargs = {"flow_func": flow_func, "auxiliary": H, "residual": R} |
|
|
| |
| v = min(G, key=G.degree) |
| |
| min_cut = set(G[v]) |
| |
| 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 |
| |
| 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 |
|
|
|
|
| @nx._dispatchable |
| 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.") |
|
|
| |
| H = build_auxiliary_edge_connectivity(G) |
| R = build_residual_network(H, "capacity") |
| kwargs = {"flow_func": flow_func, "residual": R, "auxiliary": H} |
|
|
| |
| 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) |
|
|
| |
| |
| if G.is_directed(): |
| |
| if not nx.is_weakly_connected(G): |
| raise nx.NetworkXError("Input graph is not connected") |
|
|
| |
| 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: |
| 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: |
| |
| if not nx.is_connected(G): |
| raise nx.NetworkXError("Input graph is not connected") |
|
|
| |
| node = min(G, key=G.degree) |
| min_cut = set(G.edges(node)) |
| |
| |
| for node in G: |
| D = nx.dominating_set(G, start_with=node) |
| v = D.pop() |
| if D: |
| break |
| else: |
| |
| |
| |
| 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 |
|
|