Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
multi_dijkstra.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// Runs multiple Dijkstra searches simultaneously (single-threaded, but growing
15// their search radii at the same time) on the same graph.
16// Supports custom arc length functors, and custom stopping criterion and
17// tracking.
18//
19// EXAMPLE:
20// With two sources and a custom stopping criterion that stops the first
21// Dijkstra when it has settled 1000 nodes and the second when it has reached
22// the search radius 123.45:
23//
24// ListGraph<> graph; // From ortools/graph/graph.h
25// ... build the graph ...
26// int source1 = ... , source2 = ...;
27// vector<double> arc_lengths(graph.num_arcs(), 0);
28// ... set the arc lengths ...
29// int num_nodes_to_settle_in_first_search = 1000;
30// std::vector<absl::flat_hash_map<int, DistanceAndParentArc<double>>>
31// reached_nodes; // One map per source: node -> DistanceAndParentArc.
32// reached_nodes = MultiDijkstra<double>(
33// graph,
34// [&arc_lengths](int a) { return arc_lengths[a]; },
35// {{source1}, {source2}},
36// [&num_nodes_to_settle_in_first_search](
37// int settled_node, int source_index, double settled_distance) {
38// if (source_index == 0) {
39// return --num_nodes_to_settle_in_first_search == 0;
40// } else {
41// return settled_distance >= 123.45;
42// }
43// });
44
45#ifndef OR_TOOLS_GRAPH_MULTI_DIJKSTRA_H_
46#define OR_TOOLS_GRAPH_MULTI_DIJKSTRA_H_
47
48#include <iostream>
49#include <ostream>
50#include <queue>
51#include <vector>
52
53#include "absl/container/flat_hash_map.h"
55
56namespace operations_research {
57
58template <class DistanceType>
60 DistanceType distance;
61 int parent_arc; // -1 means "no parent" (i.e. we're at the root).
62 bool operator==(const DistanceAndParentArc& other) const {
63 return distance == other.distance && parent_arc == other.parent_arc;
64 }
65};
66
67template <class DistanceType>
68std::ostream& operator<<(
69 std::ostream& out,
70 DistanceAndParentArc<DistanceType> distance_and_parent_arc);
71
72// Runs multiple Dijkstra searches simultaneously on the same graph, in a single
73// thread. All the Dijkstras share the same priority queue: their search radius
74// will grow "simulatenously".
75//
76// Moreover, each individual Dijkstra search can have several nodes as its
77// "source", and the stopping criterion for each Dijkstra search is highly
78// customizable: the user controls it via a "settled node callback",
79// called every time a node is settled. See the API below.
80//
81// The Dijkstra are sparse: the global space complexity will be linear in the
82// number of search states explored. Ditto for the time complexity, with an
83// additional logarithmic factor caused by the priority queue.
84//
85// This class has many similarities with BoundedDijkstraWrapper from
86// ./bounded_dijkstra.h but adds the overhead of tracking the source index for
87// every search state, and of the sparse (but slower) node hash maps.
88//
89// Arguments:
90// - "graph": the graph. The Graph class must support the interface of
91// ortools/graph/graph.h: OutgoingArcs(), Head(), Tail().
92// - "arc_length_functor": we will call arc_length_functor(a) on every
93// arc "a" explored, where "a" is cast to an int. It should return the arc's
94// length, which is statically cast to DistanceType.
95// - "source_sets" contains the sources. Each source is itself a set of nodes.
96// - "settled_node_callback" will be called every time we settled a node, with 3
97// arguments (node, source index, distance of node from that source).
98// If it returns true, the Dijkstra search from that source will stop.
99//
100// Returns the list of Dijkstra search: for each source #s, the element #s of
101// the returned vector will map every node explored in the Dijkstra from source
102// #s to its distance and parent arc.
103template <class DistanceType, class Graph, class ArcLengthFunctor,
104 class SettledNodeCallbackType>
105std::vector<absl::flat_hash_map<int, DistanceAndParentArc<DistanceType>>>
106MultiDijkstra(const Graph& graph, ArcLengthFunctor arc_length_functor,
107 const std::vector<std::vector<int>>& source_sets,
108 SettledNodeCallbackType settled_node_callback);
109
110// =============================================================================
111// Implementation of the function templates
112// =============================================================================
113template <class DistanceType>
114std::ostream& operator<<(
115 std::ostream& out,
116 DistanceAndParentArc<DistanceType> distance_and_parent_arc) {
117 return out << "{distance=" << distance_and_parent_arc.distance
118 << ", parent_arc=" << distance_and_parent_arc.parent_arc << "}";
119}
120
121template <class DistanceType, class Graph, class ArcLengthFunctor,
122 class SettledNodeCallbackType>
123std::vector<absl::flat_hash_map<int, DistanceAndParentArc<DistanceType>>>
124MultiDijkstra(const Graph& graph, ArcLengthFunctor arc_length_functor,
125 const std::vector<std::vector<int>>& source_sets,
126 SettledNodeCallbackType settled_node_callback) {
127 // Initialize the return data structure and the priority queue.
128 const int num_sources = source_sets.size();
129 std::vector<absl::flat_hash_map<int, DistanceAndParentArc<DistanceType>>>
130 reached_from(num_sources); // Also the returned output!
131 struct State {
132 DistanceType distance;
133 int node;
134 int source_index;
135 };
136 struct StateComparator {
137 bool operator()(const State& s0, const State& s1) const {
138 if (s0.distance != s1.distance) return s0.distance > s1.distance;
139 if (s0.node != s1.node) return s0.node > s1.node;
140 return s0.source_index > s1.source_index;
141 }
142 };
143 StateComparator pq_state_comparator;
144 std::priority_queue<State, std::vector<State>, StateComparator> pq(
145 pq_state_comparator);
146 std::vector<bool> dijkstra_is_active(num_sources, false);
147 int num_active_dijkstras = 0;
148 for (int source_index = 0; source_index < num_sources; ++source_index) {
149 if (!source_sets[source_index].empty()) {
150 dijkstra_is_active[source_index] = true;
151 ++num_active_dijkstras;
152 }
153 for (const int node : source_sets[source_index]) {
155 &reached_from[source_index],
156 {node, {/*distance=*/0, /*parent_arc=*/-1}})) {
157 pq.push({/*distance=*/0, /*node=*/node, /*source_index=*/source_index});
158 }
159 }
160 }
161
162 // Main Dijkstra loop.
163 while (!pq.empty() && num_active_dijkstras > 0) {
164 const State state = pq.top();
165 pq.pop();
166
167 if (!dijkstra_is_active[state.source_index]) continue;
168
169 // Dijkstra optimization: ignore states that don't correspond to the optimal
170 // (such states have been preceded by better states in the queue order,
171 // without being deleted since priority_queue doesn't have erase()).
172 if (gtl::FindOrDie(reached_from[state.source_index], state.node).distance <
173 state.distance) {
174 continue;
175 }
176
177 if (settled_node_callback(state.node, state.source_index, state.distance)) {
178 dijkstra_is_active[state.source_index] = false;
179 --num_active_dijkstras;
180 continue;
181 }
182
183 for (const int arc : graph.OutgoingArcs(state.node)) {
184 const DistanceType distance = arc_length_functor(arc) + state.distance;
185 const int head_node = graph.Head(arc);
186 auto insertion_result = reached_from[state.source_index].insert(
187 {head_node, {/*distance=*/distance, /*parent_arc=*/arc}});
188 if (!insertion_result.second) { // Already reached...
189 if (insertion_result.first->second.distance <= distance) continue;
190 insertion_result.first->second = {/*distance=*/distance,
191 /*parent_arc=*/arc};
192 }
193 pq.push(State{/*distance=*/distance, /*node=*/head_node,
194 /*source_index=*/state.source_index});
195 }
196 }
197 return reached_from;
198}
199
200} // namespace operations_research
201
202#endif // OR_TOOLS_GRAPH_MULTI_DIJKSTRA_H_
NodeIndexType size() const
Definition graph.h:212
GraphType graph
int arc
bool InsertIfNotPresent(Collection *const collection, const typename Collection::value_type &value)
Definition map_util.h:127
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition map_util.h:211
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< absl::flat_hash_map< int, DistanceAndParentArc< DistanceType > > > MultiDijkstra(const Graph &graph, ArcLengthFunctor arc_length_functor, const std::vector< std::vector< int > > &source_sets, SettledNodeCallbackType settled_node_callback)
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
util::ReverseArcStaticGraph Graph
Type of graph to use.
double distance
bool operator==(const DistanceAndParentArc &other) const