Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
dag_shortest_path.cc
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
15
16#include <limits>
17#include <utility>
18#include <vector>
19
20#include "absl/log/check.h"
21#include "absl/types/span.h"
22#include "ortools/graph/graph.h"
24
25namespace operations_research {
26
27namespace {
28
29using GraphType = util::StaticGraph<>;
30using NodeIndex = GraphType::NodeIndex;
31using ArcIndex = GraphType::ArcIndex;
32
33struct ShortestPathOnDagProblem {
34 GraphType graph;
35 std::vector<double> arc_lengths;
36 std::vector<ArcIndex> original_arc_indices;
37 std::vector<NodeIndex> topological_order;
38};
39
40ShortestPathOnDagProblem ReadProblem(
41 const int num_nodes, absl::Span<const ArcWithLength> arcs_with_length) {
42 GraphType graph(num_nodes, arcs_with_length.size());
43 std::vector<double> arc_lengths;
44 arc_lengths.reserve(arcs_with_length.size());
45 for (const auto& arc : arcs_with_length) {
46 graph.AddArc(arc.from, arc.to);
47 arc_lengths.push_back(arc.length);
48 }
49 std::vector<ArcIndex> permutation;
50 graph.Build(&permutation);
51 util::Permute(permutation, &arc_lengths);
52
53 std::vector<ArcIndex> original_arc_indices(permutation.size());
54 if (!permutation.empty()) {
55 for (ArcIndex i = 0; i < permutation.size(); ++i) {
56 original_arc_indices[permutation[i]] = i;
57 }
58 }
59
60 absl::StatusOr<std::vector<NodeIndex>> topological_order =
62 CHECK_OK(topological_order) << "arcs_with_length form a cycle.";
63
64 return ShortestPathOnDagProblem{
65 .graph = std::move(graph),
66 .arc_lengths = std::move(arc_lengths),
67 .original_arc_indices = std::move(original_arc_indices),
68 .topological_order = std::move(topological_order).value()};
69}
70
71void GetOriginalArcPath(absl::Span<const ArcIndex> original_arc_indices,
72 std::vector<ArcIndex>& arc_path) {
73 if (original_arc_indices.empty()) {
74 return;
75 }
76 for (int i = 0; i < arc_path.size(); ++i) {
77 arc_path[i] = original_arc_indices[arc_path[i]];
78 }
79}
80
81} // namespace
82
84 const int num_nodes, absl::Span<const ArcWithLength> arcs_with_length,
85 const int source, const int destination) {
86 const ShortestPathOnDagProblem problem =
87 ReadProblem(num_nodes, arcs_with_length);
88
90 &problem.graph, &problem.arc_lengths, problem.topological_order);
91 shortest_path_on_dag.RunShortestPathOnDag({source});
92
93 if (!shortest_path_on_dag.IsReachable(destination)) {
94 return PathWithLength{.length = std::numeric_limits<double>::infinity()};
95 }
96
97 std::vector<int> arc_path = shortest_path_on_dag.ArcPathTo(destination);
98 GetOriginalArcPath(problem.original_arc_indices, arc_path);
99 return PathWithLength{
100 .length = shortest_path_on_dag.LengthTo(destination),
101 .arc_path = std::move(arc_path),
102 .node_path = shortest_path_on_dag.NodePathTo(destination)};
103}
104
105std::vector<PathWithLength> KShortestPathsOnDag(
106 const int num_nodes, absl::Span<const ArcWithLength> arcs_with_length,
107 const int source, const int destination, const int path_count) {
108 const ShortestPathOnDagProblem problem =
109 ReadProblem(num_nodes, arcs_with_length);
110
111 KShortestPathsOnDagWrapper<GraphType> shortest_paths_on_dag(
112 &problem.graph, &problem.arc_lengths, problem.topological_order,
113 path_count);
114 shortest_paths_on_dag.RunKShortestPathOnDag({source});
115
116 if (!shortest_paths_on_dag.IsReachable(destination)) {
117 return {PathWithLength{.length = std::numeric_limits<double>::infinity()}};
118 }
119
120 std::vector<double> lengths = shortest_paths_on_dag.LengthsTo(destination);
121 std::vector<std::vector<GraphType::ArcIndex>> arc_paths =
122 shortest_paths_on_dag.ArcPathsTo(destination);
123 std::vector<std::vector<GraphType::NodeIndex>> node_paths =
124 shortest_paths_on_dag.NodePathsTo(destination);
125 std::vector<PathWithLength> paths;
126 paths.reserve(lengths.size());
127 for (int k = 0; k < lengths.size(); ++k) {
128 GetOriginalArcPath(problem.original_arc_indices, arc_paths[k]);
129 paths.push_back(PathWithLength{.length = lengths[k],
130 .arc_path = std::move(arc_paths[k]),
131 .node_path = std::move(node_paths[k])});
132 }
133 return paths;
134}
135
136} // namespace operations_research
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
std::vector< double > LengthsTo(NodeIndex node) const
double LengthTo(NodeIndex node) const
Returns the length of the shortest path from node's source to node.
std::vector< ArcIndex > ArcPathTo(NodeIndex node) const
void RunShortestPathOnDag(absl::Span< const NodeIndex > sources)
std::vector< NodeIndex > NodePathTo(NodeIndex node) const
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)
PathWithLength ShortestPathsOnDag(const int num_nodes, absl::Span< const ArcWithLength > arcs_with_length, const int source, const int destination)
absl::StatusOr< std::vector< int > > FastTopologicalSort(const AdjacencyLists &adj)
void Permute(const IntVector &permutation, Array *array_to_permute)
Definition graph.h:756