123#ifndef OR_TOOLS_GRAPH_GENERIC_MAX_FLOW_H_
124#define OR_TOOLS_GRAPH_GENERIC_MAX_FLOW_H_
134#include "absl/strings/string_view.h"
137#include "ortools/graph/flow_problem.pb.h"
157template <
typename Element,
typename IntegerPriority>
179 void Push(Element element, IntegerPriority priority);
187 Element PopBack(std::vector<std::pair<Element, IntegerPriority> >* queue);
192 std::vector<std::pair<Element, IntegerPriority> > even_queue_;
193 std::vector<std::pair<Element, IntegerPriority> > odd_queue_;
248template <
typename Graph,
typename ArcFlowT = int64_t,
249 typename FlowSumT = int64_t>
257 static_assert(std::is_signed_v<FlowSumType>);
362 DCHECK_EQ(tail,
Tail(arc));
364 potentials[tail] == potentials[
Head(arc)] + 1;
461 template <
bool reverse>
466 std::numeric_limits<FlowSumType>::max();
540template <
typename Element,
typename IntegerPriority>
543 return even_queue_.empty() && odd_queue_.empty();
546template <
typename Element,
typename IntegerPriority>
552template <
typename Element,
typename IntegerPriority>
554 Element element, IntegerPriority priority) {
556 DCHECK(even_queue_.empty() || priority >= even_queue_.back().second - 1);
557 DCHECK(odd_queue_.empty() || priority >= odd_queue_.back().second - 1);
563 DCHECK(odd_queue_.empty() || priority >= odd_queue_.back().second);
564 odd_queue_.push_back(std::make_pair(element, priority));
566 DCHECK(even_queue_.empty() || priority >= even_queue_.back().second);
567 even_queue_.push_back(std::make_pair(element, priority));
571template <
typename Element,
typename IntegerPriority>
574 if (even_queue_.empty())
return PopBack(&odd_queue_);
575 if (odd_queue_.empty())
return PopBack(&even_queue_);
576 if (odd_queue_.back().second > even_queue_.back().second) {
577 return PopBack(&odd_queue_);
579 return PopBack(&even_queue_);
583template <
typename Element,
typename IntegerPriority>
584Element PriorityQueueWithRestrictedPush<Element, IntegerPriority>::PopBack(
585 std::vector<std::pair<Element, IntegerPriority> >* queue) {
586 DCHECK(!queue->empty());
587 Element element = queue->back().first;
592template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
602 DCHECK(
graph->IsNodeValid(source));
603 DCHECK(
graph->IsNodeValid(sink));
605 if (max_num_nodes > 0) {
610 node_excess_ = std::make_unique<FlowSumType[]>(max_num_nodes);
616 if (max_num_arcs > 0) {
628template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
632 DCHECK_LE(0, new_capacity);
653template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
655 std::vector<NodeIndex>* result) {
659template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
661 std::vector<NodeIndex>* result) {
665template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
676 LOG(DFATAL) <<
"node_excess_[" << node <<
"] = " <<
node_excess_[node]
686 if (direct_capacity < 0) {
687 LOG(DFATAL) <<
"residual_arc_capacity_[" << arc
688 <<
"] = " << direct_capacity <<
" < 0";
691 if (opposite_capacity < 0) {
692 LOG(DFATAL) <<
"residual_arc_capacity_[" << opposite
693 <<
"] = " << opposite_capacity <<
" < 0";
697 if (direct_capacity + opposite_capacity < 0) {
698 LOG(DFATAL) <<
"initial capacity [" << arc
699 <<
"] = " << direct_capacity + opposite_capacity <<
" < 0";
705 LOG(DFATAL) <<
"The algorithm terminated, but the flow is not maximal!";
712template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
718 std::vector<bool> is_reached(num_nodes,
false);
719 std::vector<NodeIndex> to_process;
723 while (!to_process.empty()) {
724 const NodeIndex node = to_process.back();
725 to_process.pop_back();
726 for (
const ArcIndex arc :
graph_->OutgoingOrOppositeIncomingArcs(node)) {
729 if (!is_reached[head]) {
730 is_reached[head] =
true;
731 to_process.push_back(head);
736 return is_reached[
sink_];
739template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
743 for (
const ArcIndex arc :
graph_->OutgoingOrOppositeIncomingArcs(node)) {
750template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
752 absl::string_view context,
ArcIndex arc)
const {
755 return absl::StrFormat(
756 "%s Arc %d, from %d to %d, "
757 "Residual capacity = %d, "
758 "Residual capacity for reverse arc = %d, "
759 "Height(tail) = %d, Height(head) = %d, "
760 "Excess(tail) = %d, Excess(head) = %d",
766template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
795template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
814 for (
ArcIndex arc = 0; arc < num_arcs; ++arc) {
820 for (
ArcIndex arc = 0; arc < num_arcs; ++arc) {
831 for (
NodeIndex node = 0; node < num_nodes; ++node) {
833 for (
const ArcIndex arc :
graph_->OutgoingOrOppositeIncomingArcs(node)) {
844template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
856 std::vector<bool> stored(num_nodes,
false);
857 stored[
sink_] =
true;
861 std::vector<bool> visited(num_nodes,
false);
862 visited[
sink_] =
true;
866 std::vector<ArcIndex> arc_stack;
870 std::vector<int> index_branch;
873 std::vector<NodeIndex> reverse_topological_order;
880 arc_stack.push_back(arc);
886 while (!arc_stack.empty()) {
887 DCHECK_GT(
Flow(arc_stack.back()), 0)
888 << arc_stack.size() - 1 <<
" arc " << arc_stack.back() <<
" "
889 <<
Tail(arc_stack.back()) <<
"->" <<
Head(arc_stack.back());
898 reverse_topological_order.push_back(node);
899 DCHECK(!index_branch.empty());
900 index_branch.pop_back();
902 arc_stack.pop_back();
908 DCHECK(!stored[node]);
909 DCHECK(index_branch.empty() ||
910 (arc_stack.size() - 1 > index_branch.back()));
911 visited[node] =
true;
912 index_branch.push_back(arc_stack.size() - 1);
916 if (flow > 0 && !stored[head]) {
917 if (!visited[head]) {
918 arc_stack.push_back(arc);
924 int cycle_begin = index_branch.size();
925 while (cycle_begin > 0 &&
926 Head(arc_stack[index_branch[cycle_begin - 1]]) != head) {
933 int first_saturated_index = index_branch.size();
934 for (
int i = index_branch.size() - 1; i >= cycle_begin; --i) {
935 const ArcIndex arc_on_cycle = arc_stack[index_branch[i]];
937 arc_flow <= flow_on_cycle) {
938 flow_on_cycle = arc_flow;
939 first_saturated_index = i;
948 PushFlow(-flow_on_cycle, node, arc);
949 for (
int i = index_branch.size() - 1; i >= cycle_begin; --i) {
950 const ArcIndex arc_on_cycle = arc_stack[index_branch[i]];
951 PushFlow(-flow_on_cycle,
Tail(arc_on_cycle), arc_on_cycle);
952 if (i >= first_saturated_index) {
953 DCHECK(visited[
Head(arc_on_cycle)]);
954 visited[
Head(arc_on_cycle)] =
false;
956 DCHECK_GT(
Flow(arc_on_cycle), 0);
965 if (first_saturated_index < index_branch.size()) {
966 arc_stack.resize(index_branch[first_saturated_index]);
967 index_branch.resize(first_saturated_index);
977 DCHECK(arc_stack.empty());
978 DCHECK(index_branch.empty());
982 for (
int i = 0; i < reverse_topological_order.size(); i++) {
983 const NodeIndex node = reverse_topological_order[i];
985 for (
const ArcIndex arc :
graph_->OutgoingOrOppositeIncomingArcs(node)) {
1000template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1004 int queue_index = 0;
1024 for (
const ArcIndex arc :
graph_->OutgoingOrOppositeIncomingArcs(node)) {
1052 if (node_excess[head] > 0) {
1080 for (
NodeIndex node = 0; node < num_nodes; ++node) {
1089 for (
int i = 1; i <
bfs_queue_.size(); ++i) {
1098template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1109 bool flow_pushed =
false;
1119 DCHECK_GE(flow, 0) << flow;
1120 DCHECK_GE(current_flow_out_of_source, 0) << current_flow_out_of_source;
1122 if (capped_flow <
static_cast<FlowSumType>(flow)) {
1129 if (capped_flow == 0)
return true;
1140template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1164template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1173 node_excess[tail]));
1181 node_excess[tail] -=
static_cast<FlowSumType>(flow);
1185template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1191 for (
NodeIndex node = 0; node < num_nodes; ++node) {
1202template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1207 std::vector<int> skip_active_node;
1226 skip_active_node.assign(num_nodes, 0);
1227 skip_active_node[
sink_] = 2;
1228 skip_active_node[
source_] = 2;
1232 if (skip_active_node[node] > 1) {
1256 ++skip_active_node[node];
1259 }
while (num_skipped > 0);
1270template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1284 graph_->OutgoingOrOppositeIncomingArcsStartingFrom(
1289 if (node_excess[head] == 0) {
1295 if (node_excess[node] == 0) {
1296 first_admissible_arc[node] = arc;
1305 if (node_potentials[node] >= num_nodes)
break;
1309template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1315 NodeHeight min_height = std::numeric_limits<NodeHeight>::max();
1317 for (
const ArcIndex arc :
graph_->OutgoingOrOppositeIncomingArcs(node)) {
1321 if (head_height < min_height) {
1322 min_height = head_height;
1323 first_admissible_arc = arc;
1340template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1343 return graph_->OppositeArc(arc);
1346template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1352template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1354 return graph_->IsArcValid(arc);
1357template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1358template <
bool reverse>
1360 NodeIndex start, std::vector<NodeIndex>* result) {
1365 if (start >= num_nodes) {
1367 result->push_back(start);
1373 int queue_index = 0;
1379 for (
const ArcIndex arc :
graph_->OutgoingOrOppositeIncomingArcs(node)) {
1390template <
typename Graph,
typename ArcFlowT,
typename FlowSumT>
1392 FlowModelProto model;
1393 model.set_problem_type(FlowModelProto::MAX_FLOW);
1394 for (
int n = 0; n <
graph_->num_nodes(); ++n) {
1395 FlowNodeProto* node = model.add_nodes();
1397 if (n ==
source_) node->set_supply(1);
1398 if (n ==
sink_) node->set_supply(-1);
1400 for (
int a = 0; a <
graph_->num_arcs(); ++a) {
1401 FlowArcProto* arc = model.add_arcs();
1402 arc->set_tail(
graph_->Tail(a));
1403 arc->set_head(
graph_->Head(a));
void InitializePreflow()
Initializes the preflow to a state that enables to run Refine.
const Graph * graph_
A pointer to the graph passed as argument.
std::unique_ptr< NodeHeight[]> node_potential_
void Discharge(NodeIndex node)
void RefineWithGlobalUpdate()
PriorityQueueWithRestrictedPush< NodeIndex, NodeHeight > active_node_by_height_
bool IsAdmissible(NodeIndex tail, ArcIndex arc, const NodeHeight *potentials) const
Returns true if arc is admissible.
std::unique_ptr< ArcIndex[]> first_admissible_arc_
An array representing the first admissible arc for each node in graph_.
void GetSourceSideMinCut(std::vector< NodeIndex > *result)
void PushFlow(ArcFlowType flow, NodeIndex tail, ArcIndex arc)
NodeIndex GetSourceNodeIndex() const
Returns the index of the node corresponding to the source of the network.
ArcIndex Opposite(ArcIndex arc) const
bool CheckRelabelPrecondition(NodeIndex node) const
StatsGroup stats_
Statistics about this class.
NodeIndex sink_
The index of the sink node in graph_.
void InitializeActiveNodeContainer()
Initializes the container active_nodes_.
std::unique_ptr< ArcFlowType[]> initial_capacity_
GenericMaxFlow & operator=(const GenericMaxFlow &)=delete
bool SaturateOutgoingArcsFromSource()
bool IsArcValid(ArcIndex arc) const
const util::ReverseArcStaticGraph< NodeIndex, ArcIndex > * graph() const
FlowSumType Flow(ArcIndex arc) const
bool AugmentingPathExists() const
NodeIndex Tail(ArcIndex arc) const
std::vector< NodeIndex > bfs_queue_
FlowModelProto CreateFlowModel()
Returns the protocol buffer representation of the current problem.
std::unique_ptr< FlowSumType[]> node_excess_
bool IsArcDirect(ArcIndex arc) const
void PushActiveNode(const NodeIndex &node)
Push element to the active node container.
void PushFlowExcessBackToSource()
bool Solve()
Returns true if a maximum flow was solved.
void Relabel(NodeIndex node)
Graph::NodeIndex NodeIndex
void GetSinkSideMinCut(std::vector< NodeIndex > *result)
NodeIndex source_
The index of the source node in graph_.
void ComputeReachableNodes(NodeIndex start, std::vector< NodeIndex > *result)
void PushAsMuchFlowAsPossible(NodeIndex tail, ArcIndex arc, FlowSumType *node_excess)
NodeIndex Head(ArcIndex arc) const
Handy member functions to make the code more compact.
ArcFlowType Capacity(ArcIndex arc) const
Returns the initial capacity of an arc.
GenericMaxFlow(const Graph *graph, NodeIndex source, NodeIndex sink)
void Refine()
Performs optimization step.
bool IsActive(NodeIndex node) const
static constexpr FlowSumType kMaxFlowSum
Maximum manageable flow.
std::string DebugString(absl::string_view context, ArcIndex arc) const
NodeIndex GetSinkNodeIndex() const
Returns the index of the node corresponding to the sink of the network.
ZVector< ArcFlowType > residual_arc_capacity_
NodeIndex GetAndRemoveFirstActiveNode()
Get the first element from the active node container.
std::vector< bool > node_in_bfs_queue_
FlowSumType GetOptimalFlow() const
Returns the total flow found by the algorithm.
bool IsEmptyActiveNodeContainer()
Check the emptiness of the container.
Status status_
The status of the problem.
void SetArcCapacity(ArcIndex arc, ArcFlowType new_capacity)
GenericMaxFlow(const GenericMaxFlow &)=delete
This type is neither copyable nor movable.
bool IsEmpty() const
Is the queue empty?
void Push(Element element, IntegerPriority priority)
PriorityQueueWithRestrictedPush & operator=(const PriorityQueueWithRestrictedPush &)=delete
PriorityQueueWithRestrictedPush(const PriorityQueueWithRestrictedPush &)=delete
This type is neither copyable nor movable.
PriorityQueueWithRestrictedPush()
void Clear()
Clears the queue.
Base class to print a nice summary of a group of statistics.
static constexpr bool kHasNegativeReverseArcs
static const int32_t kNilArc
In SWIG mode, we don't want anything besides these top-level includes.
util::ReverseArcStaticGraph Graph
Type of graph to use.
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)