Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
flow_graph.h
Go to the documentation of this file.
1// Copyright 2010-2025 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
14#ifndef UTIL_GRAPH_FLOW_GRAPH_H_
15#define UTIL_GRAPH_FLOW_GRAPH_H_
16
17#include <cstddef>
18#include <cstdint>
19#include <memory>
20#include <vector>
21
22#include "absl/log/check.h"
23#include "absl/types/span.h"
25#include "ortools/graph/graph.h"
27
28namespace util {
29
30// Graph specialized for max-flow/min-cost-flow algorithms.
31// It follows the ortools/graph/graph.h interface.
32//
33// For max-flow or min-cost-flow we need a directed graph where each arc from
34// tail to head has a "reverse" arc from head to tail. In practice many input
35// graphs already have such reverse arc and it can make a big difference just to
36// reuse them.
37//
38// This is similar to ReverseArcStaticGraph but handle reverse arcs in a
39// different way. Instead of always creating a NEW reverse arc for each arc of
40// the input graph, this will detect if a reverse arc was already present in the
41// input, and do not create a new one when this is the case. In the best case,
42// this can gain a factor 2 in the final graph size, note however that the graph
43// construction is slighlty slower because of this detection.
44//
45// A side effect of reusing reverse arc is also that we cannot anymore clearly
46// separate the original arcs from the newly created one. So the algorithm needs
47// to be able to handle that.
48//
49// TODO(user): Currently only max-flow handles this graph, but not
50// min-cost-flow.
51template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
52class FlowGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
53 // Note that we do NOT use negated indices for reverse arc. So we use false
54 // for the last template argument here HasNegativeReverseArcs.
59 using Base::num_arcs_;
60 using Base::num_nodes_;
61
62 public:
63 FlowGraph() = default;
64
65 FlowGraph(NodeIndexType num_nodes, ArcIndexType arc_capacity) {
66 this->Reserve(num_nodes, arc_capacity);
67 this->FreezeCapacities();
68 this->AddNode(num_nodes - 1);
69 }
70
71 NodeIndexType Head(ArcIndexType arc) const {
72 DCHECK(is_built_);
73 DCHECK_GE(arc, 0);
74 DCHECK_LT(arc, num_arcs_);
75 return heads_[arc];
76 }
77
78 NodeIndexType Tail(ArcIndexType arc) const {
79 DCHECK(is_built_);
80 DCHECK_GE(arc, 0);
81 DCHECK_LT(arc, num_arcs_);
82
83 // Note that we could trade memory for speed if this happens to be hot.
84 // However, it is expected that most user will access arcs via
85 // for (const int arc : graph.OutgoingArcs(tail)) {}
86 // in which case all arcs already have a known tail.
87 return heads_[reverse_[arc]];
88 }
89
90 // Each arc has a reverse.
91 // If not added by the client, we have created one, see Build().
92 ArcIndexType OppositeArc(ArcIndexType arc) const {
93 DCHECK(is_built_);
94 DCHECK_GE(arc, 0);
95 DCHECK_LT(arc, num_arcs_);
96 return reverse_[arc];
97 }
98
99 // Iteration.
101 return OutgoingArcsStartingFrom(node, start_[node]);
102 }
104 NodeIndexType node, ArcIndexType from) const {
105 DCHECK(is_built_);
106 DCHECK_GE(node, 0);
107 DCHECK_LT(node, num_nodes_);
108 DCHECK_GE(from, start_[node]);
109 return util::IntegerRange<ArcIndexType>(from, start_[node + 1]);
110 }
111
112 // These are needed to use with the current max-flow implementation.
113 // We don't distinguish direct from reverse arc anymore, and this is just
114 // the same as OutgoingArcs()/OutgoingArcsStartingFrom().
116 NodeIndexType node) const {
117 return OutgoingArcs(node);
118 }
120 NodeIndexType node, ArcIndexType from) const {
121 return OutgoingArcsStartingFrom(node, from);
122 }
123
124 absl::Span<const NodeIndexType> operator[](NodeIndexType node) const {
125 const int b = start_[node];
126 const size_t size = start_[node + 1] - b;
127 if (size == 0) return {};
128 return {&heads_[b], size};
129 }
130
131 void ReserveArcs(ArcIndexType bound) override {
132 Base::ReserveArcs(bound);
133 tmp_tails_.reserve(bound);
134 tmp_heads_.reserve(bound);
135 }
136
137 void AddNode(NodeIndexType node) {
138 num_nodes_ = std::max(num_nodes_, node + 1);
139 }
140
141 ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head) {
142 AddNode(tail > head ? tail : head);
143 tmp_tails_.push_back(tail);
144 tmp_heads_.push_back(head);
145 return num_arcs_++;
146 }
147
148 void Build() { Build(nullptr); }
149 void Build(std::vector<ArcIndexType>* permutation);
150
151 // This influence what Build() does. If true, we will detect already existing
152 // pairs of (arc, reverse_arc) and only construct new reverse arc for the one
153 // that we couldn't match. Otherwise, we will construct a new reverse arc for
154 // each input arcs.
155 void SetDetectReverse(bool value) { option_detect_reverse_ = value; }
156
157 // This influence what Build() does. If true, the order of each outgoing arc
158 // will be sorted by their head. Otherwise, it will be in the original order
159 // with the newly created reverse arc afterwards.
160 void SetSortByHead(bool value) { option_sort_by_head_ = value; }
161
162 private:
163 // Computes the permutation that would stable_sort input, and fill start_
164 // to correspond to the beginning of each section of identical elements.
165 // This assumes input only contains element in [0, num_nodes_).
166 void FillPermutationAndStart(absl::Span<const NodeIndexType> input,
167 absl::Span<ArcIndexType> perm);
168
169 // These are similar but tie-break identical element by "second_criteria".
170 // We have two versions. It seems filling the reverse permutation instead is
171 // slighthly faster and require less memory.
172 void FillPermutationAndStart(absl::Span<const NodeIndexType> first_criteria,
173 absl::Span<const NodeIndexType> second_criteria,
174 absl::Span<ArcIndexType> perm);
175 void FillReversePermutationAndStart(
176 absl::Span<const NodeIndexType> first_criteria,
177 absl::Span<const NodeIndexType> second_criteria,
178 absl::Span<ArcIndexType> reverse_perm);
179
180 // Internal helpers for the Fill*() functions above.
181 void InitializeStart(absl::Span<const NodeIndexType> input);
182 void RestoreStart();
183
184 // Different build options.
185 bool option_detect_reverse_ = true;
186 bool option_sort_by_head_ = false;
187
188 // Starts at false and set to true on Build(). This is mainly used in
189 // DCHECKs() to guarantee that the graph is just built once, and no new arcs
190 // are added afterwards.
191 bool is_built_ = false;
192
193 // This is just used before Build() to store the AddArc() data.
194 std::vector<NodeIndexType> tmp_tails_;
195 std::vector<NodeIndexType> tmp_heads_;
196
197 // First outgoing arc for a node.
198 // Indexed by NodeIndexType + a sentinel start_[num_nodes_] = num_arcs_.
199 std::unique_ptr<ArcIndexType[]> start_;
200
201 // Indexed by ArcIndexType an of size num_arcs_.
202 // Head for an arc.
203 std::unique_ptr<NodeIndexType[]> heads_;
204 // Reverse arc for an arc.
205 std::unique_ptr<ArcIndexType[]> reverse_;
206};
207
208namespace internal {
209
210// Helper to permute a given span into another one.
211template <class Key, class Value>
212void PermuteInto(absl::Span<const Key> permutation,
213 absl::Span<const Value> input, absl::Span<Value> output) {
214 DCHECK_EQ(permutation.size(), input.size());
215 DCHECK_EQ(permutation.size(), output.size());
216 for (int i = 0; i < permutation.size(); ++i) {
217 output[permutation[i]] = input[i];
218 }
219}
220
221} // namespace internal
222
223template <typename NodeIndexType, typename ArcIndexType>
224void FlowGraph<NodeIndexType, ArcIndexType>::InitializeStart(
225 absl::Span<const NodeIndexType> input) {
226 // Computes the number of each elements.
227 std::fill(start_.get(), start_.get() + num_nodes_, 0);
228 start_[num_nodes_] = input.size(); // sentinel.
229
230 for (const NodeIndexType node : input) start_[node]++;
231
232 // Compute the cumulative sums (shifted one element to the right).
233 int sum = 0;
234 for (int i = 0; i < num_nodes_; ++i) {
235 int temp = start_[i];
236 start_[i] = sum;
237 sum += temp;
238 }
239 DCHECK_EQ(sum, input.size());
240}
241
242template <typename NodeIndexType, typename ArcIndexType>
243void FlowGraph<NodeIndexType, ArcIndexType>::RestoreStart() {
244 // Restore in start[i] the index of the first arc with tail >= i.
245 for (int i = num_nodes_; --i > 0;) {
246 start_[i] = start_[i - 1];
247 }
248 start_[0] = 0;
249}
250
251template <typename NodeIndexType, typename ArcIndexType>
252void FlowGraph<NodeIndexType, ArcIndexType>::FillPermutationAndStart(
253 absl::Span<const NodeIndexType> input, absl::Span<ArcIndexType> perm) {
254 DCHECK_EQ(input.size(), perm.size());
255 InitializeStart(input);
256
257 // Computes the permutation.
258 // Note that this temporarily alters the start_ vector.
259 for (int i = 0; i < input.size(); ++i) {
260 perm[i] = start_[input[i]]++;
261 }
262
263 RestoreStart();
264}
265
266template <typename NodeIndexType, typename ArcIndexType>
267void FlowGraph<NodeIndexType, ArcIndexType>::FillPermutationAndStart(
268 absl::Span<const NodeIndexType> first_criteria,
269 absl::Span<const NodeIndexType> second_criteria,
270 absl::Span<ArcIndexType> perm) {
271 // First sort by second_criteria.
272 FillPermutationAndStart(second_criteria, absl::MakeSpan(perm));
273
274 // Create a temporary permuted version of first_criteria.
275 const auto tmp_storage = std::make_unique<ArcIndexType[]>(num_arcs_);
276 const auto tmp = absl::MakeSpan(tmp_storage.get(), num_arcs_);
278
279 // Now sort by permuted first_criteria.
280 const auto second_perm_storage = std::make_unique<ArcIndexType[]>(num_arcs_);
281 const auto second_perm = absl::MakeSpan(second_perm_storage.get(), num_arcs_);
282 FillPermutationAndStart(tmp, second_perm);
283
284 // Update the final permutation. This guarantee that for the same
285 // first_criteria, the second_criteria will be used.
286 for (ArcIndexType& image : perm) {
287 image = second_perm[image];
288 }
289}
290
291template <typename NodeIndexType, typename ArcIndexType>
292void FlowGraph<NodeIndexType, ArcIndexType>::FillReversePermutationAndStart(
293 absl::Span<const NodeIndexType> first_criteria,
294 absl::Span<const NodeIndexType> second_criteria,
295 absl::Span<ArcIndexType> reverse_perm) {
296 // Compute the reverse perm according to the second criteria.
297 InitializeStart(second_criteria);
298 const auto r_perm_storage = std::make_unique<ArcIndexType[]>(num_arcs_);
299 const auto r_perm = absl::MakeSpan(r_perm_storage.get(), num_arcs_);
300 auto* start = start_.get();
301 for (int i = 0; i < second_criteria.size(); ++i) {
302 r_perm[start[second_criteria[i]]++] = i;
303 }
304
305 // Now radix-sort by the first criteria and combine the two permutations.
306 InitializeStart(first_criteria);
307 for (const int i : r_perm) {
308 reverse_perm[start[first_criteria[i]]++] = i;
309 }
310 RestoreStart();
311}
312
313template <typename NodeIndexType, typename ArcIndexType>
315 std::vector<ArcIndexType>* permutation) {
316 DCHECK(!is_built_);
317 if (is_built_) return;
318 is_built_ = true;
319
320 start_ = std::make_unique<ArcIndexType[]>(num_nodes_ + 1);
321 std::vector<ArcIndexType> perm(num_arcs_);
322
323 const int kNoExplicitReverseArc = -1;
324 std::vector<ArcIndexType> reverse(num_arcs_, kNoExplicitReverseArc);
325
326 bool fix_final_permutation = false;
327 if (option_detect_reverse_) {
328 // Step 1 we only keep a "canonical version" of each arc where tail <= head.
329 // This will allow us to detect reverse as duplicates instead.
330 std::vector<bool> was_reversed_(num_arcs_, false);
331 for (int arc = 0; arc < num_arcs_; ++arc) {
332 if (tmp_heads_[arc] < tmp_tails_[arc]) {
333 std::swap(tmp_heads_[arc], tmp_tails_[arc]);
334 was_reversed_[arc] = true;
335 }
336 }
337
338 // Step 2, compute the perm to sort by tail then head.
339 // This is something we do a few times here, we compute the permutation with
340 // a kind of radix sort by computing the degree of each node.
341 auto reverse_perm = absl::MakeSpan(perm); // we reuse space
342 FillReversePermutationAndStart(tmp_tails_, tmp_heads_,
343 absl::MakeSpan(reverse_perm));
344
345 // Identify arc pairs that are reverse of each other and fill reverse for
346 // them. The others position will stay at -1.
347 int candidate_i = 0;
348 int num_filled = 0;
349 for (int i = 0; i < num_arcs_; ++i) {
350 const int arc = reverse_perm[i];
351 const int candidate_arc = reverse_perm[candidate_i];
352 if (tmp_heads_[arc] != tmp_heads_[candidate_arc] ||
353 tmp_tails_[arc] != tmp_tails_[candidate_arc]) {
354 candidate_i = i;
355 continue;
356 }
357
358 if (was_reversed_[arc] != was_reversed_[candidate_arc]) {
359 DCHECK_EQ(reverse[arc], -1);
360 DCHECK_EQ(reverse[candidate_arc], -1);
361 reverse[arc] = candidate_arc;
362 reverse[candidate_arc] = arc;
363 num_filled += 2;
364
365 // Find the next candidate without reverse if there is a gap.
366 for (; ++candidate_i < i;) {
367 if (reverse[reverse_perm[candidate_i]] == -1) break;
368 }
369 if (candidate_i == i) {
370 candidate_i = ++i; // Advance by two.
371 }
372 }
373 }
374
375 // Create the extra reversed arcs needed.
376 {
377 const int extra_size = num_arcs_ - num_filled;
378 tmp_tails_.resize(num_arcs_ + extra_size);
379 tmp_heads_.resize(num_arcs_ + extra_size);
380 reverse.resize(num_arcs_ + extra_size);
381 int new_index = num_arcs_;
382 for (int i = 0; i < num_arcs_; ++i) {
383 // Fix the initial swap.
384 if (was_reversed_[i]) {
385 std::swap(tmp_tails_[i], tmp_heads_[i]);
386 }
387
388 if (reverse[i] != kNoExplicitReverseArc) continue;
389 reverse[i] = new_index;
390 reverse[new_index] = i;
391 tmp_tails_[new_index] = tmp_heads_[i];
392 tmp_heads_[new_index] = tmp_tails_[i];
393 ++new_index;
394 }
395 CHECK_EQ(new_index, num_arcs_ + extra_size);
396 }
397 } else {
398 // Just create a reverse for each arc.
399 tmp_tails_.resize(2 * num_arcs_);
400 tmp_heads_.resize(2 * num_arcs_);
401 reverse.resize(2 * num_arcs_);
402 for (int arc = 0; arc < num_arcs_; ++arc) {
403 const int image = num_arcs_ + arc;
404 tmp_heads_[image] = tmp_tails_[arc];
405 tmp_tails_[image] = tmp_heads_[arc];
406 reverse[image] = arc;
407 reverse[arc] = image;
408 }
409
410 // It seems better to put all the reverse first instead of last in
411 // the adjacency list, so lets do that here. Note that we need to fix
412 // the permutation returned to the user in this case.
413 //
414 // With this, we should have almost exactly the same behavior
415 // as ReverseArcStaticGraph().
416 fix_final_permutation = true;
417 for (int arc = 0; arc < num_arcs_; ++arc) {
418 const int image = num_arcs_ + arc;
419 std::swap(tmp_heads_[image], tmp_heads_[arc]);
420 std::swap(tmp_tails_[image], tmp_tails_[arc]);
421 }
422 }
423
424 num_arcs_ = tmp_heads_.size();
425 perm.resize(num_arcs_);
426
427 // Do we sort each OutgoingArcs(node) by head ?
428 // Or is it better to keep all new reverse arc before or after ?
429 //
430 // TODO(user): For now we only support sorting, or all new reverse after
431 // and keep the original arc order.
432 if (option_sort_by_head_) {
433 FillPermutationAndStart(tmp_tails_, tmp_heads_, absl::MakeSpan(perm));
434 } else {
435 FillPermutationAndStart(tmp_tails_, absl::MakeSpan(perm));
436 }
437
438 // Create the final heads_.
439 heads_ = std::make_unique<NodeIndexType[]>(num_arcs_);
441 perm, tmp_heads_, absl::MakeSpan(heads_.get(), num_arcs_));
442
443 // No longer needed.
444 gtl::STLClearObject(&tmp_tails_);
445 gtl::STLClearObject(&tmp_heads_);
446
447 // We now create reverse_, this needs the permutation on both sides.
448 reverse_ = std::make_unique<ArcIndexType[]>(num_arcs_);
449 for (int i = 0; i < num_arcs_; ++i) {
450 reverse_[perm[i]] = perm[reverse[i]];
451 }
452
453 if (permutation != nullptr) {
454 if (fix_final_permutation) {
455 for (int i = 0; i < num_arcs_ / 2; ++i) {
456 std::swap(perm[i], perm[num_arcs_ / 2 + i]);
457 }
458 }
459 permutation->swap(perm);
460 }
461
462 node_capacity_ = num_nodes_;
463 arc_capacity_ = num_arcs_;
464 this->FreezeCapacities();
465}
466
467} // namespace util
468
469#endif // UTIL_GRAPH_FLOW_GRAPH_H_
virtual void ReserveArcs(ArcIndexType bound)
Definition graph.h:257
void Reserve(int32_t node_capacity, int32_t arc_capacity)
Definition graph.h:263
FlowGraph(NodeIndexType num_nodes, ArcIndexType arc_capacity)
Definition flow_graph.h:65
void SetDetectReverse(bool value)
Definition flow_graph.h:155
void SetSortByHead(bool value)
Definition flow_graph.h:160
util::IntegerRange< ArcIndexType > OutgoingOrOppositeIncomingArcs(NodeIndexType node) const
Definition flow_graph.h:115
ArcIndexType OppositeArc(ArcIndexType arc) const
Definition flow_graph.h:92
void AddNode(NodeIndexType node)
Definition flow_graph.h:137
absl::Span< const NodeIndexType > operator[](NodeIndexType node) const
Definition flow_graph.h:124
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
Definition flow_graph.h:141
NodeIndexType Head(ArcIndexType arc) const
Definition flow_graph.h:71
util::IntegerRange< ArcIndexType > OutgoingArcs(NodeIndexType node) const
Iteration.
Definition flow_graph.h:100
util::IntegerRange< ArcIndexType > OutgoingOrOppositeIncomingArcsStartingFrom(NodeIndexType node, ArcIndexType from) const
Definition flow_graph.h:119
util::IntegerRange< ArcIndexType > OutgoingArcsStartingFrom(NodeIndexType node, ArcIndexType from) const
Definition flow_graph.h:103
void ReserveArcs(ArcIndexType bound) override
Definition flow_graph.h:131
NodeIndexType Tail(ArcIndexType arc) const
Definition flow_graph.h:78
FlowGraph()=default
void STLClearObject(T *obj)
Definition stl_util.h:123
void PermuteInto(absl::Span< const Key > permutation, absl::Span< const Value > input, absl::Span< Value > output)
Helper to permute a given span into another one.
Definition flow_graph.h:212
A collections of i/o utilities for the Graph classes in ./graph.h.
static int input(yyscan_t yyscanner)