Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
dag_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_SHORTEST_PATH_H_
15#define OR_TOOLS_GRAPH_DAG_SHORTEST_PATH_H_
16
17#include <cmath>
18#if __cplusplus >= 202002L
19#include <concepts>
20#endif
21#include <functional>
22#include <limits>
23#include <vector>
24
25#include "absl/algorithm/container.h"
26#include "absl/log/check.h"
27#include "absl/status/status.h"
28#include "absl/strings/str_format.h"
29#include "absl/types/span.h"
31
32namespace operations_research {
33// TODO(b/332475231): extend to non-floating lengths.
34// TODO(b/332476147): extend to allow for length functor.
35
36// This library provides a few APIs to compute the shortest path on a given
37// directed acyclic graph (DAG).
38//
39// In the DAG, multiple arcs between the same pair of nodes is allowed. However,
40// self-loop arcs are not allowed.
41//
42// Note that we use the length formalism here, but the arc lengths can represent
43// any numeric physical quantity. A shortest path will just be a path minimizing
44// this quantity where the length of a path is the sum of the length of its
45// arcs. An arc length can be negative, or +inf (indicating that it should not
46// be used). An arc length cannot be -inf or nan.
47
48// -----------------------------------------------------------------------------
49// Basic API.
50// -----------------------------------------------------------------------------
51
52// `from` and `to` should both be in [0, num_nodes).
53// If the length is +inf, then the arc should not be used.
54struct ArcWithLength {
55 int from = 0;
56 int to = 0;
57 double length = 0.0;
58};
60struct PathWithLength {
61 double length = 0.0;
62 // The returned arc indices points into the `arcs_with_length` passed to the
63 // function below.
64 std::vector<int> arc_path;
65 std::vector<int> node_path; // includes the source node.
66};
68// Returns {+inf, {}, {}} if there is no path of finite length from the source
69// to the destination. Dies if `arcs_with_length` has a cycle.
71 int num_nodes, absl::Span<const ArcWithLength> arcs_with_length, int source,
72 int destination);
73
74// Returns the k-shortest paths by increasing length. Returns fewer than k paths
75// if there are fewer than k paths from the source to the destination. Returns
76// {{+inf, {}, {}}} if there is no path of finite length from the source to the
77// destination. Dies if `arcs_with_length` has a cycle.
78std::vector<PathWithLength> KShortestPathsOnDag(
79 int num_nodes, absl::Span<const ArcWithLength> arcs_with_length, int source,
80 int destination, int path_count);
81
82// -----------------------------------------------------------------------------
83// Advanced API.
84// -----------------------------------------------------------------------------
85// This concept only enforces the standard graph API needed for all algorithms
86// on DAGs. One could add the requirement of being a DAG wihtin this concept
87// (which is done before running the algorithm).
88#if __cplusplus >= 202002L
89template <class GraphType>
90concept DagGraphType = requires(GraphType graph) {
91 { typename GraphType::NodeIndex{} };
92 { typename GraphType::ArcIndex{} };
93 { graph.num_nodes() } -> std::same_as<typename GraphType::NodeIndex>;
94 { graph.num_arcs() } -> std::same_as<typename GraphType::ArcIndex>;
95 { graph.OutgoingArcs(typename GraphType::NodeIndex{}) };
96 {
97 graph.Tail(typename GraphType::ArcIndex{})
98 } -> std::same_as<typename GraphType::NodeIndex>;
99 {
100 graph.Head(typename GraphType::ArcIndex{})
101 } -> std::same_as<typename GraphType::NodeIndex>;
102 { graph.Build() };
103};
104#endif
105
106// A wrapper that holds the memory needed to run many shortest path computations
107// efficiently on the given DAG. One call of `RunShortestPathOnDag()` has time
108// complexity O(|E| + |V|) and space complexity O(|V|).
109// `GraphType` can use any of the interfaces defined in `util/graph/graph.h`.
110template <class GraphType>
111#if __cplusplus >= 202002L
112 requires DagGraphType<GraphType>
113#endif
114class ShortestPathsOnDagWrapper {
115 public:
116 using NodeIndex = typename GraphType::NodeIndex;
117 using ArcIndex = typename GraphType::ArcIndex;
119 // IMPORTANT: All arguments must outlive the class.
120 //
121 // The vector of `arc_lengths` *must* be of size `graph.num_arcs()` and
122 // indexed the same way as in `graph`.
123 //
124 // You *must* provide a topological order. You can use
125 // `util::graph::FastTopologicalSort(graph)` to compute one if you don't
126 // already have one. An invalid topological order results in an upper bound
127 // for all shortest path computations. For maximum performance, you can
128 // further reindex the nodes under the topological order so that the memory
129 // access pattern is generally forward instead of random. For example, if the
130 // topological order for a graph with 4 nodes is [2,1,0,3], you can re-label
131 // the nodes 2, 1, and 0 to 0, 1, and 2 (and updates arcs accordingly).
132 //
133 // Validity of arcs and topological order are CHECKed if compiled in DEBUG
134 // mode.
135 //
136 // SUBTLE: You can modify the graph, the arc lengths or the topological order
137 // between calls to the `RunShortestPathOnDag()` function. That's fine. Doing
138 // so will obviously invalidate the result API of the last shortest path run,
139 // which could return an upper bound, junk, or crash.
140 ShortestPathsOnDagWrapper(const GraphType* graph,
141 const std::vector<double>* arc_lengths,
142 absl::Span<const NodeIndex> topological_order);
143
144 // Computes the shortest path to all reachable nodes from the given sources.
145 // This must be called before any of the query functions below.
146 void RunShortestPathOnDag(absl::Span<const NodeIndex> sources);
147
148 // Returns true if `node` is reachable from at least one source, i.e., the
149 // length from at least one source is finite.
150 bool IsReachable(NodeIndex node) const;
151 const std::vector<NodeIndex>& reached_nodes() const { return reached_nodes_; }
152
153 // Returns the length of the shortest path from `node`'s source to `node`.
154 double LengthTo(NodeIndex node) const { return length_from_sources_[node]; }
155 std::vector<double> LengthTo() const { return length_from_sources_; }
156
157 // Returns the list of all the arcs in the shortest path from `node`'s
158 // source to `node`. CHECKs if the node is reachable.
159 std::vector<ArcIndex> ArcPathTo(NodeIndex node) const;
160
161 // Returns the list of all the nodes in the shortest path from `node`'s
162 // source to `node` (including the source). CHECKs if the node is reachable.
163 std::vector<NodeIndex> NodePathTo(NodeIndex node) const;
164
165 // Accessors to the underlying graph and arc lengths.
166 const GraphType& graph() const { return *graph_; }
167 const std::vector<double>& arc_lengths() const { return *arc_lengths_; }
168
169 private:
170 static constexpr double kInf = std::numeric_limits<double>::infinity();
171 const GraphType* const graph_;
172 const std::vector<double>* const arc_lengths_;
173 absl::Span<const NodeIndex> const topological_order_;
174
175 // Data about the last call of the RunShortestPathOnDag() function.
176 std::vector<double> length_from_sources_;
177 std::vector<ArcIndex> incoming_shortest_path_arc_;
178 std::vector<NodeIndex> reached_nodes_;
179};
180
181// A wrapper that holds the memory needed to run many k-shortest paths
182// computations efficiently on the given DAG. One call of
183// `RunKShortestPathOnDag()` has time complexity O(|E| + k|V|log(d)) where d is
184// the mean degree of the graph and space complexity O(k|V|).
185// `GraphType` can use any of the interfaces defined in `util/graph/graph.h`.
186// IMPORTANT: Only use if `path_count > 1` (k > 1) otherwise use
187// `ShortestPathsOnDagWrapper`.
188template <class GraphType>
189#if __cplusplus >= 202002L
190 requires DagGraphType<GraphType>
191#endif
193 public:
194 using NodeIndex = typename GraphType::NodeIndex;
195 using ArcIndex = typename GraphType::ArcIndex;
197 // IMPORTANT: All arguments must outlive the class.
198 //
199 // The vector of `arc_lengths` *must* be of size `graph.num_arcs()` and
200 // indexed the same way as in `graph`.
201 //
202 // You *must* provide a topological order. You can use
203 // `util::graph::FastTopologicalSort(graph)` to compute one if you don't
204 // already have one. An invalid topological order results in an upper bound
205 // for all shortest path computations. For maximum performance, you can
206 // further reindex the nodes under the topological order so that the memory
207 // access pattern is generally forward instead of random. For example, if the
208 // topological order for a graph with 4 nodes is [2,1,0,3], you can re-label
209 // the nodes 2, 1, and 0 to 0, 1, and 2 (and updates arcs accordingly).
210 //
211 // Validity of arcs and topological order are CHECKed if compiled in DEBUG
212 // mode.
213 //
214 // SUBTLE: You can modify the graph, the arc lengths or the topological order
215 // between calls to the `RunKShortestPathOnDag()` function. That's fine. Doing
216 // so will obviously invalidate the result API of the last shortest path run,
217 // which could return an upper bound, junk, or crash.
218 KShortestPathsOnDagWrapper(const GraphType* graph,
219 const std::vector<double>* arc_lengths,
220 absl::Span<const NodeIndex> topological_order,
221 int path_count);
222
223 // Computes the shortest path to all reachable nodes from the given sources.
224 // This must be called before any of the query functions below.
225 void RunKShortestPathOnDag(absl::Span<const NodeIndex> sources);
226
227 // Returns true if `node` is reachable from at least one source, i.e., the
228 // length of the shortest path from at least one source is finite.
229 bool IsReachable(NodeIndex node) const;
230 const std::vector<NodeIndex>& reached_nodes() const { return reached_nodes_; }
231
232 // Returns the lengths of the k-shortest paths from `node`'s source to `node`
233 // in increasing order. If there are less than k paths, return all path
234 // lengths.
235 std::vector<double> LengthsTo(NodeIndex node) const;
236
237 // Returns the list of all the arcs of the k-shortest paths from `node`'s
238 // source to `node`.
239 std::vector<std::vector<ArcIndex>> ArcPathsTo(NodeIndex node) const;
240
241 // Returns the list of all the nodes of the k-shortest paths from `node`'s
242 // source to `node` (including the source). CHECKs if the node is reachable.
243 std::vector<std::vector<NodeIndex>> NodePathsTo(NodeIndex node) const;
244
245 // Accessors to the underlying graph and arc lengths.
246 const GraphType& graph() const { return *graph_; }
247 const std::vector<double>& arc_lengths() const { return *arc_lengths_; }
248 int path_count() const { return path_count_; }
249
250 private:
251 static constexpr double kInf = std::numeric_limits<double>::infinity();
253 const GraphType* const graph_;
254 const std::vector<double>* const arc_lengths_;
255 absl::Span<const NodeIndex> const topological_order_;
256 const int path_count_;
257
258 GraphType reverse_graph_;
259 // Maps reverse arc indices to indices in the original graph.
260 std::vector<ArcIndex> arc_indices_;
261
262 // Data about the last call of the `RunKShortestPathOnDag()` function. The
263 // first dimension is the index of the path (1st being the shortest). The
264 // second dimension are nodes.
265 std::vector<std::vector<double>> lengths_from_sources_;
266 std::vector<std::vector<ArcIndex>> incoming_shortest_paths_arc_;
267 std::vector<std::vector<int>> incoming_shortest_paths_index_;
268 std::vector<bool> is_source_;
269 std::vector<NodeIndex> reached_nodes_;
270};
271
272template <class GraphType>
273#if __cplusplus >= 202002L
274 requires DagGraphType<GraphType>
275#endif
276absl::Status TopologicalOrderIsValid(
277 const GraphType& graph,
278 absl::Span<const typename GraphType::NodeIndex> topological_order);
279
280// -----------------------------------------------------------------------------
281// Implementations.
282// -----------------------------------------------------------------------------
283// TODO(b/332475804): If `ArcPathTo` and/or `NodePathTo` functions become
284// bottlenecks:
285// (1) have the class preallocate a buffer of size `num_nodes`
286// (2) assign into an index rather than with push_back
287// (3) return by absl::Span (or return a copy) with known size.
288template <typename GraphType>
289#if __cplusplus >= 202002L
290 requires DagGraphType<GraphType>
291#endif
292std::vector<typename GraphType::NodeIndex> NodePathImpliedBy(
293 absl::Span<const typename GraphType::ArcIndex> arc_path,
294 const GraphType& graph) {
295 CHECK(!arc_path.empty());
296 std::vector<typename GraphType::NodeIndex> node_path;
297 node_path.reserve(arc_path.size() + 1);
298 for (const typename GraphType::ArcIndex arc_index : arc_path) {
299 node_path.push_back(graph.Tail(arc_index));
300 }
301 node_path.push_back(graph.Head(arc_path.back()));
302 return node_path;
303}
304
305template <class GraphType>
306#if __cplusplus >= 202002L
307 requires DagGraphType<GraphType>
308#endif
309void CheckNodeIsValid(typename GraphType::NodeIndex node,
310 const GraphType& graph) {
311 CHECK_GE(node, 0) << "Node must be nonnegative. Input value: " << node;
312 CHECK_LT(node, graph.num_nodes())
313 << "Node must be a valid node. Input value: " << node
314 << ". Number of nodes in the input graph: " << graph.num_nodes();
316
317template <class GraphType>
318#if __cplusplus >= 202002L
319 requires DagGraphType<GraphType>
320#endif
321absl::Status TopologicalOrderIsValid(
322 const GraphType& graph,
323 absl::Span<const typename GraphType::NodeIndex> topological_order) {
324 using NodeIndex = typename GraphType::NodeIndex;
325 using ArcIndex = typename GraphType::ArcIndex;
326 const NodeIndex num_nodes = graph.num_nodes();
327 if (topological_order.size() != num_nodes) {
328 return absl::InvalidArgumentError(absl::StrFormat(
329 "topological_order.size() = %i, != graph.num_nodes() = %i",
330 topological_order.size(), num_nodes));
331 }
332 std::vector<NodeIndex> inverse_topology(num_nodes, -1);
333 for (NodeIndex node = 0; node < topological_order.size(); ++node) {
334 if (inverse_topology[topological_order[node]] >= 0) {
335 return absl::InvalidArgumentError(
336 absl::StrFormat("node % i appears twice in topological order",
337 topological_order[node]));
338 }
339 inverse_topology[topological_order[node]] = node;
340 }
341 for (NodeIndex tail = 0; tail < num_nodes; ++tail) {
342 for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
343 const NodeIndex head = graph.Head(arc);
344 if (inverse_topology[tail] >= inverse_topology[head]) {
345 return absl::InvalidArgumentError(absl::StrFormat(
346 "arc (%i, %i) is inconsistent with topological order", tail, head));
347 }
348 }
349 }
350 return absl::OkStatus();
351}
352
353// -----------------------------------------------------------------------------
354// ShortestPathsOnDagWrapper implementation.
355// -----------------------------------------------------------------------------
356template <class GraphType>
357#if __cplusplus >= 202002L
358 requires DagGraphType<GraphType>
359#endif
361 const GraphType* graph, const std::vector<double>* arc_lengths,
362 absl::Span<const NodeIndex> topological_order)
363 : graph_(graph),
364 arc_lengths_(arc_lengths),
365 topological_order_(topological_order) {
366 CHECK(graph_ != nullptr);
367 CHECK(arc_lengths_ != nullptr);
368 CHECK_GT(graph_->num_nodes(), 0) << "The graph is empty: it has no nodes";
369 CHECK_GT(graph_->num_arcs(), 0) << "The graph is empty: it has no arcs";
370#ifndef NDEBUG
371 CHECK_EQ(arc_lengths_->size(), graph_->num_arcs());
372 for (const double arc_length : *arc_lengths_) {
373 CHECK(arc_length != -kInf && !std::isnan(arc_length))
374 << absl::StrFormat("length cannot be -inf nor NaN");
375 }
376 CHECK_OK(TopologicalOrderIsValid(*graph_, topological_order_))
377 << "Invalid topological order";
378#endif
379
380 // Memory allocation is done here and only once in order to avoid reallocation
381 // at each call of `RunShortestPathOnDag()` for better performance.
382 length_from_sources_.resize(graph_->num_nodes(), kInf);
383 incoming_shortest_path_arc_.resize(graph_->num_nodes(), -1);
384 reached_nodes_.reserve(graph_->num_nodes());
385}
386
387template <class GraphType>
388#if __cplusplus >= 202002L
389 requires DagGraphType<GraphType>
390#endif
391void ShortestPathsOnDagWrapper<GraphType>::RunShortestPathOnDag(
392 absl::Span<const NodeIndex> sources) {
393 // Caching the vector addresses allow to not fetch it on each access.
394 const absl::Span<double> length_from_sources =
395 absl::MakeSpan(length_from_sources_);
396 const absl::Span<const double> arc_lengths = *arc_lengths_;
397
398 // Avoid reassigning `incoming_shortest_path_arc_` at every call for better
399 // performance, so it only makes sense for nodes that are reachable from at
400 // least one source, the other ones will contain junk.
401 for (const NodeIndex node : reached_nodes_) {
402 length_from_sources[node] = kInf;
403 }
404 DCHECK(std::all_of(length_from_sources.begin(), length_from_sources.end(),
405 [](double l) { return l == kInf; }));
406 reached_nodes_.clear();
407
408 for (const NodeIndex source : sources) {
409 CheckNodeIsValid(source, *graph_);
410 length_from_sources[source] = 0.0;
411 }
412
413 for (const NodeIndex tail : topological_order_) {
414 const double length_to_tail = length_from_sources[tail];
415 // Stop exploring a node as soon as its length to all sources is +inf.
416 if (length_to_tail == kInf) {
417 continue;
418 }
419 reached_nodes_.push_back(tail);
420 for (const ArcIndex arc : graph_->OutgoingArcs(tail)) {
421 const NodeIndex head = graph_->Head(arc);
422 DCHECK(arc_lengths[arc] != -kInf);
423 const double length_to_head = arc_lengths[arc] + length_to_tail;
424 if (length_to_head < length_from_sources[head]) {
425 length_from_sources[head] = length_to_head;
426 incoming_shortest_path_arc_[head] = arc;
427 }
428 }
429 }
430}
431
432template <class GraphType>
433#if __cplusplus >= 202002L
434 requires DagGraphType<GraphType>
435#endif
436bool ShortestPathsOnDagWrapper<GraphType>::IsReachable(NodeIndex node) const {
437 CheckNodeIsValid(node, *graph_);
438 return length_from_sources_[node] < kInf;
439}
440
441template <class GraphType>
442#if __cplusplus >= 202002L
443 requires DagGraphType<GraphType>
444#endif
445std::vector<typename GraphType::ArcIndex>
447 CHECK(IsReachable(node));
448 std::vector<ArcIndex> arc_path;
449 NodeIndex current_node = node;
450 for (int i = 0; i < graph_->num_nodes(); ++i) {
451 ArcIndex current_arc = incoming_shortest_path_arc_[current_node];
452 if (current_arc == -1) {
453 break;
455 arc_path.push_back(current_arc);
456 current_node = graph_->Tail(current_arc);
457 }
458 absl::c_reverse(arc_path);
459 return arc_path;
460}
461
462template <class GraphType>
463#if __cplusplus >= 202002L
464 requires DagGraphType<GraphType>
465#endif
466std::vector<typename GraphType::NodeIndex>
467ShortestPathsOnDagWrapper<GraphType>::NodePathTo(NodeIndex node) const {
468 const std::vector<typename GraphType::ArcIndex> arc_path = ArcPathTo(node);
469 if (arc_path.empty()) {
470 return {node};
471 }
472 return NodePathImpliedBy(ArcPathTo(node), *graph_);
473}
474
475// -----------------------------------------------------------------------------
476// KShortestPathsOnDagWrapper implementation.
477// -----------------------------------------------------------------------------
478template <class GraphType>
479#if __cplusplus >= 202002L
480 requires DagGraphType<GraphType>
481#endif
483 const GraphType* graph, const std::vector<double>* arc_lengths,
484 absl::Span<const NodeIndex> topological_order, const int path_count)
485 : graph_(graph),
486 arc_lengths_(arc_lengths),
487 topological_order_(topological_order),
488 path_count_(path_count) {
489 CHECK(graph_ != nullptr);
490 CHECK(arc_lengths_ != nullptr);
491 CHECK_GT(graph_->num_nodes(), 0) << "The graph is empty: it has no nodes";
492 CHECK_GT(graph_->num_arcs(), 0) << "The graph is empty: it has no arcs";
493 CHECK_GT(path_count_, 0) << "path_count must be greater than 0";
494#ifndef NDEBUG
495 CHECK_EQ(arc_lengths_->size(), graph_->num_arcs());
496 for (const double arc_length : *arc_lengths_) {
497 CHECK(arc_length != -kInf && !std::isnan(arc_length))
498 << absl::StrFormat("length cannot be -inf nor NaN");
499 }
500 CHECK_OK(TopologicalOrderIsValid(*graph_, topological_order_))
501 << "Invalid topological order";
502#endif
503
504 // TODO(b/332475713): Optimize if reverse graph is already provided in
505 // `GraphType`.
506 const int num_arcs = graph_->num_arcs();
507 reverse_graph_ = GraphType(graph_->num_nodes(), num_arcs);
508 for (ArcIndex arc_index = 0; arc_index < num_arcs; ++arc_index) {
509 reverse_graph_.AddArc(graph->Head(arc_index), graph->Tail(arc_index));
510 }
511 std::vector<ArcIndex> permutation;
512 reverse_graph_.Build(&permutation);
513 arc_indices_.resize(permutation.size());
514 if (!permutation.empty()) {
515 for (int i = 0; i < permutation.size(); ++i) {
516 arc_indices_[permutation[i]] = i;
517 }
518 }
519
520 // Memory allocation is done here and only once in order to avoid reallocation
521 // at each call of `RunKShortestPathOnDag()` for better performance.
522 lengths_from_sources_.resize(path_count_);
523 incoming_shortest_paths_arc_.resize(path_count_);
524 incoming_shortest_paths_index_.resize(path_count_);
525 for (int k = 0; k < path_count_; ++k) {
526 lengths_from_sources_[k].resize(graph_->num_nodes(), kInf);
527 incoming_shortest_paths_arc_[k].resize(graph_->num_nodes(), -1);
528 incoming_shortest_paths_index_[k].resize(graph_->num_nodes(), -1);
529 }
530 is_source_.resize(graph_->num_nodes(), false);
531 reached_nodes_.reserve(graph_->num_nodes());
532}
533
534template <class GraphType>
535#if __cplusplus >= 202002L
536 requires DagGraphType<GraphType>
537#endif
539 absl::Span<const NodeIndex> sources) {
540 // Caching the vector addresses allow to not fetch it on each access.
541 const absl::Span<const double> arc_lengths = *arc_lengths_;
542 const absl::Span<const ArcIndex> arc_indices = arc_indices_;
543
544 // Avoid reassigning `incoming_shortest_path_arc_` at every call for better
545 // performance, so it only makes sense for nodes that are reachable from at
546 // least one source, the other ones will contain junk.
547
548 for (const NodeIndex node : reached_nodes_) {
549 is_source_[node] = false;
550 for (int k = 0; k < path_count_; ++k) {
551 lengths_from_sources_[k][node] = kInf;
552 }
553 }
554 reached_nodes_.clear();
555#ifndef NDEBUG
556 for (int k = 0; k < path_count_; ++k) {
557 CHECK(std::all_of(lengths_from_sources_[k].begin(),
558 lengths_from_sources_[k].end(),
559 [](double l) { return l == kInf; }));
560 }
561#endif
562
563 for (const NodeIndex source : sources) {
564 CheckNodeIsValid(source, *graph_);
565 is_source_[source] = true;
566 }
567
568 struct IncomingArcPath {
569 double path_length = 0.0;
570 ArcIndex arc_index = 0;
571 double arc_length = 0.0;
572 NodeIndex from = 0;
573 int path_index = 0;
574
575 bool operator<(const IncomingArcPath& other) const {
576 return std::tie(path_length, from) <
577 std::tie(other.path_length, other.from);
578 }
579 bool operator>(const IncomingArcPath& other) const { return other < *this; }
580 };
581 std::vector<IncomingArcPath> min_heap;
582 auto comp = std::greater<IncomingArcPath>();
583 for (const NodeIndex to : topological_order_) {
584 min_heap.clear();
585 if (is_source_[to]) {
586 min_heap.push_back({.arc_index = -1});
587 }
588 for (const ArcIndex reverse_arc_index : reverse_graph_.OutgoingArcs(to)) {
589 const ArcIndex arc_index = arc_indices.empty()
590 ? reverse_arc_index
591 : arc_indices[reverse_arc_index];
592 const NodeIndex from = graph_->Tail(arc_index);
593 const double arc_length = arc_lengths[arc_index];
594 DCHECK(arc_length != -kInf);
595 const double path_length =
596 lengths_from_sources_.front()[from] + arc_length;
597 if (path_length == kInf) {
598 continue;
599 }
600 min_heap.push_back({.path_length = path_length,
601 .arc_index = arc_index,
602 .arc_length = arc_length,
603 .from = from});
604 std::push_heap(min_heap.begin(), min_heap.end(), comp);
605 }
606 if (min_heap.empty()) {
607 continue;
608 }
609 reached_nodes_.push_back(to);
610 for (int k = 0; k < path_count_; ++k) {
611 std::pop_heap(min_heap.begin(), min_heap.end(), comp);
612 IncomingArcPath& incoming_arc_path = min_heap.back();
613 lengths_from_sources_[k][to] = incoming_arc_path.path_length;
614 incoming_shortest_paths_arc_[k][to] = incoming_arc_path.arc_index;
615 incoming_shortest_paths_index_[k][to] = incoming_arc_path.path_index;
616 if (incoming_arc_path.arc_index != -1 &&
617 incoming_arc_path.path_index < path_count_ - 1 &&
618 lengths_from_sources_[incoming_arc_path.path_index + 1]
619 [incoming_arc_path.from] < kInf) {
620 ++incoming_arc_path.path_index;
621 incoming_arc_path.path_length =
622 lengths_from_sources_[incoming_arc_path.path_index]
623 [incoming_arc_path.from] +
624 incoming_arc_path.arc_length;
625 std::push_heap(min_heap.begin(), min_heap.end(), comp);
626 } else {
627 min_heap.pop_back();
628 if (min_heap.empty()) {
629 break;
630 }
631 }
632 }
633 }
634}
635
636template <class GraphType>
637#if __cplusplus >= 202002L
638 requires DagGraphType<GraphType>
639#endif
641 CheckNodeIsValid(node, *graph_);
642 return lengths_from_sources_.front()[node] < kInf;
643}
644
645template <class GraphType>
646#if __cplusplus >= 202002L
647 requires DagGraphType<GraphType>
648#endif
650 NodeIndex node) const {
651 std::vector<double> lengths_to;
652 lengths_to.reserve(path_count_);
653 for (int k = 0; k < path_count_; ++k) {
654 const double length_to = lengths_from_sources_[k][node];
655 if (length_to == kInf) {
656 break;
657 }
658 lengths_to.push_back(length_to);
660 return lengths_to;
661}
662
663template <class GraphType>
664#if __cplusplus >= 202002L
665 requires DagGraphType<GraphType>
666#endif
667std::vector<std::vector<typename GraphType::ArcIndex>>
669 std::vector<std::vector<ArcIndex>> arc_paths;
670 arc_paths.reserve(path_count_);
671 for (int k = 0; k < path_count_; ++k) {
672 if (lengths_from_sources_[k][node] == kInf) {
673 break;
674 }
675 std::vector<ArcIndex> arc_path;
676 int current_path_index = k;
677 NodeIndex current_node = node;
678 for (int i = 0; i < graph_->num_nodes(); ++i) {
679 ArcIndex current_arc =
680 incoming_shortest_paths_arc_[current_path_index][current_node];
681 if (current_arc == -1) {
682 break;
683 }
684 arc_path.push_back(current_arc);
685 current_path_index =
686 incoming_shortest_paths_index_[current_path_index][current_node];
687 current_node = graph_->Tail(current_arc);
688 }
689 absl::c_reverse(arc_path);
690 arc_paths.push_back(arc_path);
691 }
692 return arc_paths;
693}
694
695template <class GraphType>
696#if __cplusplus >= 202002L
697 requires DagGraphType<GraphType>
698#endif
699std::vector<std::vector<typename GraphType::NodeIndex>>
701 const std::vector<std::vector<ArcIndex>> arc_paths = ArcPathsTo(node);
702 std::vector<std::vector<NodeIndex>> node_paths(arc_paths.size());
703 for (int k = 0; k < arc_paths.size(); ++k) {
704 if (arc_paths[k].empty()) {
705 node_paths[k] = {node};
706 } else {
707 node_paths[k] = NodePathImpliedBy(arc_paths[k], *graph_);
708 }
709 }
710 return node_paths;
711}
712
713} // namespace operations_research
714#endif // OR_TOOLS_GRAPH_DAG_SHORTEST_PATH_H_
KShortestPathsOnDagWrapper(const GraphType *graph, const std::vector< double > *arc_lengths, absl::Span< const NodeIndex > topological_order, int path_count)
const GraphType & graph() const
Accessors to the underlying graph and arc lengths.
void RunKShortestPathOnDag(absl::Span< const NodeIndex > sources)
std::vector< std::vector< ArcIndex > > ArcPathsTo(NodeIndex node) const
std::vector< std::vector< NodeIndex > > NodePathsTo(NodeIndex node) const
const std::vector< NodeIndex > & reached_nodes() const
const std::vector< double > & arc_lengths() const
std::vector< double > LengthsTo(NodeIndex node) const
ShortestPathsOnDagWrapper(const GraphType *graph, const std::vector< double > *arc_lengths, absl::Span< const NodeIndex > topological_order)
const GraphType & graph() const
Accessors to the underlying graph and arc lengths.
std::vector< ArcIndex > ArcPathTo(NodeIndex node) const
void RunShortestPathOnDag(absl::Span< const NodeIndex > sources)
const std::vector< double > & arc_lengths() const
std::vector< NodeIndex > NodePathTo(NodeIndex node) const
const std::vector< NodeIndex > & reached_nodes() const
std::vector< NodeIndex > topological_order
std::vector< double > arc_lengths
GraphType graph
int arc
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< PathWithLength > KShortestPathsOnDag(const int num_nodes, absl::Span< const ArcWithLength > arcs_with_length, const int source, const int destination, const int path_count)
void CheckNodeIsValid(typename GraphType::NodeIndex node, const GraphType &graph)
std::vector< typename GraphType::NodeIndex > NodePathImpliedBy(absl::Span< const typename GraphType::ArcIndex > arc_path, const GraphType &graph)
absl::Status TopologicalOrderIsValid(const GraphType &graph, absl::Span< const typename GraphType::NodeIndex > topological_order)
PathWithLength ShortestPathsOnDag(const int num_nodes, absl::Span< const ArcWithLength > arcs_with_length, const int source, const int destination)
trees with all degrees equal to
int head
int tail
std::optional< int64_t > end