animationInterpolation
/
.venv
/Lib
/site-packages
/networkx
/algorithms
/connectivity
/disjoint_paths.py
"""Flow based node and edge disjoint paths.""" | |
import networkx as nx | |
# Define the default maximum flow function to use for the underlying | |
# maximum flow computations | |
from networkx.algorithms.flow import ( | |
edmonds_karp, | |
preflow_push, | |
shortest_augmenting_path, | |
) | |
from networkx.exception import NetworkXNoPath | |
default_flow_func = edmonds_karp | |
from itertools import filterfalse as _filterfalse | |
# Functions to build auxiliary data structures. | |
from .utils import build_auxiliary_edge_connectivity, build_auxiliary_node_connectivity | |
__all__ = ["edge_disjoint_paths", "node_disjoint_paths"] | |
def edge_disjoint_paths( | |
G, s, t, flow_func=None, cutoff=None, auxiliary=None, residual=None | |
): | |
"""Returns the edges disjoint paths between source and target. | |
Edge disjoint paths are paths that do not share any edge. The | |
number of edge disjoint paths between source and target is equal | |
to their edge connectivity. | |
Parameters | |
---------- | |
G : NetworkX graph | |
s : node | |
Source node for the flow. | |
t : node | |
Sink node for the flow. | |
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. The choice of the default function | |
may change from version to version and should not be relied on. | |
Default value: None. | |
cutoff : integer or None (default: None) | |
Maximum number of paths to yield. If specified, the maximum flow | |
algorithm will terminate when the flow value reaches or exceeds the | |
cutoff. This only works for flows that support the cutoff parameter | |
(most do) and is ignored otherwise. | |
auxiliary : NetworkX DiGraph | |
Auxiliary digraph to compute flow based edge 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 | |
------- | |
paths : generator | |
A generator of edge independent paths. | |
Raises | |
------ | |
NetworkXNoPath | |
If there is no path between source and target. | |
NetworkXError | |
If source or target are not in the graph G. | |
See also | |
-------- | |
:meth:`node_disjoint_paths` | |
:meth:`edge_connectivity` | |
:meth:`maximum_flow` | |
:meth:`edmonds_karp` | |
:meth:`preflow_push` | |
:meth:`shortest_augmenting_path` | |
Examples | |
-------- | |
We use in this example the platonic icosahedral graph, which has node | |
edge connectivity 5, thus there are 5 edge disjoint paths between any | |
pair of nodes. | |
>>> G = nx.icosahedral_graph() | |
>>> len(list(nx.edge_disjoint_paths(G, 0, 6))) | |
5 | |
If you need to compute edge disjoint paths 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 edge disjoint paths 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 = {n: {} for n in G} | |
>>> # Reuse the auxiliary digraph and the residual network by passing them | |
>>> # as arguments | |
>>> for u, v in itertools.combinations(G, 2): | |
... k = len(list(nx.edge_disjoint_paths(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 disjoint | |
paths. 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(list(nx.edge_disjoint_paths(G, 0, 6, flow_func=shortest_augmenting_path))) | |
5 | |
Notes | |
----- | |
This is a flow based implementation of edge disjoint paths. We compute | |
the maximum flow between source and target on an auxiliary directed | |
network. The saturated edges in the residual network after running the | |
maximum flow algorithm correspond to edge disjoint paths between source | |
and target in the original network. This function handles both directed | |
and undirected graphs, and can use all flow algorithms from NetworkX flow | |
package. | |
""" | |
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") | |
if flow_func is None: | |
flow_func = default_flow_func | |
if auxiliary is None: | |
H = build_auxiliary_edge_connectivity(G) | |
else: | |
H = auxiliary | |
# Maximum possible edge disjoint paths | |
possible = min(H.out_degree(s), H.in_degree(t)) | |
if not possible: | |
raise NetworkXNoPath | |
if cutoff is None: | |
cutoff = possible | |
else: | |
cutoff = min(cutoff, possible) | |
# Compute maximum flow between source and target. Flow functions in | |
# NetworkX return a residual network. | |
kwargs = { | |
"capacity": "capacity", | |
"residual": residual, | |
"cutoff": cutoff, | |
"value_only": True, | |
} | |
if flow_func is preflow_push: | |
del kwargs["cutoff"] | |
if flow_func is shortest_augmenting_path: | |
kwargs["two_phase"] = True | |
R = flow_func(H, s, t, **kwargs) | |
if R.graph["flow_value"] == 0: | |
raise NetworkXNoPath | |
# Saturated edges in the residual network form the edge disjoint paths | |
# between source and target | |
cutset = [ | |
(u, v) | |
for u, v, d in R.edges(data=True) | |
if d["capacity"] == d["flow"] and d["flow"] > 0 | |
] | |
# This is equivalent of what flow.utils.build_flow_dict returns, but | |
# only for the nodes with saturated edges and without reporting 0 flows. | |
flow_dict = {n: {} for edge in cutset for n in edge} | |
for u, v in cutset: | |
flow_dict[u][v] = 1 | |
# Rebuild the edge disjoint paths from the flow dictionary. | |
paths_found = 0 | |
for v in list(flow_dict[s]): | |
if paths_found >= cutoff: | |
# preflow_push does not support cutoff: we have to | |
# keep track of the paths founds and stop at cutoff. | |
break | |
path = [s] | |
if v == t: | |
path.append(v) | |
yield path | |
continue | |
u = v | |
while u != t: | |
path.append(u) | |
try: | |
u, _ = flow_dict[u].popitem() | |
except KeyError: | |
break | |
else: | |
path.append(t) | |
yield path | |
paths_found += 1 | |
def node_disjoint_paths( | |
G, s, t, flow_func=None, cutoff=None, auxiliary=None, residual=None | |
): | |
r"""Computes node disjoint paths between source and target. | |
Node disjoint paths are paths that only share their first and last | |
nodes. The number of node independent paths between two nodes is | |
equal to their local node connectivity. | |
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. | |
cutoff : integer or None (default: None) | |
Maximum number of paths to yield. If specified, the maximum flow | |
algorithm will terminate when the flow value reaches or exceeds the | |
cutoff. This only works for flows that support the cutoff parameter | |
(most do) and is ignored otherwise. | |
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 | |
------- | |
paths : generator | |
Generator of node disjoint paths. | |
Raises | |
------ | |
NetworkXNoPath | |
If there is no path between source and target. | |
NetworkXError | |
If source or target are not in the graph G. | |
Examples | |
-------- | |
We use in this example the platonic icosahedral graph, which has node | |
connectivity 5, thus there are 5 node disjoint paths between any pair | |
of non neighbor nodes. | |
>>> G = nx.icosahedral_graph() | |
>>> len(list(nx.node_disjoint_paths(G, 0, 6))) | |
5 | |
If you need to compute node disjoint paths 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 node disjoint paths 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 arguments | |
>>> len(list(nx.node_disjoint_paths(G, 0, 6, auxiliary=H, residual=R))) | |
5 | |
You can also use alternative flow algorithms for computing node disjoint | |
paths. 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(list(nx.node_disjoint_paths(G, 0, 6, flow_func=shortest_augmenting_path))) | |
5 | |
Notes | |
----- | |
This is a flow based implementation of node disjoint paths. We compute | |
the maximum flow between source and target on an auxiliary directed | |
network. The saturated edges in the residual network after running the | |
maximum flow algorithm correspond to node disjoint paths between source | |
and target in the original network. This function handles both directed | |
and undirected graphs, and can use all flow algorithms from NetworkX flow | |
package. | |
See also | |
-------- | |
:meth:`edge_disjoint_paths` | |
:meth:`node_connectivity` | |
:meth:`maximum_flow` | |
:meth:`edmonds_karp` | |
:meth:`preflow_push` | |
:meth:`shortest_augmenting_path` | |
""" | |
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") | |
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.") | |
# Maximum possible edge disjoint paths | |
possible = min(H.out_degree(f"{mapping[s]}B"), H.in_degree(f"{mapping[t]}A")) | |
if not possible: | |
raise NetworkXNoPath | |
if cutoff is None: | |
cutoff = possible | |
else: | |
cutoff = min(cutoff, possible) | |
kwargs = { | |
"flow_func": flow_func, | |
"residual": residual, | |
"auxiliary": H, | |
"cutoff": cutoff, | |
} | |
# The edge disjoint paths in the auxiliary digraph correspond to the node | |
# disjoint paths in the original graph. | |
paths_edges = edge_disjoint_paths(H, f"{mapping[s]}B", f"{mapping[t]}A", **kwargs) | |
for path in paths_edges: | |
# Each node in the original graph maps to two nodes in auxiliary graph | |
yield list(_unique_everseen(H.nodes[node]["id"] for node in path)) | |
def _unique_everseen(iterable): | |
# Adapted from https://docs.python.org/3/library/itertools.html examples | |
"List unique elements, preserving order. Remember all elements ever seen." | |
# unique_everseen('AAAABBBCCDAABBB') --> A B C D | |
seen = set() | |
seen_add = seen.add | |
for element in _filterfalse(seen.__contains__, iterable): | |
seen_add(element) | |
yield element | |