123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- """
- Maximum flow (and minimum cut) algorithms on capacitated graphs.
- """
- import networkx as nx
- from .boykovkolmogorov import boykov_kolmogorov
- from .dinitz_alg import dinitz
- from .edmondskarp import edmonds_karp
- from .preflowpush import preflow_push
- from .shortestaugmentingpath import shortest_augmenting_path
- from .utils import build_flow_dict
- # Define the default flow function for computing maximum flow.
- default_flow_func = preflow_push
- __all__ = ["maximum_flow", "maximum_flow_value", "minimum_cut", "minimum_cut_value"]
- def maximum_flow(flowG, _s, _t, capacity="capacity", flow_func=None, **kwargs):
- """Find a maximum single-commodity flow.
- Parameters
- ----------
- flowG : NetworkX graph
- Edges of the graph are expected to have an attribute called
- 'capacity'. If this attribute is not present, the edge is
- considered to have infinite capacity.
- _s : node
- Source node for the flow.
- _t : node
- Sink node for the flow.
- capacity : string
- Edges of the graph G are expected to have an attribute capacity
- that indicates how much flow the edge can support. If this
- attribute is not present, the edge is considered to have
- infinite capacity. Default value: 'capacity'.
- flow_func : function
- A function for computing the maximum flow among a pair of nodes
- in a capacitated graph. The function has to accept at least three
- parameters: a Graph or Digraph, a source node, and a target node.
- And return a residual network that follows NetworkX conventions
- (see Notes). If flow_func is None, the default maximum
- flow function (:meth:`preflow_push`) is used. See below for
- alternative algorithms. The choice of the default function may change
- from version to version and should not be relied on. Default value:
- None.
- kwargs : Any other keyword parameter is passed to the function that
- computes the maximum flow.
- Returns
- -------
- flow_value : integer, float
- Value of the maximum flow, i.e., net outflow from the source.
- flow_dict : dict
- A dictionary containing the value of the flow that went through
- each edge.
- Raises
- ------
- NetworkXError
- The algorithm does not support MultiGraph and MultiDiGraph. If
- the input graph is an instance of one of these two classes, a
- NetworkXError is raised.
- NetworkXUnbounded
- If the graph has a path of infinite capacity, the value of a
- feasible flow on the graph is unbounded above and the function
- raises a NetworkXUnbounded.
- See also
- --------
- :meth:`maximum_flow_value`
- :meth:`minimum_cut`
- :meth:`minimum_cut_value`
- :meth:`edmonds_karp`
- :meth:`preflow_push`
- :meth:`shortest_augmenting_path`
- Notes
- -----
- The function used in the flow_func parameter has to return a residual
- network that follows NetworkX conventions:
- The residual network :samp:`R` from an input graph :samp:`G` has the
- same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
- of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
- self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
- in :samp:`G`.
- For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
- is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
- in :samp:`G` or zero otherwise. If the capacity is infinite,
- :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
- that does not affect the solution of the problem. This value is stored in
- :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
- :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
- satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
- The flow value, defined as the total flow into :samp:`t`, the sink, is
- stored in :samp:`R.graph['flow_value']`. Reachability to :samp:`t` using
- only edges :samp:`(u, v)` such that
- :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
- :samp:`s`-:samp:`t` cut.
- Specific algorithms may store extra data in :samp:`R`.
- The function should supports an optional boolean parameter value_only. When
- True, it can optionally terminate the algorithm as soon as the maximum flow
- value and the minimum cut can be determined.
- Examples
- --------
- >>> G = nx.DiGraph()
- >>> G.add_edge("x", "a", capacity=3.0)
- >>> G.add_edge("x", "b", capacity=1.0)
- >>> G.add_edge("a", "c", capacity=3.0)
- >>> G.add_edge("b", "c", capacity=5.0)
- >>> G.add_edge("b", "d", capacity=4.0)
- >>> G.add_edge("d", "e", capacity=2.0)
- >>> G.add_edge("c", "y", capacity=2.0)
- >>> G.add_edge("e", "y", capacity=3.0)
- maximum_flow returns both the value of the maximum flow and a
- dictionary with all flows.
- >>> flow_value, flow_dict = nx.maximum_flow(G, "x", "y")
- >>> flow_value
- 3.0
- >>> print(flow_dict["x"]["b"])
- 1.0
- You can also use alternative algorithms for computing the
- maximum flow by using the flow_func parameter.
- >>> from networkx.algorithms.flow import shortest_augmenting_path
- >>> flow_value == nx.maximum_flow(G, "x", "y", flow_func=shortest_augmenting_path)[
- ... 0
- ... ]
- True
- """
- if flow_func is None:
- if kwargs:
- raise nx.NetworkXError(
- "You have to explicitly set a flow_func if"
- " you need to pass parameters via kwargs."
- )
- flow_func = default_flow_func
- if not callable(flow_func):
- raise nx.NetworkXError("flow_func has to be callable.")
- R = flow_func(flowG, _s, _t, capacity=capacity, value_only=False, **kwargs)
- flow_dict = build_flow_dict(flowG, R)
- return (R.graph["flow_value"], flow_dict)
- def maximum_flow_value(flowG, _s, _t, capacity="capacity", flow_func=None, **kwargs):
- """Find the value of maximum single-commodity flow.
- Parameters
- ----------
- flowG : NetworkX graph
- Edges of the graph are expected to have an attribute called
- 'capacity'. If this attribute is not present, the edge is
- considered to have infinite capacity.
- _s : node
- Source node for the flow.
- _t : node
- Sink node for the flow.
- capacity : string
- Edges of the graph G are expected to have an attribute capacity
- that indicates how much flow the edge can support. If this
- attribute is not present, the edge is considered to have
- infinite capacity. Default value: 'capacity'.
- flow_func : function
- A function for computing the maximum flow among a pair of nodes
- in a capacitated graph. The function has to accept at least three
- parameters: a Graph or Digraph, a source node, and a target node.
- And return a residual network that follows NetworkX conventions
- (see Notes). If flow_func is None, the default maximum
- flow function (:meth:`preflow_push`) is used. See below for
- alternative algorithms. The choice of the default function may change
- from version to version and should not be relied on. Default value:
- None.
- kwargs : Any other keyword parameter is passed to the function that
- computes the maximum flow.
- Returns
- -------
- flow_value : integer, float
- Value of the maximum flow, i.e., net outflow from the source.
- Raises
- ------
- NetworkXError
- The algorithm does not support MultiGraph and MultiDiGraph. If
- the input graph is an instance of one of these two classes, a
- NetworkXError is raised.
- NetworkXUnbounded
- If the graph has a path of infinite capacity, the value of a
- feasible flow on the graph is unbounded above and the function
- raises a NetworkXUnbounded.
- See also
- --------
- :meth:`maximum_flow`
- :meth:`minimum_cut`
- :meth:`minimum_cut_value`
- :meth:`edmonds_karp`
- :meth:`preflow_push`
- :meth:`shortest_augmenting_path`
- Notes
- -----
- The function used in the flow_func parameter has to return a residual
- network that follows NetworkX conventions:
- The residual network :samp:`R` from an input graph :samp:`G` has the
- same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
- of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
- self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
- in :samp:`G`.
- For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
- is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
- in :samp:`G` or zero otherwise. If the capacity is infinite,
- :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
- that does not affect the solution of the problem. This value is stored in
- :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
- :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
- satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
- The flow value, defined as the total flow into :samp:`t`, the sink, is
- stored in :samp:`R.graph['flow_value']`. Reachability to :samp:`t` using
- only edges :samp:`(u, v)` such that
- :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
- :samp:`s`-:samp:`t` cut.
- Specific algorithms may store extra data in :samp:`R`.
- The function should supports an optional boolean parameter value_only. When
- True, it can optionally terminate the algorithm as soon as the maximum flow
- value and the minimum cut can be determined.
- Examples
- --------
- >>> G = nx.DiGraph()
- >>> G.add_edge("x", "a", capacity=3.0)
- >>> G.add_edge("x", "b", capacity=1.0)
- >>> G.add_edge("a", "c", capacity=3.0)
- >>> G.add_edge("b", "c", capacity=5.0)
- >>> G.add_edge("b", "d", capacity=4.0)
- >>> G.add_edge("d", "e", capacity=2.0)
- >>> G.add_edge("c", "y", capacity=2.0)
- >>> G.add_edge("e", "y", capacity=3.0)
- maximum_flow_value computes only the value of the
- maximum flow:
- >>> flow_value = nx.maximum_flow_value(G, "x", "y")
- >>> flow_value
- 3.0
- You can also use alternative algorithms for computing the
- maximum flow by using the flow_func parameter.
- >>> from networkx.algorithms.flow import shortest_augmenting_path
- >>> flow_value == nx.maximum_flow_value(
- ... G, "x", "y", flow_func=shortest_augmenting_path
- ... )
- True
- """
- if flow_func is None:
- if kwargs:
- raise nx.NetworkXError(
- "You have to explicitly set a flow_func if"
- " you need to pass parameters via kwargs."
- )
- flow_func = default_flow_func
- if not callable(flow_func):
- raise nx.NetworkXError("flow_func has to be callable.")
- R = flow_func(flowG, _s, _t, capacity=capacity, value_only=True, **kwargs)
- return R.graph["flow_value"]
- def minimum_cut(flowG, _s, _t, capacity="capacity", flow_func=None, **kwargs):
- """Compute the value and the node partition of a minimum (s, t)-cut.
- Use the max-flow min-cut theorem, i.e., the capacity of a minimum
- capacity cut is equal to the flow value of a maximum flow.
- Parameters
- ----------
- flowG : NetworkX graph
- Edges of the graph are expected to have an attribute called
- 'capacity'. If this attribute is not present, the edge is
- considered to have infinite capacity.
- _s : node
- Source node for the flow.
- _t : node
- Sink node for the flow.
- capacity : string
- Edges of the graph G are expected to have an attribute capacity
- that indicates how much flow the edge can support. If this
- attribute is not present, the edge is considered to have
- infinite capacity. Default value: 'capacity'.
- flow_func : function
- A function for computing the maximum flow among a pair of nodes
- in a capacitated graph. The function has to accept at least three
- parameters: a Graph or Digraph, a source node, and a target node.
- And return a residual network that follows NetworkX conventions
- (see Notes). If flow_func is None, the default maximum
- flow function (:meth:`preflow_push`) is used. See below for
- alternative algorithms. The choice of the default function may change
- from version to version and should not be relied on. Default value:
- None.
- kwargs : Any other keyword parameter is passed to the function that
- computes the maximum flow.
- Returns
- -------
- cut_value : integer, float
- Value of the minimum cut.
- partition : pair of node sets
- A partitioning of the nodes that defines a minimum cut.
- Raises
- ------
- NetworkXUnbounded
- If the graph has a path of infinite capacity, all cuts have
- infinite capacity and the function raises a NetworkXError.
- See also
- --------
- :meth:`maximum_flow`
- :meth:`maximum_flow_value`
- :meth:`minimum_cut_value`
- :meth:`edmonds_karp`
- :meth:`preflow_push`
- :meth:`shortest_augmenting_path`
- Notes
- -----
- The function used in the flow_func parameter has to return a residual
- network that follows NetworkX conventions:
- The residual network :samp:`R` from an input graph :samp:`G` has the
- same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
- of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
- self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
- in :samp:`G`.
- For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
- is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
- in :samp:`G` or zero otherwise. If the capacity is infinite,
- :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
- that does not affect the solution of the problem. This value is stored in
- :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
- :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
- satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
- The flow value, defined as the total flow into :samp:`t`, the sink, is
- stored in :samp:`R.graph['flow_value']`. Reachability to :samp:`t` using
- only edges :samp:`(u, v)` such that
- :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
- :samp:`s`-:samp:`t` cut.
- Specific algorithms may store extra data in :samp:`R`.
- The function should supports an optional boolean parameter value_only. When
- True, it can optionally terminate the algorithm as soon as the maximum flow
- value and the minimum cut can be determined.
- Examples
- --------
- >>> G = nx.DiGraph()
- >>> G.add_edge("x", "a", capacity=3.0)
- >>> G.add_edge("x", "b", capacity=1.0)
- >>> G.add_edge("a", "c", capacity=3.0)
- >>> G.add_edge("b", "c", capacity=5.0)
- >>> G.add_edge("b", "d", capacity=4.0)
- >>> G.add_edge("d", "e", capacity=2.0)
- >>> G.add_edge("c", "y", capacity=2.0)
- >>> G.add_edge("e", "y", capacity=3.0)
- minimum_cut computes both the value of the
- minimum cut and the node partition:
- >>> cut_value, partition = nx.minimum_cut(G, "x", "y")
- >>> reachable, non_reachable = partition
- 'partition' here is a tuple with the two sets of nodes that define
- the minimum cut. You can compute the cut set of edges that induce
- the minimum cut as follows:
- >>> 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)
- >>> print(sorted(cutset))
- [('c', 'y'), ('x', 'b')]
- >>> cut_value == sum(G.edges[u, v]["capacity"] for (u, v) in cutset)
- True
- You can also use alternative algorithms for computing the
- minimum cut by using the flow_func parameter.
- >>> from networkx.algorithms.flow import shortest_augmenting_path
- >>> cut_value == nx.minimum_cut(G, "x", "y", flow_func=shortest_augmenting_path)[0]
- True
- """
- if flow_func is None:
- if kwargs:
- raise nx.NetworkXError(
- "You have to explicitly set a flow_func if"
- " you need to pass parameters via kwargs."
- )
- flow_func = default_flow_func
- if not callable(flow_func):
- raise nx.NetworkXError("flow_func has to be callable.")
- if kwargs.get("cutoff") is not None and flow_func is preflow_push:
- raise nx.NetworkXError("cutoff should not be specified.")
- R = flow_func(flowG, _s, _t, capacity=capacity, value_only=True, **kwargs)
- # Remove saturated edges from the residual network
- cutset = [(u, v, d) for u, v, d in R.edges(data=True) if d["flow"] == d["capacity"]]
- R.remove_edges_from(cutset)
- # Then, reachable and non reachable nodes from source in the
- # residual network form the node partition that defines
- # the minimum cut.
- non_reachable = set(dict(nx.shortest_path_length(R, target=_t)))
- partition = (set(flowG) - non_reachable, non_reachable)
- # Finally add again cutset edges to the residual network to make
- # sure that it is reusable.
- if cutset is not None:
- R.add_edges_from(cutset)
- return (R.graph["flow_value"], partition)
- def minimum_cut_value(flowG, _s, _t, capacity="capacity", flow_func=None, **kwargs):
- """Compute the value of a minimum (s, t)-cut.
- Use the max-flow min-cut theorem, i.e., the capacity of a minimum
- capacity cut is equal to the flow value of a maximum flow.
- Parameters
- ----------
- flowG : NetworkX graph
- Edges of the graph are expected to have an attribute called
- 'capacity'. If this attribute is not present, the edge is
- considered to have infinite capacity.
- _s : node
- Source node for the flow.
- _t : node
- Sink node for the flow.
- capacity : string
- Edges of the graph G are expected to have an attribute capacity
- that indicates how much flow the edge can support. If this
- attribute is not present, the edge is considered to have
- infinite capacity. Default value: 'capacity'.
- flow_func : function
- A function for computing the maximum flow among a pair of nodes
- in a capacitated graph. The function has to accept at least three
- parameters: a Graph or Digraph, a source node, and a target node.
- And return a residual network that follows NetworkX conventions
- (see Notes). If flow_func is None, the default maximum
- flow function (:meth:`preflow_push`) is used. See below for
- alternative algorithms. The choice of the default function may change
- from version to version and should not be relied on. Default value:
- None.
- kwargs : Any other keyword parameter is passed to the function that
- computes the maximum flow.
- Returns
- -------
- cut_value : integer, float
- Value of the minimum cut.
- Raises
- ------
- NetworkXUnbounded
- If the graph has a path of infinite capacity, all cuts have
- infinite capacity and the function raises a NetworkXError.
- See also
- --------
- :meth:`maximum_flow`
- :meth:`maximum_flow_value`
- :meth:`minimum_cut`
- :meth:`edmonds_karp`
- :meth:`preflow_push`
- :meth:`shortest_augmenting_path`
- Notes
- -----
- The function used in the flow_func parameter has to return a residual
- network that follows NetworkX conventions:
- The residual network :samp:`R` from an input graph :samp:`G` has the
- same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
- of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
- self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
- in :samp:`G`.
- For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
- is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
- in :samp:`G` or zero otherwise. If the capacity is infinite,
- :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
- that does not affect the solution of the problem. This value is stored in
- :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
- :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
- satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
- The flow value, defined as the total flow into :samp:`t`, the sink, is
- stored in :samp:`R.graph['flow_value']`. Reachability to :samp:`t` using
- only edges :samp:`(u, v)` such that
- :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
- :samp:`s`-:samp:`t` cut.
- Specific algorithms may store extra data in :samp:`R`.
- The function should supports an optional boolean parameter value_only. When
- True, it can optionally terminate the algorithm as soon as the maximum flow
- value and the minimum cut can be determined.
- Examples
- --------
- >>> G = nx.DiGraph()
- >>> G.add_edge("x", "a", capacity=3.0)
- >>> G.add_edge("x", "b", capacity=1.0)
- >>> G.add_edge("a", "c", capacity=3.0)
- >>> G.add_edge("b", "c", capacity=5.0)
- >>> G.add_edge("b", "d", capacity=4.0)
- >>> G.add_edge("d", "e", capacity=2.0)
- >>> G.add_edge("c", "y", capacity=2.0)
- >>> G.add_edge("e", "y", capacity=3.0)
- minimum_cut_value computes only the value of the
- minimum cut:
- >>> cut_value = nx.minimum_cut_value(G, "x", "y")
- >>> cut_value
- 3.0
- You can also use alternative algorithms for computing the
- minimum cut by using the flow_func parameter.
- >>> from networkx.algorithms.flow import shortest_augmenting_path
- >>> cut_value == nx.minimum_cut_value(
- ... G, "x", "y", flow_func=shortest_augmenting_path
- ... )
- True
- """
- if flow_func is None:
- if kwargs:
- raise nx.NetworkXError(
- "You have to explicitly set a flow_func if"
- " you need to pass parameters via kwargs."
- )
- flow_func = default_flow_func
- if not callable(flow_func):
- raise nx.NetworkXError("flow_func has to be callable.")
- if kwargs.get("cutoff") is not None and flow_func is preflow_push:
- raise nx.NetworkXError("cutoff should not be specified.")
- R = flow_func(flowG, _s, _t, capacity=capacity, value_only=True, **kwargs)
- return R.graph["flow_value"]
|