Spaces:
Running
Running
""" | |
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 | |