Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
|
Forward declaration. More...
#include <max_flow.h>
Public Types | |
typedef Graph::NodeIndex | NodeIndex |
typedef Graph::ArcIndex | ArcIndex |
typedef Graph::OutgoingArcIterator | OutgoingArcIterator |
typedef Graph::OutgoingOrOppositeIncomingArcIterator | OutgoingOrOppositeIncomingArcIterator |
typedef Graph::IncomingArcIterator | IncomingArcIterator |
typedef ZVector< ArcIndex > | ArcIndexArray |
typedef NodeIndex | NodeHeight |
typedef ZVector< NodeHeight > | NodeHeightArray |
Public Types inherited from operations_research::MaxFlowStatusClass | |
enum | Status { NOT_SOLVED , OPTIMAL , INT_OVERFLOW , BAD_INPUT , BAD_RESULT } |
Public Member Functions | |
GenericMaxFlow (const Graph *graph, NodeIndex source, NodeIndex sink) | |
GenericMaxFlow (const GenericMaxFlow &)=delete | |
This type is neither copyable nor movable. | |
GenericMaxFlow & | operator= (const GenericMaxFlow &)=delete |
virtual | ~GenericMaxFlow () |
const Graph * | graph () const |
Returns the graph associated to the current object. | |
Status | status () const |
NodeIndex | GetSourceNodeIndex () const |
Returns the index of the node corresponding to the source of the network. | |
NodeIndex | GetSinkNodeIndex () const |
Returns the index of the node corresponding to the sink of the network. | |
void | SetArcCapacity (ArcIndex arc, FlowQuantity new_capacity) |
Sets the capacity for arc to new_capacity. | |
void | SetArcFlow (ArcIndex arc, FlowQuantity new_flow) |
Sets the flow for arc. | |
bool | Solve () |
Returns true if a maximum flow was solved. | |
FlowQuantity | GetOptimalFlow () const |
Returns the total flow found by the algorithm. | |
FlowQuantity | Flow (ArcIndex arc) const |
FlowQuantity | Capacity (ArcIndex arc) const |
void | GetSourceSideMinCut (std::vector< NodeIndex > *result) |
void | GetSinkSideMinCut (std::vector< NodeIndex > *result) |
bool | CheckInputConsistency () const |
bool | CheckResult () const |
bool | AugmentingPathExists () const |
void | SetUseGlobalUpdate (bool value) |
void | SetUseTwoPhaseAlgorithm (bool value) |
void | SetCheckInput (bool value) |
void | SetCheckResult (bool value) |
void | ProcessNodeByHeight (bool value) |
FlowModelProto | CreateFlowModel () |
Returns the protocol buffer representation of the current problem. | |
Protected Member Functions | |
bool | IsAdmissible (ArcIndex arc) const |
Returns true if arc is admissible. | |
bool | IsActive (NodeIndex node) const |
void | SetCapacityAndClearFlow (ArcIndex arc, FlowQuantity capacity) |
Sets the capacity of arc to 'capacity' and clears the flow on arc. | |
bool | CheckRelabelPrecondition (NodeIndex node) const |
std::string | DebugString (absl::string_view context, ArcIndex arc) const |
void | InitializeActiveNodeContainer () |
Initializes the container active_nodes_. | |
NodeIndex | GetAndRemoveFirstActiveNode () |
Get the first element from the active node container. | |
void | PushActiveNode (const NodeIndex &node) |
Push element to the active node container. | |
bool | IsEmptyActiveNodeContainer () |
Check the emptiness of the container. | |
void | Refine () |
Performs optimization step. | |
void | RefineWithGlobalUpdate () |
void | Discharge (NodeIndex node) |
void | InitializePreflow () |
Initializes the preflow to a state that enables to run Refine. | |
void | PushFlowExcessBackToSource () |
void | GlobalUpdate () |
bool | SaturateOutgoingArcsFromSource () |
void | PushFlow (FlowQuantity flow, ArcIndex arc) |
void | Relabel (NodeIndex node) |
NodeIndex | Head (ArcIndex arc) const |
Handy member functions to make the code more compact. | |
NodeIndex | Tail (ArcIndex arc) const |
ArcIndex | Opposite (ArcIndex arc) const |
bool | IsArcDirect (ArcIndex arc) const |
bool | IsArcValid (ArcIndex arc) const |
template<bool reverse> | |
void | ComputeReachableNodes (NodeIndex start, std::vector< NodeIndex > *result) |
const FlowQuantity | kMaxFlowQuantity |
const FlowQuantity | kMaxFlowQuantity |
Protected Attributes | |
const Graph * | graph_ |
A pointer to the graph passed as argument. | |
QuantityArray | node_excess_ |
An array representing the excess for each node in graph_. | |
NodeHeightArray | node_potential_ |
QuantityArray | residual_arc_capacity_ |
ArcIndexArray | first_admissible_arc_ |
An array representing the first admissible arc for each node in graph_. | |
std::vector< NodeIndex > | active_nodes_ |
PriorityQueueWithRestrictedPush< NodeIndex, NodeHeight > | active_node_by_height_ |
NodeIndex | source_ |
The index of the source node in graph_. | |
NodeIndex | sink_ |
The index of the sink node in graph_. | |
Status | status_ |
The status of the problem. | |
std::vector< bool > | node_in_bfs_queue_ |
std::vector< NodeIndex > | bfs_queue_ |
bool | use_global_update_ |
Whether or not to use GlobalUpdate(). | |
bool | use_two_phase_algorithm_ |
bool | process_node_by_height_ |
bool | check_input_ |
bool | check_result_ |
StatsGroup | stats_ |
Statistics about this class. | |
Static Protected Attributes | |
static const FlowQuantity | kMaxFlowQuantity |
Maximum manageable flow. | |
Forward declaration.
Generic MaxFlow (there is a default MaxFlow specialization defined below) that works with StarGraph and all the reverse arc graphs from graph.h, see the end of max_flow.cc for the exact types this class is compiled for.
Definition at line 324 of file max_flow.h.
Graph::ArcIndex operations_research::GenericMaxFlow< Graph >::ArcIndex |
Definition at line 327 of file max_flow.h.
ZVector<ArcIndex> operations_research::GenericMaxFlow< Graph >::ArcIndexArray |
Definition at line 332 of file max_flow.h.
Graph::IncomingArcIterator operations_research::GenericMaxFlow< Graph >::IncomingArcIterator |
Definition at line 331 of file max_flow.h.
NodeIndex operations_research::GenericMaxFlow< Graph >::NodeHeight |
The height of a node never excess 2 times the number of node, so we use the same type as a Node index.
Definition at line 336 of file max_flow.h.
ZVector<NodeHeight> operations_research::GenericMaxFlow< Graph >::NodeHeightArray |
Definition at line 337 of file max_flow.h.
Graph::NodeIndex operations_research::GenericMaxFlow< Graph >::NodeIndex |
Definition at line 326 of file max_flow.h.
Graph::OutgoingArcIterator operations_research::GenericMaxFlow< Graph >::OutgoingArcIterator |
Definition at line 328 of file max_flow.h.
Graph::OutgoingOrOppositeIncomingArcIterator operations_research::GenericMaxFlow< Graph >::OutgoingOrOppositeIncomingArcIterator |
Definition at line 330 of file max_flow.h.
operations_research::GenericMaxFlow< Graph >::GenericMaxFlow | ( | const Graph * | graph, |
NodeIndex | source, | ||
NodeIndex | sink ) |
Initialize a MaxFlow instance on the given graph. The graph does not need to be fully built yet, but its capacity reservation are used to initialize the memory of this class. source and sink must also be valid node of graph.
Definition at line 144 of file max_flow.cc.
|
delete |
This type is neither copyable nor movable.
|
inlinevirtual |
Definition at line 351 of file max_flow.h.
bool operations_research::GenericMaxFlow< Graph >::AugmentingPathExists | ( | ) | const |
Returns true if there exists a path from the source to the sink with remaining capacity. This allows us to easily check at the end that the flow we computed is indeed optimal (provided that all the conditions tested by CheckResult() also hold).
We simply compute the reachability from the source in the residual graph.
Definition at line 297 of file max_flow.cc.
|
inline |
Returns the capacity of arc using the equations given in the comment on residual_arc_capacity_.
Definition at line 391 of file max_flow.h.
bool operations_research::GenericMaxFlow< Graph >::CheckInputConsistency | ( | ) | const |
Checks the consistency of the input, i.e. that capacities on the arcs are non-negative or null.
Definition at line 182 of file max_flow.cc.
|
protected |
Returns true if a precondition for Relabel is met, i.e. the outgoing arcs of node are all either saturated or the heights of their heads are greater or equal to the height of node.
Definition at line 326 of file max_flow.cc.
bool operations_research::GenericMaxFlow< Graph >::CheckResult | ( | ) | const |
Checks whether the result is valid, i.e. that node excesses are all equal to zero (we have a flow) and that residual capacities are all non-negative or zero.
The initial capacity of the direct arcs is non-negative.
Definition at line 255 of file max_flow.cc.
|
protected |
Returns the set of nodes reachable from start in the residual graph or in the reverse residual graph (if reverse is true).
If start is not a valid node index, it can reach only itself. Note(user): This is needed because source and sink are given independently of the graph and sometimes before it is even constructed.
Definition at line 951 of file max_flow.cc.
FlowModelProto operations_research::GenericMaxFlow< Graph >::CreateFlowModel | ( | ) |
Returns the protocol buffer representation of the current problem.
Definition at line 985 of file max_flow.cc.
|
protected |
Returns context concatenated with information about arc in a human-friendly way.
Definition at line 337 of file max_flow.cc.
|
protected |
Discharges an active node node by saturating its admissible adjacent arcs, if any, and by relabelling it when it becomes inactive.
The push below will make the node active for sure. Note that we may push the sink_, but that is handled properly in Refine().
Definition at line 866 of file max_flow.cc.
|
inline |
Returns the flow on arc using the equations given in the comment on residual_arc_capacity_.
Definition at line 381 of file max_flow.h.
|
inlineprotected |
Get the first element from the active node container.
Definition at line 476 of file max_flow.h.
|
inline |
Returns the total flow found by the algorithm.
Definition at line 377 of file max_flow.h.
|
inline |
Returns the index of the node corresponding to the sink of the network.
Definition at line 365 of file max_flow.h.
void operations_research::GenericMaxFlow< Graph >::GetSinkSideMinCut | ( | std::vector< NodeIndex > * | result | ) |
Returns the nodes that can reach the sink in the residual graph, the outgoing arcs of this set form a minimum cut. Note that if this is the complement of GetNodeReachableFromSource(), then the min-cut is unique.
Definition at line 250 of file max_flow.cc.
|
inline |
Returns the index of the node corresponding to the source of the network.
Definition at line 362 of file max_flow.h.
void operations_research::GenericMaxFlow< Graph >::GetSourceSideMinCut | ( | std::vector< NodeIndex > * | result | ) |
Returns the nodes reachable from the source in the residual graph, the outgoing arcs of this set form a minimum cut.
Definition at line 244 of file max_flow.cc.
|
protected |
Computes the best possible node potential given the current flow using a reverse breadth-first search from the sink in the reverse residual graph. This is an implementation of the global update heuristic mentioned in many max-flow papers. See for instance: B.V. Cherkassky, A.V. Goldberg, "On implementing push-relabel methods for the maximum flow problem", Algorithmica, 19:390-410, 1997. ftp://reports.stanford.edu/pub/cstr/reports/cs/tr/94/1523/CS-TR-94-1523.pdf
We do two BFS in the reverse residual graph, one from the sink and one from the source. Because all the arcs from the source are saturated (except in presence of integer overflow), the source cannot reach the sink in the residual graph. However, we still want to relabel all the nodes that cannot reach the sink but can reach the source (because if they have excess, we need to push it back to the source).
Skip the arc if the height of head was already set to the correct value (Remember we are doing reverse BFS).
Note(user): We used to have a DCHECK_GE(candidate_distance, node_potential_[head]); which is always true except in the case where we can push more than kMaxFlowQuantity out of the source. The problem comes from the fact that in this case, we call PushFlowExcessBackToSource() in the middle of the algorithm. The later call will break the properties of the node potential. Note however, that this function will recompute a good node potential for all the nodes and thus fix the issue.
If head is active, we can steal some or all of its excess. This brings a huge gain on some problems. Note(user): I haven't seen this anywhere in the literature.
If the arc became saturated, it is no longer in the residual graph, so we do not need to consider head at this time.
At the end of the search, some nodes may not be in the bfs_queue_. Such nodes cannot reach the sink_ or source_ in the residual graph, so there is no point trying to push flow toward them. We obtain this effect by setting their height to something unreachable.
Reset the active nodes. Doing it like this pushes the nodes in increasing order of height. Note that bfs_queue_[0] is the sink_ so we skip it.
Definition at line 584 of file max_flow.cc.
|
inline |
Returns the graph associated to the current object.
Definition at line 354 of file max_flow.h.
|
inlineprotected |
Handy member functions to make the code more compact.
Definition at line 548 of file max_flow.h.
|
protected |
Initializes the container active_nodes_.
Definition at line 759 of file max_flow.cc.
|
protected |
Initializes the preflow to a state that enables to run Refine.
InitializePreflow() clears the whole flow that could have been computed by a previous Solve(). This is not optimal in terms of complexity.
All the initial heights are zero except for the source whose height is equal to the number of nodes and will never change during the algorithm.
Initially no arcs are admissible except maybe the one leaving the source, but we treat the source in a special way, see SaturateOutgoingArcsFromSource().
Definition at line 398 of file max_flow.cc.
|
inlineprotected |
Returns true if node is active, i.e. if its excess is positive and it is neither the source or the sink of the graph.
Definition at line 453 of file max_flow.h.
|
inlineprotected |
Returns true if arc is admissible.
Definition at line 446 of file max_flow.h.
|
protected |
Definition at line 936 of file max_flow.cc.
|
protected |
Definition at line 941 of file max_flow.cc.
|
inlineprotected |
Check the emptiness of the container.
Definition at line 493 of file max_flow.h.
|
protected |
Explicit instantiations that can be used by a client.
Definition at line 1008 of file max_flow.cc.
|
protected |
Definition at line 666 of file max_flow.h.
|
delete |
|
protected |
Definition at line 931 of file max_flow.cc.
|
inline |
Definition at line 437 of file max_flow.h.
|
inlineprotected |
Push element to the active node container.
Definition at line 484 of file max_flow.h.
|
protected |
Pushes flow on arc, i.e. consumes flow on residual_arc_capacity_[arc], and consumes -flow on residual_arc_capacity_[Opposite(arc)]. Updates node_excess_ at the tail and head of arc accordingly.
node_excess_ should be always greater than or equal to 0 except for the source where it should always be smaller than or equal to 0. Note however that we cannot check this because when we cancel the flow on a cycle in PushFlowExcessBackToSource(), we may break this invariant during the operation even if it is still valid at the end.
Update the residual capacity of the arc and its opposite arc.
Update the excesses at the tail and head of the arc.
Definition at line 737 of file max_flow.cc.
|
protected |
Clears the flow excess at each node by pushing the flow back to the source:
Note(user): Calling this function will break the property on the node potentials because of the way we cancel flow on cycle. However, we only call that at the end of the algorithm, or just before a GlobalUpdate() that will restore the precondition on the node potentials.
We implement a variation of Tarjan's strongly connected component algorithm to detect cycles published in: Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing. A description can also be found in wikipedia.
Stored nodes are settled nodes already stored in the reverse_topological_order (except the sink_ that we do not actually store).
The visited nodes that are not yet stored are all the nodes from the source_ to the current node in the current dfs branch.
Stack of arcs to explore in the dfs search. The current node is Head(arc_stack.back()).
Increasing list of indices into the arc_stack that correspond to the list of arcs in the current dfs branch from the source_ to the current node.
Node in reverse_topological_order in the final dfs tree.
We start by pushing all the outgoing arcs from the source on the stack to avoid special conditions in the code. As a result, source_ will not be stored in reverse_topological_order, and this is what we want.
Start the dfs on the subgraph formed by the direct arcs with positive flow.
If the node is visited, it means we have explored all its arcs and we have just backtracked in the dfs. Store it if it is not already stored and process the next arc on the stack.
The node is a new unexplored node, add all its outgoing arcs with positive flow to the stack and go deeper in the dfs.
There is a cycle. Find the first index to consider, arc_stack[index_branch[cycle_begin]] will be the first arc on the cycle.
Compute the maximum flow that can be canceled on the cycle and the min index such that arc_stack[index_branch[i]] will be saturated.
This is just here for a DCHECK() below.
Cancel the flow on the cycle, and set visited[node] = false for the node that will be backtracked over.
This is a simple check that the flow was pushed properly.
Backtrack the dfs just before index_branch[first_saturated_index]. If the current node is still active, there is nothing to do.
We backtracked over the current node, so there is no need to continue looping over its arcs.
Return the flow to the sink. Note that the sink_ and the source_ are not stored in reverse_topological_order.
Definition at line 429 of file max_flow.cc.
|
protected |
Performs optimization step.
Usually SaturateOutgoingArcsFromSource() will saturate all the arcs from the source in one go, and we will loop just once. But in case we can push more than kMaxFlowQuantity out of the source the loop is as follow:
Compute the current max-flow. This will push some flow back to the source and render more outgoing arcs from the source not admissible.
Definition at line 774 of file max_flow.cc.
|
protected |
The idea behind this is that if a node height augments by more than one, then it is likely to push flow back the way it came. This can lead to very costly loops. A bad case is: source -> n1 -> n2 and n2 just recently isolated from the sink. Then n2 will push flow back to n1, and n1 to n2 and so on. The height of each node will increase by steps of two until the height of the source is reached, which can take a long time. If the chain is longer, the situation is even worse. The behavior of this heuristic is related to the Gap heuristic.
We skip a node when this condition was true 2 times to avoid doing a global update too frequently.
Definition at line 813 of file max_flow.cc.
|
protected |
Relabels a node, i.e. increases its height by the minimum necessary amount. This version of Relabel is relaxed in a way such that if an admissible arc exists at the current node height, then the node is not relabeled. This enables us to deal with wrong values of first_admissible_arc_[node] when updating it is too costly.
Because we use a relaxed version, this is no longer true if the first_admissible_arc_[node] was not actually the first arc! DCHECK(CheckRelabelPrecondition(node));
Update min_height only for arcs with available capacity.
We found an admissible arc at the current height, just stop there. This is the true first_admissible_arc_[node].
Definition at line 898 of file max_flow.cc.
|
protected |
Tries to saturate all the outgoing arcs from the source that can reach the sink. Most of the time, we can do that in one go, except when more flow than kMaxFlowQuantity can be pushed out of the source in which case we have to be careful. Returns true if some flow was pushed.
If sink_ or source_ already have kMaxFlowQuantity, then there is no point pushing more flow since it will cause an integer overflow.
This is a special IsAdmissible() condition for the source.
We are careful in case the sum of the flow out of the source is greater than kMaxFlowQuantity to avoid overflow.
We push as much flow as we can so the current flow on the network will be kMaxFlowQuantity.
Since at the beginning of the function, current_flow_out_of_source was different from kMaxFlowQuantity, we are sure to have pushed some flow before if capped_flow is 0.
Definition at line 694 of file max_flow.cc.
void operations_research::GenericMaxFlow< Graph >::SetArcCapacity | ( | ArcIndex | arc, |
FlowQuantity | new_capacity ) |
Sets the capacity for arc to new_capacity.
The above condition is true if one of the two conditions is true: 1/ (capacity_delta > 0), meaning we are increasing the capacity 2/ (capacity_delta < 0 && free_capacity + capacity_delta >= 0) meaning we are reducing the capacity, but that the capacity reduction is not larger than the free capacity.
Definition at line 194 of file max_flow.cc.
void operations_research::GenericMaxFlow< Graph >::SetArcFlow | ( | ArcIndex | arc, |
FlowQuantity | new_flow ) |
Sets the flow for arc.
Definition at line 228 of file max_flow.cc.
|
inlineprotected |
Sets the capacity of arc to 'capacity' and clears the flow on arc.
Definition at line 458 of file max_flow.h.
|
inline |
Definition at line 435 of file max_flow.h.
|
inline |
Definition at line 436 of file max_flow.h.
|
inline |
Sets the different algorithm options. All default to true. See the corresponding variable declaration below for more details.
Definition at line 430 of file max_flow.h.
|
inline |
Definition at line 434 of file max_flow.h.
bool operations_research::GenericMaxFlow< Graph >::Solve | ( | ) |
Returns true if a maximum flow was solved.
Deal with the case when source_ or sink_ is not inside graph_. Since they are both specified independently of the graph, we do need to take care of this corner case.
Behave like a normal graph where source_ and sink_ are disconnected.
In this case, we are sure that the flow is > kMaxFlowQuantity.
Definition at line 353 of file max_flow.cc.
|
inline |
Returns the status of last call to Solve(). NOT_SOLVED is returned if Solve() has never been called or if the problem has been modified in such a way that the previous solution becomes invalid.
Definition at line 359 of file max_flow.h.
|
inlineprotected |
Definition at line 549 of file max_flow.h.
|
protected |
A priority queue used for managing active nodes in the algorithm. It allows to select the active node with highest height before each Discharge(). Moreover, since all pushes from this node will be to nodes with height greater or equal to the initial discharged node height minus one, the PriorityQueueWithRestrictedPush is a perfect fit.
Definition at line 613 of file max_flow.h.
|
protected |
A stack used for managing active nodes in the algorithm.
Definition at line 606 of file max_flow.h.
|
protected |
Definition at line 627 of file max_flow.h.
|
protected |
Whether or not we check the input, this is a small price to pay for robustness. Disable only if you know the input is valid because an invalid input can cause the algorithm to run into an infinite loop!
Definition at line 650 of file max_flow.h.
|
protected |
Whether or not we check the result.
Definition at line 654 of file max_flow.h.
|
protected |
An array representing the first admissible arc for each node in graph_.
Definition at line 600 of file max_flow.h.
|
protected |
A pointer to the graph passed as argument.
Definition at line 563 of file max_flow.h.
|
staticprotected |
Maximum manageable flow.
Definition at line 560 of file max_flow.h.
|
protected |
An array representing the excess for each node in graph_.
Definition at line 566 of file max_flow.h.
|
protected |
BFS queue used by the GlobalUpdate() function. We do not use a C++ queue because we need access to the vector for different optimizations.
Definition at line 626 of file max_flow.h.
|
protected |
An array representing the height function for each node in graph_. For a given node, this is a lower bound on the shortest path length from this node to the sink in the residual network. The height of a node always goes up during the course of a Solve().
Since initially we saturate all the outgoing arcs of the source, we can never reach the sink from the source in the residual graph. Initially we set the height of the source to n (the number of node of the graph) and it never changes. If a node as an height >= n, then this node can't reach the sink and its height minus n is a lower bound on the shortest path length from this node to the source in the residual graph.
Definition at line 579 of file max_flow.h.
|
protected |
Whether or not we use the PriorityQueueWithRestrictedPush to process the active nodes rather than a simple queue. This can only be true if use_global_update_ is true.
Note(user): using a template will be slightly faster, but since we test this in a non-critical path, this only has a minor impact.
Definition at line 645 of file max_flow.h.
|
protected |
An array representing the residual_capacity for each arc in graph_. Residual capacities enable one to represent the capacity and flow for all arcs in the graph in the following manner. For all arc, residual_arc_capacity_[arc] = capacity[arc] - flow[arc] Moreover, for reverse arcs, capacity[arc] = 0 by definition, Also flow[Opposite(arc)] = -flow[arc] by definition. Therefore:
Definition at line 597 of file max_flow.h.
|
protected |
The index of the sink node in graph_.
Definition at line 619 of file max_flow.h.
|
protected |
The index of the source node in graph_.
Definition at line 616 of file max_flow.h.
|
mutableprotected |
Statistics about this class.
Definition at line 657 of file max_flow.h.
|
protected |
The status of the problem.
Definition at line 622 of file max_flow.h.
|
protected |
Whether or not to use GlobalUpdate().
Definition at line 630 of file max_flow.h.
|
protected |
Whether or not we use a two-phase algorithm: 1/ Only deal with nodes that can reach the sink. At the end we know the value of the maximum flow and we have a min-cut. 2/ Call PushFlowExcessBackToSource() to obtain a max-flow. This is usually a lot faster than the first phase.
Definition at line 637 of file max_flow.h.