Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
dag_constrained_shortest_path.h
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
14#ifndef OR_TOOLS_GRAPH_DAG_CONSTRAINED_SHORTEST_PATH_H_
15#define OR_TOOLS_GRAPH_DAG_CONSTRAINED_SHORTEST_PATH_H_
16
17#include <cmath>
18#include <limits>
19#include <vector>
20
21#include "absl/algorithm/container.h"
22#include "absl/base/log_severity.h"
23#include "absl/log/check.h"
24#include "absl/strings/str_format.h"
25#include "absl/types/span.h"
28#include "ortools/graph/graph.h"
29
30namespace operations_research {
31
32// This library provides APIs to compute the constrained shortest path (CSP) on
33// a given directed acyclic graph (DAG) with resources on each arc. A CSP is a
34// shortest path on a DAG which does not exceed a set of maximum resources
35// consumption. The algorithm is exponential and has no guarantee to finish. It
36// is based on bi-drectionnal search. First is a forward pass from the source to
37// nodes “somewhere in the middle” to generate forward labels, just as the
38// onedirectional labeling algorithm we discussed; then a symmetric backward
39// pass from the destination generates backward labels; and finally at each node
40// with both forward and backward labels, it joins any pair of labels to form a
41// feasible complete path. Intuitively, the number of labels grows exponentially
42// with the number of arcs in the path. The overall number of labels are then
43// expected to be smaller with shorter paths. For DAG with a topological
44// ordering, we can pick any node (usually right in the middle) as a *midpoint*
45// to stop each pass at. Then labels can be joined at only one half of the nodes
46// by considering all edges between each half.
47//
48// In the DAG, multiple arcs between the same pair of nodes is allowed. However,
49// self-loop arcs are not allowed.
50//
51// Note that we use the length formalism here, but the arc lengths can represent
52// any numeric physical quantity. A shortest path will just be a path minimizing
53// this quantity where the length/resources of a path is the sum of the
54// length/resources of its arcs. An arc length can be negative, or +inf
55// (indicating that it should not be used). An arc length cannot be -inf or nan.
56//
57// Resources on each arc must be non-negative and cannot be +inf or nan.
58
59// -----------------------------------------------------------------------------
60// Basic API.
61// -----------------------------------------------------------------------------
62
63// `tail` and `head` should both be in [0, num_nodes)
64// If the length is +inf, then the arc is not used.
65struct ArcWithLengthAndResources {
66 int from = 0;
67 int to = 0;
68 double length = 0.0;
69 std::vector<double> resources;
70};
72// Returns {+inf, {}, {}} if there is no path of finite length from the source
73// to the destination. Dies if `arcs_with_length_and_resources` has a cycle.
75 int num_nodes,
76 absl::Span<const ArcWithLengthAndResources> arcs_with_length_and_resources,
77 int source, int destination, const std::vector<double>& max_resources);
78
79// -----------------------------------------------------------------------------
80// Advanced API.
81// -----------------------------------------------------------------------------
82// A wrapper that holds the memory needed to run many constrained shortest path
83// computations efficiently on the given DAG (on which resources do not change).
84// `GraphType` can use one of the interfaces defined in `util/graph/graph.h`.
85template <class GraphType>
86#if __cplusplus >= 202002L
87 requires DagGraphType<GraphType>
88#endif
90 public:
91 using NodeIndex = typename GraphType::NodeIndex;
92 using ArcIndex = typename GraphType::ArcIndex;
94 // IMPORTANT: All arguments must outlive the class.
95 //
96 // The vectors of `arc_lengths` and `arc_resources[i]` (for all resource i)
97 // *must* be of size `graph.num_arcs()` and indexed the same way as in
98 // `graph`. The vector `arc_resources` and `max_resources` *must* be of same
99 // size.
100 //
101 // You *must* provide a topological order. You can use
102 // `util::graph::FastTopologicalSort(graph)` to compute one if you don't
103 // already have one. An invalid topological order results in an upper bound
104 // for all shortest path computations. For maximum performance, you can
105 // further reindex the nodes under the topological order so that the memory
106 // access pattern is generally forward instead of random. For example, if the
107 // topological order for a graph with 4 nodes is [2,1,0,3], you can re-label
108 // the nodes 2, 1, and 0 to 0, 1, and 2 (and updates arcs accordingly).
109 //
110 // Validity of arcs and topological order are DCHECKed.
111 //
112 // If the number of labels in memory exceeds `max_num_created_labels / 2` at
113 // any point in each pass of the algorithm, new labels are not generated
114 // anymore and it returns the best path found so far, most particularly the
115 // empty path if none were found.
116 //
117 // IMPORTANT: You cannot modify anything except `arc_lengths` between calls to
118 // the `RunConstrainedShortestPathOnDag()` function.
120 const GraphType* graph, const std::vector<double>* arc_lengths,
121 const std::vector<std::vector<double>>* arc_resources,
122 absl::Span<const NodeIndex> topological_order,
123 absl::Span<const NodeIndex> sources,
124 absl::Span<const NodeIndex> destinations,
125 const std::vector<double>* max_resources,
126 int max_num_created_labels = 1e9);
127
128 // Returns {+inf, {}, {}} if there is no constrained path of finite length
129 // wihtin resources constraints from one node in `sources` to one node in
130 // `destinations`.
132
133 // For benchmarking and informational purposes, returns the number of labels
134 // generated in the call of `RunConstrainedShortestPathOnDag()`.
135 int label_count() const {
136 return lengths_from_sources_[FORWARD].size() +
137 lengths_from_sources_[BACKWARD].size();
138 }
140 private:
141 enum Direction {
142 FORWARD = 0,
143 BACKWARD = 1,
144 };
145
146 inline static Direction Reverse(Direction d) {
147 return d == FORWARD ? BACKWARD : FORWARD;
148 }
149
150 // A LabelPair includes the `length` of a path that can be constructed by
151 // merging the paths from two *linkable* labels corresponding to
152 // `label_index`.
153 struct LabelPair {
154 double length = 0.0;
155 int label_index[2];
156 };
157
158 void RunHalfConstrainedShortestPathOnDag(
159 const GraphType& reverse_graph, absl::Span<const double> arc_lengths,
160 absl::Span<const std::vector<double>> arc_resources,
161 absl::Span<const std::vector<double>> min_arc_resources,
162 absl::Span<const double> max_resources, int max_num_created_labels,
163 std::vector<double>& lengths_from_sources,
164 std::vector<std::vector<double>>& resources_from_sources,
165 std::vector<ArcIndex>& incoming_arc_indices_from_sources,
166 std::vector<int>& incoming_label_indices_from_sources,
167 std::vector<int>& first_label, std::vector<int>& num_labels);
168
169 // Returns the arc index linking two nodes from each pass forming the best
170 // path. Returns -1 if no better path than the one found from
171 // `best_label_pair` is found.
172 ArcIndex MergeHalfRuns(
173 const GraphType& graph, absl::Span<const double> arc_lengths,
174 absl::Span<const std::vector<double>> arc_resources,
175 absl::Span<const double> max_resources,
176 const std::vector<NodeIndex> sub_node_indices[2],
177 const std::vector<double> lengths_from_sources[2],
178 const std::vector<std::vector<double>> resources_from_sources[2],
179 const std::vector<int> first_label[2],
180 const std::vector<int> num_labels[2], LabelPair& best_label_pair);
181
182 // Returns the path as list of arc indices that starts from a node in
183 // `sources` (if `direction` iS FORWARD) or `destinations` (if `direction` is
184 // BACKWARD) and ends in node represented by `best_label_index`.
185 std::vector<ArcIndex> ArcPathTo(
186 int best_label_index,
187 absl::Span<const ArcIndex> incoming_arc_indices_from_sources,
188 absl::Span<const int> incoming_label_indices_from_sources) const;
189
190 // Returns the list of all the nodes implied by a given `arc_path`.
191 std::vector<NodeIndex> NodePathImpliedBy(absl::Span<const ArcIndex> arc_path,
192 const GraphType& graph) const;
193
194 static constexpr double kTolerance = 1e-6;
195
196 const GraphType* const graph_;
197 const std::vector<double>* const arc_lengths_;
198 const std::vector<std::vector<double>>* const arc_resources_;
199 const std::vector<double>* const max_resources_;
200 absl::Span<const NodeIndex> sources_;
201 absl::Span<const NodeIndex> destinations_;
202 const int num_resources_;
203
204 // Data about *reachable* sub-graphs split in two for bidirectional search.
205 // Reachable nodes are nodes that can be reached given the resources
206 // constraints, i.e., for each resource, the sum of the minimum resource to
207 // get to a node from a node in `sources` and to get from a node to a node in
208 // `destinations` should be less than the maximum resource. Reachable arcs are
209 // arcs linking reachable nodes.
210 //
211 // `sub_reverse_graph_[dir]` is the reachable sub-graph split in *half* with
212 // an additional linked to sources (resp. destinations) for the forward (resp.
213 // backward) direction. For the forward (resp. backward) direction, nodes are
214 // indexed using the original (resp. reverse) topological order.
215 GraphType sub_reverse_graph_[2];
216 std::vector<std::vector<double>> sub_arc_resources_[2];
217 // `sub_full_arc_indices_[dir]` has size `sub_reverse_graph_[dir].num_arcs()`
218 // such that `sub_full_arc_indices_[dir][sub_arc] = arc` where `sub_arc` is
219 // the arc in the reachable sub-graph for direction `dir` (i.e.
220 // `sub_reverse_graph[dir]`) and `arc` is the arc in the original graph (i.e.
221 // `graph`).
222 std::vector<NodeIndex> sub_full_arc_indices_[2];
223 // `sub_node_indices_[dir]` has size `graph->num_nodes()` such that
224 // `sub_node_indices[dir][node] = sub_node` where `node` is the node in the
225 // original graph (i.e. `graph`) and `sub_node` is the node in the reachable
226 // sub-graph for direction `dir` (i.e. `sub_reverse_graph[dir]`) and -1 if
227 // `node` is not present in reachable sub-graph.
228 std::vector<NodeIndex> sub_node_indices_[2];
229 // `sub_is_source_[dir][sub_dir]` has size
230 // `sub_reverse_graph_[dir].num_nodes()` such that
231 // `sub_is_source_[dir][sub_dir][sub_node]` is true if `sub_node` is a node in
232 // the reachable sub-graph for direction `dir` (i.e. `sub_reverse_graph[dir]`)
233 // which is a source (resp. destination) is `sub_dir` is FORWARD (resp.
234 // BACKWARD).
235 std::vector<bool> sub_is_source_[2][2];
236 // `sub_min_arc_resources_[dir]` has size `max_resources->size()` and
237 // `sub_min_arc_resources_[dir][r]`, `sub_reverse_graph_[dir].num_nodes()`
238 // such that `sub_min_arc_resources_[dir][r][sub_node]` is the minimum of
239 // resource r needed to get to a destination (resp. come from a source) if
240 // `dir` is FORWARD (resp. BACKWARD).
241 std::vector<std::vector<double>> sub_min_arc_resources_[2];
242 // Maximum number of labels created for each sub-graph.
243 int max_num_created_labels_[2];
244
245 // Data about the last call of the RunConstrainedShortestPathOnDag()
246 // function. A path is only added to the following vectors if and only if
247 // it is feasible with respect to all resources.
248 // A Label includes the cumulative length, resources and the previous arc used
249 // in the path to get to this node.
250 // Instead of having a single vector of `Label` objects (cl/590819865), we
251 // split them into 3 vectors of more fundamental types as this improves
252 // push_back operations and memory release.
253 std::vector<double> lengths_from_sources_[2];
254 std::vector<std::vector<double>> resources_from_sources_[2];
255 std::vector<ArcIndex> incoming_arc_indices_from_sources_[2];
256 std::vector<int> incoming_label_indices_from_sources_[2];
257 std::vector<int> node_first_label_[2];
258 std::vector<int> node_num_labels_[2];
259};
260
261std::vector<int> GetInversePermutation(absl::Span<const int> permutation);
262
263// -----------------------------------------------------------------------------
264// Implementation.
265// -----------------------------------------------------------------------------
266
267template <class GraphType>
268#if __cplusplus >= 202002L
269 requires DagGraphType<GraphType>
270#endif
273 const GraphType* graph, const std::vector<double>* arc_lengths,
274 const std::vector<std::vector<double>>* arc_resources,
275 absl::Span<const NodeIndex> topological_order,
276 absl::Span<const NodeIndex> sources,
277 absl::Span<const NodeIndex> destinations,
278 const std::vector<double>* max_resources, int max_num_created_labels)
279 : graph_(graph),
280 arc_lengths_(arc_lengths),
281 arc_resources_(arc_resources),
282 max_resources_(max_resources),
283 sources_(sources),
284 destinations_(destinations),
285 num_resources_(max_resources->size()) {
286 CHECK(graph_ != nullptr);
287 CHECK(arc_lengths_ != nullptr);
288 CHECK(arc_resources_ != nullptr);
289 CHECK(!sources_.empty());
290 CHECK(!destinations_.empty());
291 CHECK(max_resources_ != nullptr);
292 CHECK(!max_resources_->empty())
293 << "max_resources cannot be empty. Use "
294 "ortools/graph/dag_shortest_path.h instead";
295 if (DEBUG_MODE) {
296 CHECK_EQ(arc_lengths->size(), graph->num_arcs());
297 CHECK_EQ(arc_resources->size(), max_resources->size());
298 for (absl::Span<const double> arcs_resource : *arc_resources) {
299 CHECK_EQ(arcs_resource.size(), graph->num_arcs());
300 for (const double arc_resource : arcs_resource) {
301 CHECK(arc_resource >= 0 &&
302 arc_resource != std::numeric_limits<double>::infinity() &&
303 !std::isnan(arc_resource))
304 << absl::StrFormat("resource cannot be negative nor +inf nor NaN");
305 }
306 }
307 for (const double arc_length : *arc_lengths) {
308 CHECK(arc_length != -std::numeric_limits<double>::infinity() &&
309 !std::isnan(arc_length))
310 << absl::StrFormat("length cannot be -inf nor NaN");
311 }
313 << "Invalid topological order";
314 for (const double max_resource : *max_resources) {
315 CHECK(max_resource >= 0 &&
316 max_resource != std::numeric_limits<double>::infinity() &&
317 !std::isnan(max_resource))
318 << absl::StrFormat(
319 "max_resource cannot be negative not +inf nor NaN");
320 }
321 std::vector<bool> is_source(graph->num_nodes(), false);
322 for (const NodeIndex source : sources) {
323 is_source[source] = true;
324 }
325 for (const NodeIndex destination : destinations) {
326 CHECK(!is_source[destination])
327 << "A node cannot be both a source and destination";
328 }
329 }
330
331 // Full graphs.
332 const GraphType* full_graph[2];
333 const std::vector<std::vector<double>>* full_arc_resources[2];
334 absl::Span<const NodeIndex> full_topological_order[2];
335 absl::Span<const NodeIndex> full_sources[2];
336 // Forward.
337 const int num_nodes = graph->num_nodes();
338 const int num_arcs = graph->num_arcs();
339 full_graph[FORWARD] = graph;
340 full_arc_resources[FORWARD] = arc_resources;
341 full_topological_order[FORWARD] = topological_order;
342 full_sources[FORWARD] = sources;
343 // Backward.
344 GraphType full_backward_graph(num_nodes, num_arcs);
345 for (ArcIndex arc_index = 0; arc_index < num_arcs; ++arc_index) {
346 full_backward_graph.AddArc(graph->Head(arc_index), graph->Tail(arc_index));
347 }
348 std::vector<ArcIndex> full_permutation;
349 full_backward_graph.Build(&full_permutation);
350 const std::vector<ArcIndex> full_inverse_arc_indices =
351 GetInversePermutation(full_permutation);
352 std::vector<std::vector<double>> backward_arc_resources(num_resources_);
353 for (int r = 0; r < num_resources_; ++r) {
354 backward_arc_resources[r] = (*arc_resources)[r];
355 util::Permute(full_permutation, &backward_arc_resources[r]);
356 }
357 std::vector<NodeIndex> full_backward_topological_order;
358 full_backward_topological_order.reserve(num_nodes);
359 for (int i = num_nodes - 1; i >= 0; --i) {
360 full_backward_topological_order.push_back(topological_order[i]);
361 }
362 full_graph[BACKWARD] = &full_backward_graph;
363 full_arc_resources[BACKWARD] = &backward_arc_resources;
364 full_topological_order[BACKWARD] = full_backward_topological_order;
365 full_sources[BACKWARD] = destinations;
366
367 // Get the minimum resources sources -> node and node -> destination for each
368 // node.
369 std::vector<std::vector<double>> full_min_arc_resources[2];
370 for (const Direction dir : {FORWARD, BACKWARD}) {
371 full_min_arc_resources[dir].reserve(num_resources_);
372 std::vector<double> full_arc_resource = full_arc_resources[dir]->front();
373 ShortestPathsOnDagWrapper<GraphType> shortest_paths_on_dag(
374 full_graph[dir], &full_arc_resource, full_topological_order[dir]);
375 for (int r = 0; r < num_resources_; ++r) {
376 full_arc_resource = (*(full_arc_resources[dir]))[r];
377 shortest_paths_on_dag.RunShortestPathOnDag(full_sources[dir]);
378 full_min_arc_resources[dir].push_back(shortest_paths_on_dag.LengthTo());
379 }
380 }
381
382 // Get reachable subgraph.
383 std::vector<bool> is_reachable(num_nodes, true);
384 std::vector<NodeIndex> sub_topological_order;
385 sub_topological_order.reserve(num_nodes);
386 for (const NodeIndex node_index : topological_order) {
387 for (int r = 0; r < num_resources_; ++r) {
388 if (full_min_arc_resources[FORWARD][r][node_index] +
389 full_min_arc_resources[BACKWARD][r][node_index] >
390 (*max_resources)[r]) {
391 is_reachable[node_index] = false;
392 break;
393 }
394 }
395 if (is_reachable[node_index]) {
396 sub_topological_order.push_back(node_index);
397 }
398 }
399 const int reachable_node_count = sub_topological_order.size();
400
401 // We split the number of labels evenly between each search (+1 for the
402 // additional source node).
403 max_num_created_labels_[BACKWARD] = max_num_created_labels / 2 + 1;
404 max_num_created_labels_[FORWARD] =
405 max_num_created_labels - max_num_created_labels / 2 + 1;
406
407 // Split sub-graphs and related information.
408 // The split is based on the number of paths. This is used as a simple proxy
409 // for the number of labels.
410 int mid_index = 0;
411 {
412 // We use double to avoid overflow. Note that this is an heuristic, so we
413 // don't care too much if we are not precise enough.
414 std::vector<double> path_count[2];
415 for (const Direction dir : {FORWARD, BACKWARD}) {
416 const GraphType& reverse_full_graph = *(full_graph[Reverse(dir)]);
417 path_count[dir].resize(num_nodes);
418 for (const NodeIndex source : full_sources[dir]) {
419 ++path_count[dir][source];
420 }
421 for (const NodeIndex to : full_topological_order[dir]) {
422 if (!is_reachable[to]) continue;
423 for (const ArcIndex arc : reverse_full_graph.OutgoingArcs(to)) {
424 const NodeIndex from = reverse_full_graph.Head(arc);
425 if (!is_reachable[from]) continue;
426 path_count[dir][to] += path_count[dir][from];
427 }
428 }
429 }
430 for (const NodeIndex node_index : sub_topological_order) {
431 if (path_count[FORWARD][node_index] > path_count[BACKWARD][node_index]) {
432 break;
433 }
434 ++mid_index;
435 }
436 if (mid_index == reachable_node_count) {
437 mid_index = reachable_node_count / 2;
438 }
439 }
440
441 for (const Direction dir : {FORWARD, BACKWARD}) {
442 absl::Span<const NodeIndex> const sub_nodes =
443 dir == FORWARD
444 ? absl::MakeSpan(sub_topological_order).subspan(0, mid_index)
445 : absl::MakeSpan(sub_topological_order)
446 .subspan(mid_index, reachable_node_count - mid_index);
447 sub_node_indices_[dir].assign(num_nodes, -1);
448 sub_min_arc_resources_[dir].resize(num_resources_);
449 for (int r = 0; r < num_resources_; ++r) {
450 sub_min_arc_resources_[dir][r].resize(sub_nodes.size());
451 }
452 for (NodeIndex i = 0; i < sub_nodes.size(); ++i) {
453 const NodeIndex sub_node_index =
454 dir == FORWARD ? i : sub_nodes.size() - 1 - i;
455 sub_node_indices_[dir][sub_nodes[i]] = sub_node_index;
456 for (int r = 0; r < num_resources_; ++r) {
457 sub_min_arc_resources_[dir][r][sub_node_index] =
458 full_min_arc_resources[Reverse(dir)][r][sub_nodes[i]];
459 }
460 }
461 // IMPORTANT: The sub-graph has an additional node linked to sources (resp.
462 // destinations) for the forward (resp. backward) direction. This additional
463 // node is indexed with the last index. All added arcs are given to have an
464 // arc index in the original graph of -1.
465 const int sub_arcs_count = num_arcs + full_sources[dir].size();
466 sub_reverse_graph_[dir] = GraphType(sub_nodes.size() + 1, sub_arcs_count);
467 sub_arc_resources_[dir].resize(num_resources_);
468 for (int r = 0; r < num_resources_; ++r) {
469 sub_arc_resources_[dir][r].reserve(sub_arcs_count);
470 }
471 sub_full_arc_indices_[dir].reserve(sub_arcs_count);
472 const GraphType& reverse_full_graph = *(full_graph[Reverse(dir)]);
473 for (ArcIndex arc_index = 0; arc_index < num_arcs; ++arc_index) {
474 const NodeIndex from =
475 sub_node_indices_[dir][reverse_full_graph.Tail(arc_index)];
476 const NodeIndex to =
477 sub_node_indices_[dir][reverse_full_graph.Head(arc_index)];
478 if (from == -1 || to == -1) {
479 continue;
480 }
481 sub_reverse_graph_[dir].AddArc(from, to);
482 ArcIndex sub_full_arc_index;
483 if (dir == FORWARD && !full_inverse_arc_indices.empty()) {
484 sub_full_arc_index = full_inverse_arc_indices[arc_index];
485 } else {
486 sub_full_arc_index = arc_index;
487 }
488 for (int r = 0; r < num_resources_; ++r) {
489 sub_arc_resources_[dir][r].push_back(
490 (*arc_resources_)[r][sub_full_arc_index]);
491 }
492 sub_full_arc_indices_[dir].push_back(sub_full_arc_index);
493 }
494 for (const NodeIndex source : full_sources[dir]) {
495 const NodeIndex sub_source = sub_node_indices_[dir][source];
496 if (sub_source == -1) {
497 continue;
498 }
499 sub_reverse_graph_[dir].AddArc(sub_source, sub_nodes.size());
500 for (int r = 0; r < num_resources_; ++r) {
501 sub_arc_resources_[dir][r].push_back(0.0);
502 }
503 sub_full_arc_indices_[dir].push_back(-1);
504 }
505 std::vector<ArcIndex> sub_permutation;
506 sub_reverse_graph_[dir].Build(&sub_permutation);
507 for (int r = 0; r < num_resources_; ++r) {
508 util::Permute(sub_permutation, &sub_arc_resources_[dir][r]);
509 }
510 util::Permute(sub_permutation, &sub_full_arc_indices_[dir]);
511 }
512
513 // Memory allocation is done here and only once in order to avoid
514 // reallocation at each call of `RunConstrainedShortestPathOnDag()` for
515 // better performance.
516 for (const Direction dir : {FORWARD, BACKWARD}) {
517 resources_from_sources_[dir].resize(num_resources_);
518 node_first_label_[dir].resize(sub_reverse_graph_[dir].size());
519 node_num_labels_[dir].resize(sub_reverse_graph_[dir].size());
520 }
521}
522
523template <class GraphType>
524#if __cplusplus >= 202002L
525 requires DagGraphType<GraphType>
526#endif
528 GraphType>::RunConstrainedShortestPathOnDag() {
529 // Assign lengths on sub-relevant graphs.
530 std::vector<double> sub_arc_lengths[2];
531 for (const Direction dir : {FORWARD, BACKWARD}) {
532 sub_arc_lengths[dir].reserve(sub_reverse_graph_[dir].num_arcs());
533 for (ArcIndex sub_arc_index = 0;
534 sub_arc_index < sub_reverse_graph_[dir].num_arcs(); ++sub_arc_index) {
535 const ArcIndex arc_index = sub_full_arc_indices_[dir][sub_arc_index];
536 if (arc_index == -1) {
537 sub_arc_lengths[dir].push_back(0.0);
538 continue;
539 }
540 sub_arc_lengths[dir].push_back((*arc_lengths_)[arc_index]);
541 }
542 }
543
544 {
545 ThreadPool search_threads(2);
546 search_threads.StartWorkers();
547 for (const Direction dir : {FORWARD, BACKWARD}) {
548 search_threads.Schedule([this, dir, &sub_arc_lengths]() {
549 RunHalfConstrainedShortestPathOnDag(
550 /*reverse_graph=*/sub_reverse_graph_[dir],
551 /*arc_lengths=*/sub_arc_lengths[dir],
552 /*arc_resources=*/sub_arc_resources_[dir],
553 /*min_arc_resources=*/sub_min_arc_resources_[dir],
554 /*max_resources=*/*max_resources_,
555 /*max_num_created_labels=*/max_num_created_labels_[dir],
556 /*lengths_from_sources=*/lengths_from_sources_[dir],
557 /*resources_from_sources=*/resources_from_sources_[dir],
558 /*incoming_arc_indices_from_sources=*/
559 incoming_arc_indices_from_sources_[dir],
560 /*incoming_label_indices_from_sources=*/
561 incoming_label_indices_from_sources_[dir],
562 /*first_label=*/node_first_label_[dir],
563 /*num_labels=*/node_num_labels_[dir]);
564 });
565 }
566 }
567
568 // Check destinations within relevant half sub-graphs.
569 LabelPair best_label_pair = {
570 .length = std::numeric_limits<double>::infinity(),
571 .label_index = {-1, -1}};
572 for (const Direction dir : {FORWARD, BACKWARD}) {
573 absl::Span<const NodeIndex> destinations =
574 dir == FORWARD ? destinations_ : sources_;
575 for (const NodeIndex dst : destinations) {
576 const NodeIndex sub_dst = sub_node_indices_[dir][dst];
577 if (sub_dst == -1) {
578 continue;
579 }
580 const int num_labels_dst = node_num_labels_[dir][sub_dst];
581 if (num_labels_dst == 0) {
582 continue;
583 }
584 const int first_label_dst = node_first_label_[dir][sub_dst];
585 for (int label_index = first_label_dst;
586 label_index < first_label_dst + num_labels_dst; ++label_index) {
587 const double length_dst = lengths_from_sources_[dir][label_index];
588 if (length_dst < best_label_pair.length) {
589 best_label_pair.length = length_dst;
590 best_label_pair.label_index[dir] = label_index;
591 }
592 }
593 }
594 }
595
596 const ArcIndex merging_arc_index = MergeHalfRuns(
597 /*graph=*/*graph_, /*arc_lengths=*/*arc_lengths_,
598 /*arc_resources=*/*arc_resources_,
599 /*max_resources=*/*max_resources_,
600 /*sub_node_indices=*/sub_node_indices_,
601 /*lengths_from_sources=*/lengths_from_sources_,
602 /*resources_from_sources=*/resources_from_sources_,
603 /*first_label=*/node_first_label_,
604 /*num_labels=*/node_num_labels_, /*best_label_pair=*/best_label_pair);
605
606 std::vector<ArcIndex> arc_path;
607 for (const Direction dir : {FORWARD, BACKWARD}) {
608 for (const ArcIndex sub_arc_index : ArcPathTo(
609 /*best_label_index=*/best_label_pair.label_index[dir],
610 /*incoming_arc_indices_from_sources=*/
611 incoming_arc_indices_from_sources_[dir],
612 /*incoming_label_indices_from_sources=*/
613 incoming_label_indices_from_sources_[dir])) {
614 const ArcIndex arc_index = sub_full_arc_indices_[dir][sub_arc_index];
615 if (arc_index == -1) {
616 break;
617 }
618 arc_path.push_back(arc_index);
619 }
620 if (dir == FORWARD && merging_arc_index != -1) {
621 absl::c_reverse(arc_path);
622 arc_path.push_back(merging_arc_index);
623 }
624 }
625
626 // Clear all labels from the next run.
627 for (const Direction dir : {FORWARD, BACKWARD}) {
628 lengths_from_sources_[dir].clear();
629 for (int r = 0; r < num_resources_; ++r) {
630 resources_from_sources_[dir][r].clear();
631 }
632 incoming_arc_indices_from_sources_[dir].clear();
633 incoming_label_indices_from_sources_[dir].clear();
634 }
635 return {.length = best_label_pair.length,
636 .arc_path = arc_path,
637 .node_path = NodePathImpliedBy(arc_path, *graph_)};
638}
639
640template <class GraphType>
641#if __cplusplus >= 202002L
642 requires DagGraphType<GraphType>
643#endif
644void ConstrainedShortestPathsOnDagWrapper<GraphType>::
645 RunHalfConstrainedShortestPathOnDag(
646 const GraphType& reverse_graph, absl::Span<const double> arc_lengths,
647 absl::Span<const std::vector<double>> arc_resources,
648 absl::Span<const std::vector<double>> min_arc_resources,
649 absl::Span<const double> max_resources,
650 const int max_num_created_labels,
651 std::vector<double>& lengths_from_sources,
652 std::vector<std::vector<double>>& resources_from_sources,
653 std::vector<ArcIndex>& incoming_arc_indices_from_sources,
654 std::vector<int>& incoming_label_indices_from_sources,
655 std::vector<int>& first_label, std::vector<int>& num_labels) {
656 // Initialize source node.
657 const NodeIndex source_node = reverse_graph.num_nodes() - 1;
658 first_label[source_node] = 0;
659 num_labels[source_node] = 1;
660 lengths_from_sources.push_back(0);
661 for (int r = 0; r < num_resources_; ++r) {
662 resources_from_sources[r].push_back(0);
663 }
664 incoming_arc_indices_from_sources.push_back(-1);
665 incoming_label_indices_from_sources.push_back(-1);
666
667 std::vector<double> lengths_to;
668 std::vector<std::vector<double>> resources_to(num_resources_);
669 std::vector<ArcIndex> incoming_arc_indices_to;
670 std::vector<int> incoming_label_indices_to;
671 std::vector<int> label_indices_to;
672 std::vector<double> resources(num_resources_);
673 for (NodeIndex to = 0; to < source_node; ++to) {
674 lengths_to.clear();
675 for (int r = 0; r < num_resources_; ++r) {
676 resources_to[r].clear();
677 }
678 incoming_arc_indices_to.clear();
679 incoming_label_indices_to.clear();
680 for (const ArcIndex reverse_arc_index : reverse_graph.OutgoingArcs(to)) {
681 const NodeIndex from = reverse_graph.Head(reverse_arc_index);
682 const double arc_length = arc_lengths[reverse_arc_index];
683 DCHECK(arc_length != -std::numeric_limits<double>::infinity());
684 if (arc_length == std::numeric_limits<double>::infinity()) {
685 continue;
686 }
687 for (int label_index = first_label[from];
688 label_index < first_label[from] + num_labels[from]; ++label_index) {
689 bool path_is_feasible = true;
690 for (int r = 0; r < num_resources_; ++r) {
691 DCHECK_GE(arc_resources[r][reverse_arc_index], 0.0);
692 resources[r] = resources_from_sources[r][label_index] +
693 arc_resources[r][reverse_arc_index];
694 if (resources[r] + min_arc_resources[r][to] > max_resources[r]) {
695 path_is_feasible = false;
696 break;
697 }
698 }
699 if (!path_is_feasible) {
700 continue;
701 }
702 lengths_to.push_back(lengths_from_sources[label_index] + arc_length);
703 for (int r = 0; r < num_resources_; ++r) {
704 resources_to[r].push_back(resources[r]);
705 }
706 incoming_arc_indices_to.push_back(reverse_arc_index);
707 incoming_label_indices_to.push_back(label_index);
708 }
709 }
710 // Sort labels lexicographically with lengths then resources.
711 label_indices_to.clear();
712 label_indices_to.reserve(lengths_to.size());
713 for (int i = 0; i < lengths_to.size(); ++i) {
714 label_indices_to.push_back(i);
715 }
716 absl::c_sort(label_indices_to, [&](const int i, const int j) {
717 if (lengths_to[i] < lengths_to[j]) return true;
718 if (lengths_to[i] > lengths_to[j]) return false;
719 for (int r = 0; r < num_resources_; ++r) {
720 if (resources_to[r][i] < resources_to[r][j]) return true;
721 if (resources_to[r][i] > resources_to[r][j]) return false;
722 }
723 return i < j;
724 });
725
726 first_label[to] = lengths_from_sources.size();
727 int& num_labels_to = num_labels[to];
728 // Reset the number of labels to zero otherwise it holds the previous run
729 // result.
730 num_labels_to = 0;
731 for (int i = 0; i < label_indices_to.size(); ++i) {
732 // Check if label "i" on node `to` is dominated by any other label.
733 const int label_i_index = label_indices_to[i];
734 bool label_i_is_dominated = false;
735 for (int j = 0; j < i - 1; ++j) {
736 const int label_j_index = label_indices_to[j];
737 if (lengths_to[label_i_index] <= lengths_to[label_j_index]) continue;
738 bool label_j_dominates_label_i = true;
739 for (int r = 0; r < num_resources_; ++r) {
740 if (resources_to[r][label_i_index] <=
741 resources_to[r][label_j_index]) {
742 label_j_dominates_label_i = false;
743 break;
744 }
745 }
746 if (label_j_dominates_label_i) {
747 label_i_is_dominated = true;
748 break;
749 }
750 }
751 if (label_i_is_dominated) continue;
752 lengths_from_sources.push_back(lengths_to[label_i_index]);
753 for (int r = 0; r < num_resources_; ++r) {
754 resources_from_sources[r].push_back(resources_to[r][label_i_index]);
755 }
756 incoming_arc_indices_from_sources.push_back(
757 incoming_arc_indices_to[label_i_index]);
758 incoming_label_indices_from_sources.push_back(
759 incoming_label_indices_to[label_i_index]);
760 ++num_labels_to;
761 if (lengths_from_sources.size() >= max_num_created_labels) {
762 return;
763 }
764 }
765 }
766}
767
768template <class GraphType>
769#if __cplusplus >= 202002L
770 requires DagGraphType<GraphType>
771#endif
772typename GraphType::ArcIndex
773ConstrainedShortestPathsOnDagWrapper<GraphType>::MergeHalfRuns(
774 const GraphType& graph, absl::Span<const double> arc_lengths,
775 absl::Span<const std::vector<double>> arc_resources,
776 absl::Span<const double> max_resources,
777 const std::vector<NodeIndex> sub_node_indices[2],
778 const std::vector<double> lengths_from_sources[2],
779 const std::vector<std::vector<double>> resources_from_sources[2],
780 const std::vector<int> first_label[2], const std::vector<int> num_labels[2],
781 LabelPair& best_label_pair) {
782 const std::vector<NodeIndex>& forward_sub_node_indices =
783 sub_node_indices[FORWARD];
784 absl::Span<const double> forward_lengths = lengths_from_sources[FORWARD];
785 const std::vector<std::vector<double>>& forward_resources =
786 resources_from_sources[FORWARD];
787 absl::Span<const int> forward_first_label = first_label[FORWARD];
788 absl::Span<const int> forward_num_labels = num_labels[FORWARD];
789 const std::vector<NodeIndex>& backward_sub_node_indices =
790 sub_node_indices[BACKWARD];
791 absl::Span<const double> backward_lengths = lengths_from_sources[BACKWARD];
792 const std::vector<std::vector<double>>& backward_resources =
793 resources_from_sources[BACKWARD];
794 absl::Span<const int> backward_first_label = first_label[BACKWARD];
795 absl::Span<const int> backward_num_labels = num_labels[BACKWARD];
796 ArcIndex merging_arc_index = -1;
797 for (ArcIndex arc_index = 0; arc_index < graph.num_arcs(); ++arc_index) {
798 const NodeIndex sub_from = forward_sub_node_indices[graph.Tail(arc_index)];
799 if (sub_from == -1) {
800 continue;
801 }
802 const NodeIndex sub_to = backward_sub_node_indices[graph.Head(arc_index)];
803 if (sub_to == -1) {
804 continue;
805 }
806 const int num_labels_from = forward_num_labels[sub_from];
807 if (num_labels_from == 0) {
808 continue;
809 }
810 const int num_labels_to = backward_num_labels[sub_to];
811 if (num_labels_to == 0) {
812 continue;
813 }
814 const double arc_length = arc_lengths[arc_index];
815 DCHECK(arc_length != -std::numeric_limits<double>::infinity());
816 if (arc_length == std::numeric_limits<double>::infinity()) {
817 continue;
818 }
819 const int first_label_from = forward_first_label[sub_from];
820 const int first_label_to = backward_first_label[sub_to];
821 for (int label_to_index = first_label_to;
822 label_to_index < first_label_to + num_labels_to; ++label_to_index) {
823 const double length_to = backward_lengths[label_to_index];
824 if (arc_length + length_to >= best_label_pair.length) {
825 continue;
826 }
827 for (int label_from_index = first_label_from;
828 label_from_index < first_label_from + num_labels_from;
829 ++label_from_index) {
830 const double length_from = forward_lengths[label_from_index];
831 if (length_from + arc_length + length_to >= best_label_pair.length) {
832 continue;
833 }
834 bool path_is_feasible = true;
835 for (int r = 0; r < num_resources_; ++r) {
836 DCHECK_GE(arc_resources[r][arc_index], 0.0);
837 if (forward_resources[r][label_from_index] +
838 arc_resources[r][arc_index] +
839 backward_resources[r][label_to_index] >
840 max_resources[r]) {
841 path_is_feasible = false;
842 break;
843 }
844 }
845 if (!path_is_feasible) {
846 continue;
847 }
848 best_label_pair.length = length_from + arc_length + length_to;
849 best_label_pair.label_index[FORWARD] = label_from_index;
850 best_label_pair.label_index[BACKWARD] = label_to_index;
851 merging_arc_index = arc_index;
852 }
853 }
854 }
855 return merging_arc_index;
856}
857
858template <class GraphType>
859#if __cplusplus >= 202002L
860 requires DagGraphType<GraphType>
861#endif
862std::vector<typename GraphType::ArcIndex>
863ConstrainedShortestPathsOnDagWrapper<GraphType>::ArcPathTo(
864 const int best_label_index,
865 absl::Span<const ArcIndex> incoming_arc_indices_from_sources,
866 absl::Span<const int> incoming_label_indices_from_sources) const {
867 int current_label_index = best_label_index;
868 std::vector<ArcIndex> arc_path;
869 for (int i = 0; i < graph_->num_nodes(); ++i) {
870 if (current_label_index == -1) {
871 break;
872 }
873 arc_path.push_back(incoming_arc_indices_from_sources[current_label_index]);
874 current_label_index =
875 incoming_label_indices_from_sources[current_label_index];
876 }
877 return arc_path;
878}
879
880template <typename GraphType>
881#if __cplusplus >= 202002L
882 requires DagGraphType<GraphType>
883#endif
884std::vector<typename GraphType::NodeIndex>
885ConstrainedShortestPathsOnDagWrapper<GraphType>::NodePathImpliedBy(
886 absl::Span<const ArcIndex> arc_path, const GraphType& graph) const {
887 if (arc_path.empty()) {
888 return {};
889 }
890 std::vector<NodeIndex> node_path;
891 node_path.reserve(arc_path.size() + 1);
892 for (const ArcIndex arc_index : arc_path) {
893 node_path.push_back(graph.Tail(arc_index));
894 }
895 node_path.push_back(graph.Head(arc_path.back()));
896 return node_path;
897}
898
899} // namespace operations_research
900
901#endif // OR_TOOLS_GRAPH_DAG_CONSTRAINED_SHORTEST_PATH_H_
IntegerValue size
ConstrainedShortestPathsOnDagWrapper(const GraphType *graph, const std::vector< double > *arc_lengths, const std::vector< std::vector< double > > *arc_resources, absl::Span< const NodeIndex > topological_order, absl::Span< const NodeIndex > sources, absl::Span< const NodeIndex > destinations, const std::vector< double > *max_resources, int max_num_created_labels=1e9)
double LengthTo(NodeIndex node) const
Returns the length of the shortest path from node's source to node.
void RunShortestPathOnDag(absl::Span< const NodeIndex > sources)
std::vector< NodeIndex > topological_order
std::vector< double > arc_lengths
GraphType graph
int arc
const bool DEBUG_MODE
Definition macros.h:24
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< typename GraphType::NodeIndex > NodePathImpliedBy(absl::Span< const typename GraphType::ArcIndex > arc_path, const GraphType &graph)
std::vector< int > GetInversePermutation(absl::Span< const int > permutation)
absl::Status TopologicalOrderIsValid(const GraphType &graph, absl::Span< const typename GraphType::NodeIndex > topological_order)
PathWithLength ConstrainedShortestPathsOnDag(const int num_nodes, absl::Span< const ArcWithLengthAndResources > arcs_with_length_and_resources, int source, int destination, const std::vector< double > &max_resources)
void Permute(const IntVector &permutation, Array *array_to_permute)
Definition graph.h:758
trees with all degrees equal to