Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
max_flow.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <limits>
18#include <memory>
19#include <string>
20#include <vector>
21
22#include "absl/memory/memory.h"
23#include "absl/strings/str_format.h"
24#include "absl/strings/string_view.h"
25#include "ortools/graph/graph.h"
27
28namespace operations_research {
29
30SimpleMaxFlow::SimpleMaxFlow() : num_nodes_(0) {}
31
33 FlowQuantity capacity) {
34 const ArcIndex num_arcs = arc_tail_.size();
35 num_nodes_ = std::max(num_nodes_, tail + 1);
36 num_nodes_ = std::max(num_nodes_, head + 1);
37 arc_tail_.push_back(tail);
38 arc_head_.push_back(head);
39 arc_capacity_.push_back(capacity);
40 return num_arcs;
41}
42
43NodeIndex SimpleMaxFlow::NumNodes() const { return num_nodes_; }
44
45ArcIndex SimpleMaxFlow::NumArcs() const { return arc_tail_.size(); }
46
47NodeIndex SimpleMaxFlow::Tail(ArcIndex arc) const { return arc_tail_[arc]; }
48
49NodeIndex SimpleMaxFlow::Head(ArcIndex arc) const { return arc_head_[arc]; }
50
52 return arc_capacity_[arc];
53}
54
56 arc_capacity_[arc] = capacity;
57}
58
60 const ArcIndex num_arcs = arc_capacity_.size();
61 arc_flow_.assign(num_arcs, 0);
62 underlying_max_flow_.reset();
63 underlying_graph_.reset();
64 optimal_flow_ = 0;
65 if (source == sink || source < 0 || sink < 0) {
66 return BAD_INPUT;
67 }
68 if (source >= num_nodes_ || sink >= num_nodes_) {
69 return OPTIMAL;
70 }
71 underlying_graph_ = std::make_unique<Graph>(num_nodes_, num_arcs);
72 underlying_graph_->AddNode(source);
73 underlying_graph_->AddNode(sink);
74 for (int arc = 0; arc < num_arcs; ++arc) {
75 underlying_graph_->AddArc(arc_tail_[arc], arc_head_[arc]);
76 }
77 underlying_graph_->Build(&arc_permutation_);
78 underlying_max_flow_ = std::make_unique<GenericMaxFlow<Graph>>(
79 underlying_graph_.get(), source, sink);
80 for (ArcIndex arc = 0; arc < num_arcs; ++arc) {
81 ArcIndex permuted_arc =
82 arc < arc_permutation_.size() ? arc_permutation_[arc] : arc;
83 underlying_max_flow_->SetArcCapacity(permuted_arc, arc_capacity_[arc]);
84 }
85 if (underlying_max_flow_->Solve()) {
86 optimal_flow_ = underlying_max_flow_->GetOptimalFlow();
87 for (ArcIndex arc = 0; arc < num_arcs; ++arc) {
88 ArcIndex permuted_arc =
89 arc < arc_permutation_.size() ? arc_permutation_[arc] : arc;
90 arc_flow_[arc] = underlying_max_flow_->Flow(permuted_arc);
91 }
92 }
93 // Translate the GenericMaxFlow::Status. It is different because NOT_SOLVED
94 // does not make sense in the simple api.
95 switch (underlying_max_flow_->status()) {
97 return BAD_RESULT;
99 return OPTIMAL;
101 return POSSIBLE_OVERFLOW;
103 return BAD_INPUT;
105 return BAD_RESULT;
106 }
107 return BAD_RESULT;
108}
109
110FlowQuantity SimpleMaxFlow::OptimalFlow() const { return optimal_flow_; }
111
112FlowQuantity SimpleMaxFlow::Flow(ArcIndex arc) const { return arc_flow_[arc]; }
113
114void SimpleMaxFlow::GetSourceSideMinCut(std::vector<NodeIndex>* result) {
115 if (underlying_max_flow_ == nullptr) return;
116 underlying_max_flow_->GetSourceSideMinCut(result);
117}
118
119void SimpleMaxFlow::GetSinkSideMinCut(std::vector<NodeIndex>* result) {
120 if (underlying_max_flow_ == nullptr) return;
121 underlying_max_flow_->GetSinkSideMinCut(result);
122}
123
125 NodeIndex sink) const {
126 FlowModelProto model;
127 model.set_problem_type(FlowModelProto::MAX_FLOW);
128 for (int n = 0; n < num_nodes_; ++n) {
129 FlowNodeProto* node = model.add_nodes();
130 node->set_id(n);
131 if (n == source) node->set_supply(1);
132 if (n == sink) node->set_supply(-1);
133 }
134 for (int a = 0; a < arc_tail_.size(); ++a) {
135 FlowArcProto* arc = model.add_arcs();
136 arc->set_tail(Tail(a));
137 arc->set_head(Head(a));
138 arc->set_capacity(Capacity(a));
139 }
140 return model;
141}
142
143template <typename Graph>
145 NodeIndex sink)
146 : graph_(graph),
147 node_excess_(),
148 node_potential_(),
149 residual_arc_capacity_(),
150 first_admissible_arc_(),
151 active_nodes_(),
152 source_(source),
153 sink_(sink),
154 use_global_update_(true),
155 use_two_phase_algorithm_(true),
156 process_node_by_height_(true),
157 check_input_(true),
158 check_result_(true),
159 stats_("MaxFlow") {
161 DCHECK(graph->IsNodeValid(source));
162 DCHECK(graph->IsNodeValid(sink));
163 const NodeIndex max_num_nodes = Graphs<Graph>::NodeReservation(*graph_);
164 if (max_num_nodes > 0) {
165 node_excess_.Reserve(0, max_num_nodes - 1);
167 node_potential_.Reserve(0, max_num_nodes - 1);
169 first_admissible_arc_.Reserve(0, max_num_nodes - 1);
171 bfs_queue_.reserve(max_num_nodes);
172 active_nodes_.reserve(max_num_nodes);
173 }
174 const ArcIndex max_num_arcs = Graphs<Graph>::ArcReservation(*graph_);
175 if (max_num_arcs > 0) {
176 residual_arc_capacity_.Reserve(-max_num_arcs, max_num_arcs - 1);
178 }
179}
180
181template <typename Graph>
183 SCOPED_TIME_STAT(&stats_);
184 bool ok = true;
185 for (ArcIndex arc = 0; arc < graph_->num_arcs(); ++arc) {
186 if (residual_arc_capacity_[arc] < 0) {
187 ok = false;
188 }
189 }
190 return ok;
191}
192
193template <typename Graph>
195 FlowQuantity new_capacity) {
196 SCOPED_TIME_STAT(&stats_);
197 DCHECK_LE(0, new_capacity);
198 DCHECK(IsArcDirect(arc));
199 const FlowQuantity free_capacity = residual_arc_capacity_[arc];
200 const FlowQuantity capacity_delta = new_capacity - Capacity(arc);
201 if (capacity_delta == 0) {
202 return; // Nothing to do.
203 }
204 status_ = NOT_SOLVED;
205 if (free_capacity + capacity_delta >= 0) {
206 // The above condition is true if one of the two conditions is true:
207 // 1/ (capacity_delta > 0), meaning we are increasing the capacity
208 // 2/ (capacity_delta < 0 && free_capacity + capacity_delta >= 0)
209 // meaning we are reducing the capacity, but that the capacity
210 // reduction is not larger than the free capacity.
211 DCHECK((capacity_delta > 0) ||
212 (capacity_delta < 0 && free_capacity + capacity_delta >= 0));
213 residual_arc_capacity_.Set(arc, free_capacity + capacity_delta);
214 DCHECK_LE(0, residual_arc_capacity_[arc]);
215 } else {
216 // Note that this breaks the preflow invariants but it is currently not an
217 // issue since we restart from scratch on each Solve() and we set the status
218 // to NOT_SOLVED.
219 //
220 // TODO(user): The easiest is probably to allow negative node excess in
221 // other places than the source, but the current implementation does not
222 // deal with this.
223 SetCapacityAndClearFlow(arc, new_capacity);
224 }
225}
226
227template <typename Graph>
229 SCOPED_TIME_STAT(&stats_);
230 DCHECK(IsArcValid(arc));
231 DCHECK_GE(new_flow, 0);
232 const FlowQuantity capacity = Capacity(arc);
233 DCHECK_GE(capacity, new_flow);
234
235 // Note that this breaks the preflow invariants but it is currently not an
236 // issue since we restart from scratch on each Solve() and we set the status
237 // to NOT_SOLVED.
238 residual_arc_capacity_.Set(Opposite(arc), -new_flow);
239 residual_arc_capacity_.Set(arc, capacity - new_flow);
240 status_ = NOT_SOLVED;
241}
242
243template <typename Graph>
245 std::vector<NodeIndex>* result) {
246 ComputeReachableNodes<false>(source_, result);
247}
248
249template <typename Graph>
250void GenericMaxFlow<Graph>::GetSinkSideMinCut(std::vector<NodeIndex>* result) {
251 ComputeReachableNodes<true>(sink_, result);
252}
253
254template <typename Graph>
256 SCOPED_TIME_STAT(&stats_);
257 bool ok = true;
258 if (node_excess_[source_] != -node_excess_[sink_]) {
259 LOG(DFATAL) << "-node_excess_[source_] = " << -node_excess_[source_]
260 << " != node_excess_[sink_] = " << node_excess_[sink_];
261 ok = false;
262 }
263 for (NodeIndex node = 0; node < graph_->num_nodes(); ++node) {
264 if (node != source_ && node != sink_) {
265 if (node_excess_[node] != 0) {
266 LOG(DFATAL) << "node_excess_[" << node << "] = " << node_excess_[node]
267 << " != 0";
268 ok = false;
269 }
270 }
271 }
272 for (ArcIndex arc = 0; arc < graph_->num_arcs(); ++arc) {
273 const ArcIndex opposite = Opposite(arc);
274 const FlowQuantity direct_capacity = residual_arc_capacity_[arc];
275 const FlowQuantity opposite_capacity = residual_arc_capacity_[opposite];
276 if (direct_capacity < 0) {
277 LOG(DFATAL) << "residual_arc_capacity_[" << arc
278 << "] = " << direct_capacity << " < 0";
279 ok = false;
280 }
281 if (opposite_capacity < 0) {
282 LOG(DFATAL) << "residual_arc_capacity_[" << opposite
283 << "] = " << opposite_capacity << " < 0";
284 ok = false;
285 }
286 // The initial capacity of the direct arcs is non-negative.
287 if (direct_capacity + opposite_capacity < 0) {
288 LOG(DFATAL) << "initial capacity [" << arc
289 << "] = " << direct_capacity + opposite_capacity << " < 0";
290 ok = false;
291 }
292 }
293 return ok;
294}
295
296template <typename Graph>
298 SCOPED_TIME_STAT(&stats_);
299
300 // We simply compute the reachability from the source in the residual graph.
301 const NodeIndex num_nodes = graph_->num_nodes();
302 std::vector<bool> is_reached(num_nodes, false);
303 std::vector<NodeIndex> to_process;
304
305 to_process.push_back(source_);
306 is_reached[source_] = true;
307 while (!to_process.empty()) {
308 const NodeIndex node = to_process.back();
309 to_process.pop_back();
310 for (OutgoingOrOppositeIncomingArcIterator it(*graph_, node); it.Ok();
311 it.Next()) {
312 const ArcIndex arc = it.Index();
313 if (residual_arc_capacity_[arc] > 0) {
314 const NodeIndex head = graph_->Head(arc);
315 if (!is_reached[head]) {
316 is_reached[head] = true;
317 to_process.push_back(head);
318 }
319 }
320 }
321 }
322 return is_reached[sink_];
323}
324
325template <typename Graph>
327 DCHECK(IsActive(node));
328 for (OutgoingOrOppositeIncomingArcIterator it(*graph_, node); it.Ok();
329 it.Next()) {
330 const ArcIndex arc = it.Index();
331 DCHECK(!IsAdmissible(arc)) << DebugString("CheckRelabelPrecondition:", arc);
332 }
333 return true;
334}
335
336template <typename Graph>
337std::string GenericMaxFlow<Graph>::DebugString(absl::string_view context,
338 ArcIndex arc) const {
339 const NodeIndex tail = Tail(arc);
340 const NodeIndex head = Head(arc);
341 return absl::StrFormat(
342 "%s Arc %d, from %d to %d, "
343 "Capacity = %d, Residual capacity = %d, "
344 "Flow = residual capacity for reverse arc = %d, "
345 "Height(tail) = %d, Height(head) = %d, "
346 "Excess(tail) = %d, Excess(head) = %d",
347 context, arc, tail, head, Capacity(arc), residual_arc_capacity_[arc],
348 Flow(arc), node_potential_[tail], node_potential_[head],
349 node_excess_[tail], node_excess_[head]);
350}
351
352template <typename Graph>
354 status_ = NOT_SOLVED;
355 if (check_input_ && !CheckInputConsistency()) {
356 status_ = BAD_INPUT;
357 return false;
358 }
359 InitializePreflow();
360
361 // Deal with the case when source_ or sink_ is not inside graph_.
362 // Since they are both specified independently of the graph, we do need to
363 // take care of this corner case.
364 const NodeIndex num_nodes = graph_->num_nodes();
365 if (sink_ >= num_nodes || source_ >= num_nodes) {
366 // Behave like a normal graph where source_ and sink_ are disconnected.
367 // Note that the arc flow is set to 0 by InitializePreflow().
368 status_ = OPTIMAL;
369 return true;
370 }
371 if (use_global_update_) {
372 RefineWithGlobalUpdate();
373 } else {
374 Refine();
375 }
376 if (check_result_) {
377 if (!CheckResult()) {
378 status_ = BAD_RESULT;
379 return false;
380 }
381 if (GetOptimalFlow() < kMaxFlowQuantity && AugmentingPathExists()) {
382 LOG(ERROR) << "The algorithm terminated, but the flow is not maximal!";
383 status_ = BAD_RESULT;
384 return false;
385 }
386 }
387 DCHECK_EQ(node_excess_[sink_], -node_excess_[source_]);
388 status_ = OPTIMAL;
389 if (GetOptimalFlow() == kMaxFlowQuantity && AugmentingPathExists()) {
390 // In this case, we are sure that the flow is > kMaxFlowQuantity.
391 status_ = INT_OVERFLOW;
392 }
393 IF_STATS_ENABLED(VLOG(1) << stats_.StatString());
394 return true;
395}
396
397template <typename Graph>
399 SCOPED_TIME_STAT(&stats_);
400 // InitializePreflow() clears the whole flow that could have been computed
401 // by a previous Solve(). This is not optimal in terms of complexity.
402 // TODO(user): find a way to make the re-solving incremental (not an obvious
403 // task, and there has not been a lot of literature on the subject.)
404 node_excess_.SetAll(0);
405 const ArcIndex num_arcs = graph_->num_arcs();
406 for (ArcIndex arc = 0; arc < num_arcs; ++arc) {
407 SetCapacityAndClearFlow(arc, Capacity(arc));
408 }
409
410 // All the initial heights are zero except for the source whose height is
411 // equal to the number of nodes and will never change during the algorithm.
412 node_potential_.SetAll(0);
413 node_potential_.Set(source_, graph_->num_nodes());
414
415 // Initially no arcs are admissible except maybe the one leaving the source,
416 // but we treat the source in a special way, see
417 // SaturateOutgoingArcsFromSource().
418 const NodeIndex num_nodes = graph_->num_nodes();
419 for (NodeIndex node = 0; node < num_nodes; ++node) {
420 first_admissible_arc_[node] = Graph::kNilArc;
421 }
422}
423
424// Note(user): Calling this function will break the property on the node
425// potentials because of the way we cancel flow on cycle. However, we only call
426// that at the end of the algorithm, or just before a GlobalUpdate() that will
427// restore the precondition on the node potentials.
428template <typename Graph>
430 SCOPED_TIME_STAT(&stats_);
431 const NodeIndex num_nodes = graph_->num_nodes();
432
433 // We implement a variation of Tarjan's strongly connected component algorithm
434 // to detect cycles published in: Tarjan, R. E. (1972), "Depth-first search
435 // and linear graph algorithms", SIAM Journal on Computing. A description can
436 // also be found in wikipedia.
437
438 // Stored nodes are settled nodes already stored in the
439 // reverse_topological_order (except the sink_ that we do not actually store).
440 std::vector<bool> stored(num_nodes, false);
441 stored[sink_] = true;
443 // The visited nodes that are not yet stored are all the nodes from the
444 // source_ to the current node in the current dfs branch.
445 std::vector<bool> visited(num_nodes, false);
446 visited[sink_] = true;
447
448 // Stack of arcs to explore in the dfs search.
449 // The current node is Head(arc_stack.back()).
450 std::vector<ArcIndex> arc_stack;
451
452 // Increasing list of indices into the arc_stack that correspond to the list
453 // of arcs in the current dfs branch from the source_ to the current node.
454 std::vector<int> index_branch;
455
456 // Node in reverse_topological_order in the final dfs tree.
457 std::vector<NodeIndex> reverse_topological_order;
458
459 // We start by pushing all the outgoing arcs from the source on the stack to
460 // avoid special conditions in the code. As a result, source_ will not be
461 // stored in reverse_topological_order, and this is what we want.
462 for (OutgoingArcIterator it(*graph_, source_); it.Ok(); it.Next()) {
463 const ArcIndex arc = it.Index();
464 const FlowQuantity flow = Flow(arc);
465 if (flow > 0) {
466 arc_stack.push_back(arc);
467 }
468 }
469 visited[source_] = true;
471 // Start the dfs on the subgraph formed by the direct arcs with positive flow.
472 while (!arc_stack.empty()) {
473 const NodeIndex node = Head(arc_stack.back());
474
475 // If the node is visited, it means we have explored all its arcs and we
476 // have just backtracked in the dfs. Store it if it is not already stored
477 // and process the next arc on the stack.
478 if (visited[node]) {
479 if (!stored[node]) {
480 stored[node] = true;
481 reverse_topological_order.push_back(node);
482 DCHECK(!index_branch.empty());
483 index_branch.pop_back();
484 }
485 arc_stack.pop_back();
486 continue;
487 }
488
489 // The node is a new unexplored node, add all its outgoing arcs with
490 // positive flow to the stack and go deeper in the dfs.
491 DCHECK(!stored[node]);
492 DCHECK(index_branch.empty() ||
493 (arc_stack.size() - 1 > index_branch.back()));
494 visited[node] = true;
495 index_branch.push_back(arc_stack.size() - 1);
496
497 for (OutgoingArcIterator it(*graph_, node); it.Ok(); it.Next()) {
498 const ArcIndex arc = it.Index();
499 const FlowQuantity flow = Flow(arc);
500 const NodeIndex head = Head(arc);
501 if (flow > 0 && !stored[head]) {
502 if (!visited[head]) {
503 arc_stack.push_back(arc);
504 } else {
505 // There is a cycle.
506 // Find the first index to consider,
507 // arc_stack[index_branch[cycle_begin]] will be the first arc on the
508 // cycle.
509 int cycle_begin = index_branch.size();
510 while (cycle_begin > 0 &&
511 Head(arc_stack[index_branch[cycle_begin - 1]]) != head) {
512 --cycle_begin;
513 }
514
515 // Compute the maximum flow that can be canceled on the cycle and the
516 // min index such that arc_stack[index_branch[i]] will be saturated.
517 FlowQuantity max_flow = flow;
518 int first_saturated_index = index_branch.size();
519 for (int i = index_branch.size() - 1; i >= cycle_begin; --i) {
520 const ArcIndex arc_on_cycle = arc_stack[index_branch[i]];
521 if (Flow(arc_on_cycle) <= max_flow) {
522 max_flow = Flow(arc_on_cycle);
523 first_saturated_index = i;
524 }
525 }
526
527 // This is just here for a DCHECK() below.
528 const FlowQuantity excess = node_excess_[head];
529
530 // Cancel the flow on the cycle, and set visited[node] = false for
531 // the node that will be backtracked over.
532 PushFlow(-max_flow, arc);
533 for (int i = index_branch.size() - 1; i >= cycle_begin; --i) {
534 const ArcIndex arc_on_cycle = arc_stack[index_branch[i]];
535 PushFlow(-max_flow, arc_on_cycle);
536 if (i >= first_saturated_index) {
537 DCHECK(visited[Head(arc_on_cycle)]);
538 visited[Head(arc_on_cycle)] = false;
539 } else {
540 DCHECK_GT(Flow(arc_on_cycle), 0);
541 }
542 }
543
544 // This is a simple check that the flow was pushed properly.
545 DCHECK_EQ(excess, node_excess_[head]);
546
547 // Backtrack the dfs just before index_branch[first_saturated_index].
548 // If the current node is still active, there is nothing to do.
549 if (first_saturated_index < index_branch.size()) {
550 arc_stack.resize(index_branch[first_saturated_index]);
551 index_branch.resize(first_saturated_index);
553 // We backtracked over the current node, so there is no need to
554 // continue looping over its arcs.
555 break;
556 }
558 }
559 }
560 }
561 DCHECK(arc_stack.empty());
562 DCHECK(index_branch.empty());
563
564 // Return the flow to the sink. Note that the sink_ and the source_ are not
565 // stored in reverse_topological_order.
566 for (int i = 0; i < reverse_topological_order.size(); i++) {
567 const NodeIndex node = reverse_topological_order[i];
568 if (node_excess_[node] == 0) continue;
569 for (IncomingArcIterator it(*graph_, node); it.Ok(); it.Next()) {
570 const ArcIndex opposite_arc = Opposite(it.Index());
571 if (residual_arc_capacity_[opposite_arc] > 0) {
572 const FlowQuantity flow =
573 std::min(node_excess_[node], residual_arc_capacity_[opposite_arc]);
574 PushFlow(flow, opposite_arc);
575 if (node_excess_[node] == 0) break;
576 }
577 }
578 DCHECK_EQ(0, node_excess_[node]);
579 }
580 DCHECK_EQ(-node_excess_[source_], node_excess_[sink_]);
581}
582
583template <typename Graph>
585 SCOPED_TIME_STAT(&stats_);
586 bfs_queue_.clear();
587 int queue_index = 0;
588 const NodeIndex num_nodes = graph_->num_nodes();
589 node_in_bfs_queue_.assign(num_nodes, false);
590 node_in_bfs_queue_[sink_] = true;
591 node_in_bfs_queue_[source_] = true;
592
593 // We do two BFS in the reverse residual graph, one from the sink and one from
594 // the source. Because all the arcs from the source are saturated (except in
595 // presence of integer overflow), the source cannot reach the sink in the
596 // residual graph. However, we still want to relabel all the nodes that cannot
597 // reach the sink but can reach the source (because if they have excess, we
598 // need to push it back to the source).
599 //
600 // Note that the second pass is not needed here if we use a two-pass algorithm
601 // to return the flow to the source after we found the min cut.
602 const int num_passes = use_two_phase_algorithm_ ? 1 : 2;
603 for (int pass = 0; pass < num_passes; ++pass) {
604 if (pass == 0) {
605 bfs_queue_.push_back(sink_);
606 } else {
607 bfs_queue_.push_back(source_);
608 }
609
610 while (queue_index != bfs_queue_.size()) {
611 const NodeIndex node = bfs_queue_[queue_index];
612 ++queue_index;
613 const NodeIndex candidate_distance = node_potential_[node] + 1;
614 for (OutgoingOrOppositeIncomingArcIterator it(*graph_, node); it.Ok();
615 it.Next()) {
616 const ArcIndex arc = it.Index();
617 const NodeIndex head = Head(arc);
618
619 // Skip the arc if the height of head was already set to the correct
620 // value (Remember we are doing reverse BFS).
621 if (node_in_bfs_queue_[head]) continue;
622
623 // TODO(user): By using more memory we can speed this up quite a bit by
624 // avoiding to take the opposite arc here, too options:
625 // - if (residual_arc_capacity_[arc] != arc_capacity_[arc])
626 // - if (opposite_arc_is_admissible_[arc]) // need updates.
627 // Experiment with the first option shows more than 10% gain on this
628 // function running time, which is the bottleneck on many instances.
629 const ArcIndex opposite_arc = Opposite(arc);
630 if (residual_arc_capacity_[opposite_arc] > 0) {
631 // Note(user): We used to have a DCHECK_GE(candidate_distance,
632 // node_potential_[head]); which is always true except in the case
633 // where we can push more than kMaxFlowQuantity out of the source. The
634 // problem comes from the fact that in this case, we call
635 // PushFlowExcessBackToSource() in the middle of the algorithm. The
636 // later call will break the properties of the node potential. Note
637 // however, that this function will recompute a good node potential
638 // for all the nodes and thus fix the issue.
639
640 // If head is active, we can steal some or all of its excess.
641 // This brings a huge gain on some problems.
642 // Note(user): I haven't seen this anywhere in the literature.
643 // TODO(user): Investigate more and maybe write a publication :)
644 if (node_excess_[head] > 0) {
645 const FlowQuantity flow = std::min(
646 node_excess_[head], residual_arc_capacity_[opposite_arc]);
647 PushFlow(flow, opposite_arc);
648
649 // If the arc became saturated, it is no longer in the residual
650 // graph, so we do not need to consider head at this time.
651 if (residual_arc_capacity_[opposite_arc] == 0) continue;
652 }
653
654 // Note that there is no need to touch first_admissible_arc_[node]
655 // because of the relaxed Relabel() we use.
656 node_potential_[head] = candidate_distance;
657 node_in_bfs_queue_[head] = true;
658 bfs_queue_.push_back(head);
659 }
660 }
661 }
662 }
663
664 // At the end of the search, some nodes may not be in the bfs_queue_. Such
665 // nodes cannot reach the sink_ or source_ in the residual graph, so there is
666 // no point trying to push flow toward them. We obtain this effect by setting
667 // their height to something unreachable.
668 //
669 // Note that this also prevents cycling due to our anti-overflow procedure.
670 // For instance, suppose there is an edge s -> n outgoing from the source. If
671 // node n has no other connection and some excess, we will push the flow back
672 // to the source, but if we don't update the height of n
673 // SaturateOutgoingArcsFromSource() will push the flow to n again.
674 // TODO(user): This is another argument for another anti-overflow algorithm.
675 for (NodeIndex node = 0; node < num_nodes; ++node) {
676 if (!node_in_bfs_queue_[node]) {
677 node_potential_[node] = 2 * num_nodes - 1;
678 }
679 }
680
681 // Reset the active nodes. Doing it like this pushes the nodes in increasing
682 // order of height. Note that bfs_queue_[0] is the sink_ so we skip it.
683 DCHECK(IsEmptyActiveNodeContainer());
684 for (int i = 1; i < bfs_queue_.size(); ++i) {
685 const NodeIndex node = bfs_queue_[i];
686 if (node_excess_[node] > 0) {
687 DCHECK(IsActive(node));
688 PushActiveNode(node);
689 }
690 }
691}
692
693template <typename Graph>
695 SCOPED_TIME_STAT(&stats_);
696 const NodeIndex num_nodes = graph_->num_nodes();
697
698 // If sink_ or source_ already have kMaxFlowQuantity, then there is no
699 // point pushing more flow since it will cause an integer overflow.
700 if (node_excess_[sink_] == kMaxFlowQuantity) return false;
701 if (node_excess_[source_] == -kMaxFlowQuantity) return false;
702
703 bool flow_pushed = false;
704 for (OutgoingArcIterator it(*graph_, source_); it.Ok(); it.Next()) {
705 const ArcIndex arc = it.Index();
706 const FlowQuantity flow = residual_arc_capacity_[arc];
707
708 // This is a special IsAdmissible() condition for the source.
709 if (flow == 0 || node_potential_[Head(arc)] >= num_nodes) continue;
710
711 // We are careful in case the sum of the flow out of the source is greater
712 // than kMaxFlowQuantity to avoid overflow.
713 const FlowQuantity current_flow_out_of_source = -node_excess_[source_];
714 DCHECK_GE(flow, 0) << flow;
715 DCHECK_GE(current_flow_out_of_source, 0) << current_flow_out_of_source;
716 const FlowQuantity capped_flow =
717 kMaxFlowQuantity - current_flow_out_of_source;
718 if (capped_flow < flow) {
719 // We push as much flow as we can so the current flow on the network will
720 // be kMaxFlowQuantity.
721
722 // Since at the beginning of the function, current_flow_out_of_source
723 // was different from kMaxFlowQuantity, we are sure to have pushed some
724 // flow before if capped_flow is 0.
725 if (capped_flow == 0) return true;
726 PushFlow(capped_flow, arc);
727 return true;
728 }
729 PushFlow(flow, arc);
730 flow_pushed = true;
731 }
732 DCHECK_LE(node_excess_[source_], 0);
733 return flow_pushed;
734}
735
736template <typename Graph>
738 SCOPED_TIME_STAT(&stats_);
739 // TODO(user): Do not allow a zero flow after fixing the UniformMaxFlow code.
740 DCHECK_GE(residual_arc_capacity_[Opposite(arc)] + flow, 0);
741 DCHECK_GE(residual_arc_capacity_[arc] - flow, 0);
742
743 // node_excess_ should be always greater than or equal to 0 except for the
744 // source where it should always be smaller than or equal to 0. Note however
745 // that we cannot check this because when we cancel the flow on a cycle in
746 // PushFlowExcessBackToSource(), we may break this invariant during the
747 // operation even if it is still valid at the end.
748
749 // Update the residual capacity of the arc and its opposite arc.
750 residual_arc_capacity_[arc] -= flow;
751 residual_arc_capacity_[Opposite(arc)] += flow;
752
753 // Update the excesses at the tail and head of the arc.
754 node_excess_[Tail(arc)] -= flow;
755 node_excess_[Head(arc)] += flow;
756}
757
758template <typename Graph>
760 SCOPED_TIME_STAT(&stats_);
761 DCHECK(IsEmptyActiveNodeContainer());
762 const NodeIndex num_nodes = graph_->num_nodes();
763 for (NodeIndex node = 0; node < num_nodes; ++node) {
764 if (IsActive(node)) {
765 if (use_two_phase_algorithm_ && node_potential_[node] >= num_nodes) {
766 continue;
767 }
768 PushActiveNode(node);
769 }
770 }
771}
772
773template <typename Graph>
775 SCOPED_TIME_STAT(&stats_);
776 // Usually SaturateOutgoingArcsFromSource() will saturate all the arcs from
777 // the source in one go, and we will loop just once. But in case we can push
778 // more than kMaxFlowQuantity out of the source the loop is as follow:
779 // - Push up to kMaxFlowQuantity out of the source on the admissible outgoing
780 // arcs. Stop if no flow was pushed.
781 // - Compute the current max-flow. This will push some flow back to the
782 // source and render more outgoing arcs from the source not admissible.
783 //
784 // TODO(user): This may not be the most efficient algorithm if we need to loop
785 // many times. An alternative may be to handle the source like the other nodes
786 // in the algorithm, initially putting an excess of kMaxFlowQuantity on it,
787 // and making the source active like any other node with positive excess. To
788 // investigate.
789 //
790 // TODO(user): The code below is buggy when more than kMaxFlowQuantity can be
791 // pushed out of the source (i.e. when we loop more than once in the while()).
792 // This is not critical, since this code is not used in the default algorithm
793 // computation. The issue is twofold:
794 // - InitializeActiveNodeContainer() doesn't push the nodes in
795 // the correct order.
796 // - PushFlowExcessBackToSource() may break the node potential properties, and
797 // we will need a call to GlobalUpdate() to fix that.
798 while (SaturateOutgoingArcsFromSource()) {
799 DCHECK(IsEmptyActiveNodeContainer());
800 InitializeActiveNodeContainer();
801 while (!IsEmptyActiveNodeContainer()) {
802 const NodeIndex node = GetAndRemoveFirstActiveNode();
803 if (node == source_ || node == sink_) continue;
804 Discharge(node);
805 }
806 if (use_two_phase_algorithm_) {
807 PushFlowExcessBackToSource();
808 }
809 }
810}
811
812template <typename Graph>
814 SCOPED_TIME_STAT(&stats_);
815
816 // TODO(user): This should be graph_->num_nodes(), but ebert graph does not
817 // have a correct size if the highest index nodes have no arcs.
818 const NodeIndex num_nodes = Graphs<Graph>::NodeReservation(*graph_);
819 std::vector<int> skip_active_node;
820
821 while (SaturateOutgoingArcsFromSource()) {
822 int num_skipped;
823 do {
824 num_skipped = 0;
825 skip_active_node.assign(num_nodes, 0);
826 skip_active_node[sink_] = 2;
827 skip_active_node[source_] = 2;
828 GlobalUpdate();
829 while (!IsEmptyActiveNodeContainer()) {
830 const NodeIndex node = GetAndRemoveFirstActiveNode();
831 if (skip_active_node[node] > 1) {
832 if (node != sink_ && node != source_) ++num_skipped;
833 continue;
834 }
835 const NodeIndex old_height = node_potential_[node];
836 Discharge(node);
837
838 // The idea behind this is that if a node height augments by more than
839 // one, then it is likely to push flow back the way it came. This can
840 // lead to very costly loops. A bad case is: source -> n1 -> n2 and n2
841 // just recently isolated from the sink. Then n2 will push flow back to
842 // n1, and n1 to n2 and so on. The height of each node will increase by
843 // steps of two until the height of the source is reached, which can
844 // take a long time. If the chain is longer, the situation is even
845 // worse. The behavior of this heuristic is related to the Gap
846 // heuristic.
847 //
848 // Note that the global update will fix all such cases efficiently. So
849 // the idea is to discharge the active node as much as possible, and
850 // then do a global update.
851 //
852 // We skip a node when this condition was true 2 times to avoid doing a
853 // global update too frequently.
854 if (node_potential_[node] > old_height + 1) {
855 ++skip_active_node[node];
856 }
857 }
858 } while (num_skipped > 0);
859 if (use_two_phase_algorithm_) {
860 PushFlowExcessBackToSource();
861 }
862 }
863}
864
865template <typename Graph>
867 SCOPED_TIME_STAT(&stats_);
868 const NodeIndex num_nodes = graph_->num_nodes();
869 while (true) {
870 DCHECK(IsActive(node));
871 for (OutgoingOrOppositeIncomingArcIterator it(*graph_, node,
872 first_admissible_arc_[node]);
873 it.Ok(); it.Next()) {
874 const ArcIndex arc = it.Index();
875 if (IsAdmissible(arc)) {
876 DCHECK(IsActive(node));
877 const NodeIndex head = Head(arc);
878 if (node_excess_[head] == 0) {
879 // The push below will make the node active for sure. Note that we may
880 // push the sink_, but that is handled properly in Refine().
881 PushActiveNode(head);
882 }
883 const FlowQuantity delta =
884 std::min(node_excess_[node], residual_arc_capacity_[arc]);
885 PushFlow(delta, arc);
886 if (node_excess_[node] == 0) {
887 first_admissible_arc_[node] = arc; // arc may still be admissible.
888 return;
889 }
890 }
891 }
892 Relabel(node);
893 if (use_two_phase_algorithm_ && node_potential_[node] >= num_nodes) break;
894 }
895}
896
897template <typename Graph>
899 SCOPED_TIME_STAT(&stats_);
900 // Because we use a relaxed version, this is no longer true if the
901 // first_admissible_arc_[node] was not actually the first arc!
902 // DCHECK(CheckRelabelPrecondition(node));
903 NodeHeight min_height = std::numeric_limits<NodeHeight>::max();
904 ArcIndex first_admissible_arc = Graph::kNilArc;
905 for (OutgoingOrOppositeIncomingArcIterator it(*graph_, node); it.Ok();
906 it.Next()) {
907 const ArcIndex arc = it.Index();
908 if (residual_arc_capacity_[arc] > 0) {
909 // Update min_height only for arcs with available capacity.
910 NodeHeight head_height = node_potential_[Head(arc)];
911 if (head_height < min_height) {
912 min_height = head_height;
913 first_admissible_arc = arc;
914
915 // We found an admissible arc at the current height, just stop there.
916 // This is the true first_admissible_arc_[node].
917 if (min_height + 1 == node_potential_[node]) break;
918 }
919 }
920 }
921 DCHECK_NE(first_admissible_arc, Graph::kNilArc);
922 node_potential_[node] = min_height + 1;
923
924 // Note that after a Relabel(), the loop will continue in Discharge(), and
925 // we are sure that all the arcs before first_admissible_arc are not
926 // admissible since their height is > min_height.
927 first_admissible_arc_[node] = first_admissible_arc;
928}
929
930template <typename Graph>
934
935template <typename Graph>
937 return IsArcValid(arc) && arc >= 0;
938}
939
940template <typename Graph>
944
945template <typename Graph>
947 std::numeric_limits<FlowQuantity>::max();
948
949template <typename Graph>
950template <bool reverse>
952 NodeIndex start, std::vector<NodeIndex>* result) {
953 // If start is not a valid node index, it can reach only itself.
954 // Note(user): This is needed because source and sink are given independently
955 // of the graph and sometimes before it is even constructed.
956 const NodeIndex num_nodes = graph_->num_nodes();
957 if (start >= num_nodes) {
958 result->clear();
959 result->push_back(start);
960 return;
961 }
962 bfs_queue_.clear();
963 node_in_bfs_queue_.assign(num_nodes, false);
964
965 int queue_index = 0;
966 bfs_queue_.push_back(start);
967 node_in_bfs_queue_[start] = true;
968 while (queue_index != bfs_queue_.size()) {
969 const NodeIndex node = bfs_queue_[queue_index];
970 ++queue_index;
971 for (OutgoingOrOppositeIncomingArcIterator it(*graph_, node); it.Ok();
972 it.Next()) {
973 const ArcIndex arc = it.Index();
974 const NodeIndex head = Head(arc);
975 if (node_in_bfs_queue_[head]) continue;
976 if (residual_arc_capacity_[reverse ? Opposite(arc) : arc] == 0) continue;
977 node_in_bfs_queue_[head] = true;
978 bfs_queue_.push_back(head);
979 }
980 }
981 *result = bfs_queue_;
982}
983
984template <typename Graph>
986 FlowModelProto model;
987 model.set_problem_type(FlowModelProto::MAX_FLOW);
988 for (int n = 0; n < graph_->num_nodes(); ++n) {
989 FlowNodeProto* node = model.add_nodes();
990 node->set_id(n);
991 if (n == source_) node->set_supply(1);
992 if (n == sink_) node->set_supply(-1);
993 }
994 for (int a = 0; a < graph_->num_arcs(); ++a) {
995 FlowArcProto* arc = model.add_arcs();
996 arc->set_tail(graph_->Tail(a));
997 arc->set_head(graph_->Head(a));
998 arc->set_capacity(Capacity(a));
999 }
1000 return model;
1001}
1002
1003// Explicit instantiations that can be used by a client.
1004//
1005// TODO(user): moves this code out of a .cc file and include it at the end of
1006// the header so it can work with any graph implementation ?
1007template <>
1009 std::numeric_limits<FlowQuantity>::max();
1010template <>
1011const FlowQuantity
1013 std::numeric_limits<FlowQuantity>::max();
1014template <>
1015const FlowQuantity
1017 std::numeric_limits<FlowQuantity>::max();
1018template <>
1019const FlowQuantity
1021 std::numeric_limits<FlowQuantity>::max();
1022
1023template class GenericMaxFlow<StarGraph>;
1027
1028} // namespace operations_research
void GetSourceSideMinCut(std::vector< NodeIndex > *result)
Definition max_flow.cc:244
void SetArcCapacity(ArcIndex arc, FlowQuantity new_capacity)
Sets the capacity for arc to new_capacity.
Definition max_flow.cc:194
void Refine()
Performs optimization step.
Definition max_flow.cc:774
void PushFlow(FlowQuantity flow, ArcIndex arc)
Definition max_flow.cc:737
bool Solve()
Returns true if a maximum flow was solved.
Definition max_flow.cc:353
StarGraph::OutgoingArcIterator OutgoingArcIterator
Definition max_flow.h:328
void InitializePreflow()
Initializes the preflow to a state that enables to run Refine.
Definition max_flow.cc:398
const Graph * graph_
A pointer to the graph passed as argument.
Definition max_flow.h:563
StatsGroup stats_
Statistics about this class.
Definition max_flow.h:657
void InitializeActiveNodeContainer()
Initializes the container active_nodes_.
Definition max_flow.cc:759
ArcIndexArray first_admissible_arc_
An array representing the first admissible arc for each node in graph_.
Definition max_flow.h:600
void Discharge(NodeIndex node)
Definition max_flow.cc:866
FlowModelProto CreateFlowModel()
Returns the protocol buffer representation of the current problem.
Definition max_flow.cc:985
QuantityArray node_excess_
An array representing the excess for each node in graph_.
Definition max_flow.h:566
const Graph * graph() const
Returns the graph associated to the current object.
Definition max_flow.h:354
void SetArcFlow(ArcIndex arc, FlowQuantity new_flow)
Sets the flow for arc.
Definition max_flow.cc:228
bool CheckRelabelPrecondition(NodeIndex node) const
Definition max_flow.cc:326
bool IsArcDirect(ArcIndex arc) const
Definition max_flow.cc:936
ArcIndex Opposite(ArcIndex arc) const
Definition max_flow.cc:931
std::vector< NodeIndex > bfs_queue_
Definition max_flow.h:627
std::string DebugString(absl::string_view context, ArcIndex arc) const
Definition max_flow.cc:337
bool IsArcValid(ArcIndex arc) const
Definition max_flow.cc:941
std::vector< NodeIndex > active_nodes_
Definition max_flow.h:606
void GetSinkSideMinCut(std::vector< NodeIndex > *result)
Definition max_flow.cc:250
void ComputeReachableNodes(NodeIndex start, std::vector< NodeIndex > *result)
Definition max_flow.cc:951
GenericMaxFlow(const Graph *graph, NodeIndex source, NodeIndex sink)
Definition max_flow.cc:144
ArcIndex NumArcs() const
Returns the current number of arcs in the graph.
Definition max_flow.cc:45
FlowModelProto CreateFlowModelProto(NodeIndex source, NodeIndex sink) const
Creates the protocol buffer representation of the current problem.
Definition max_flow.cc:124
FlowQuantity OptimalFlow() const
Definition max_flow.cc:110
void SetArcCapacity(ArcIndex arc, FlowQuantity capacity)
Definition max_flow.cc:55
ArcIndex AddArcWithCapacity(NodeIndex tail, NodeIndex head, FlowQuantity capacity)
Definition max_flow.cc:32
void GetSinkSideMinCut(std::vector< NodeIndex > *result)
Definition max_flow.cc:119
FlowQuantity Capacity(ArcIndex arc) const
Definition max_flow.cc:51
@ BAD_RESULT
This should not happen. There was an error in our code (i.e. file a bug).
Definition max_flow.h:199
@ BAD_INPUT
The input is inconsistent (bad tail/head/capacity values).
Definition max_flow.h:197
Status Solve(NodeIndex source, NodeIndex sink)
Definition max_flow.cc:59
NodeIndex Head(ArcIndex arc) const
Definition max_flow.cc:49
void GetSourceSideMinCut(std::vector< NodeIndex > *result)
Definition max_flow.cc:114
NodeIndex Tail(ArcIndex arc) const
Definition max_flow.cc:47
FlowQuantity Flow(ArcIndex arc) const
Definition max_flow.cc:112
bool Reserve(int64_t new_min_index, int64_t new_max_index)
Definition zvector.h:96
void SetAll(T value)
Sets all the elements in the array to value.
Definition zvector.h:131
bool IsNodeValid(NodeIndexType node) const
Returns true if the given node is a valid node of the graph.
Definition graph.h:224
int64_t a
Definition table.cc:44
GraphType graph
GRBmodel * model
GurobiMPCallbackContext * context
int arc
In SWIG mode, we don't want anything besides these top-level includes.
int64_t delta
Definition resource.cc:1709
int head
int tail
int64_t start
#define IF_STATS_ENABLED(instructions)
Definition stats.h:417
#define SCOPED_TIME_STAT(stats)
Definition stats.h:418
static ArcIndex ArcReservation(const Graph &graph)
Definition graphs.h:41
static bool IsArcValid(const Graph &graph, ArcIndex arc)
Definition graphs.h:35
static ArcIndex OppositeArc(const Graph &graph, ArcIndex arc)
Definition graphs.h:32
static NodeIndex NodeReservation(const Graph &graph)
Definition graphs.h:38