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