14#ifndef ORTOOLS_GRAPH_BIDIRECTIONAL_DIJKSTRA_H_
15#define ORTOOLS_GRAPH_BIDIRECTIONAL_DIJKSTRA_H_
23#include "absl/base/thread_annotations.h"
24#include "absl/log/log.h"
25#include "absl/log/vlog_is_on.h"
26#include "absl/strings/str_cat.h"
27#include "absl/synchronization/mutex.h"
28#include "absl/synchronization/notification.h"
43template <
typename GraphType,
typename DistanceType>
57 const std::vector<DistanceType>* forward_arc_lengths,
58 const GraphType* backward_graph,
59 const std::vector<DistanceType>* backward_arc_lengths);
118 const std::vector<NodeDistance>& destinations);
132 inline static Direction Reverse(Direction d) {
133 return d == FORWARD ? BACKWARD : FORWARD;
136 inline static DistanceType infinity() {
137 return std::numeric_limits<DistanceType>::infinity();
140 template <Direction dir>
141 void PerformHalfSearch(absl::Notification* search_has_ended);
144 const GraphType* graph_[2];
145 const std::vector<DistanceType>* arc_lengths_[2];
148 std::priority_queue<NodeDistance> queue_[2];
150 std::vector<bool> is_source_[2];
151 std::vector<bool> is_reached_[2];
155 std::vector<char> is_settled_[2];
157 std::vector<DistanceType> distances_[2];
158 std::vector<ArcIndex> parent_arc_[2];
159 std::vector<NodeIndex> reached_nodes_[2];
174 std::vector<absl::Mutex> node_mutex_;
176 absl::Mutex search_mutex_;
177 NodeDistance best_meeting_point_ ABSL_GUARDED_BY(search_mutex_);
178 DistanceType current_search_radius_[2] ABSL_GUARDED_BY(search_mutex_);
180 ThreadPool search_threads_;
183template <
typename GraphType,
typename DistanceType>
185 const GraphType* forward_graph,
186 const std::vector<DistanceType>* forward_arc_lengths,
187 const GraphType* backward_graph,
188 const std::vector<DistanceType>* backward_arc_lengths)
189 : node_mutex_(forward_graph->num_nodes()), search_threads_(2) {
190 CHECK_EQ(forward_graph->num_nodes(), backward_graph->num_nodes());
191 const int num_nodes = forward_graph->num_nodes();
193 for (
int i = 0; i < num_nodes; ++i) {
194 CHECK_GE((*forward_arc_lengths)[i], 0) << i;
195 CHECK_GE((*backward_arc_lengths)[i], 0) << i;
197 graph_[FORWARD] = forward_graph;
198 graph_[BACKWARD] = backward_graph;
199 arc_lengths_[FORWARD] = forward_arc_lengths;
200 arc_lengths_[BACKWARD] = backward_arc_lengths;
201 for (
const Direction dir : {FORWARD, BACKWARD}) {
202 current_search_radius_[dir] = -infinity();
203 is_source_[dir].assign(num_nodes,
false);
204 is_reached_[dir].assign(num_nodes,
false);
205 is_settled_[dir].assign(num_nodes,
false);
206 distances_[dir].assign(num_nodes, infinity());
207 parent_arc_[dir].assign(num_nodes, -1);
211template <
typename GraphType,
typename DistanceType>
213 const Path& path)
const {
217 absl::StrAppend(&out, graph_[FORWARD]->Tail(arc),
" --(#", arc,
":",
218 ((*arc_lengths_[FORWARD])[arc]),
")--> ");
222 absl::StrAppend(&out,
" <--(#", arc,
":", ((*arc_lengths_[BACKWARD])[arc]),
223 ")-- ", graph_[BACKWARD]->Tail(arc));
228template <
typename GraphType,
typename DistanceType>
229std::vector<typename GraphType::NodeIndex>
234 std::vector<int> nodes;
236 nodes.push_back(graph_[FORWARD]->Tail(arc));
240 nodes.push_back(graph_[BACKWARD]->Tail(arc));
245template <
typename GraphType,
typename DistanceType>
248 const std::vector<NodeDistance>& sources,
249 const std::vector<NodeDistance>& destinations)
253 ABSL_NO_THREAD_SAFETY_ANALYSIS {
255 VLOG(2) <<
"Starting search with " << sources.size() <<
" sources and "
256 << destinations.size() <<
" destinations. Sources:";
257 for (
const NodeDistance& src : sources) VLOG(2) << src.DebugString();
258 VLOG(2) <<
"Destinations:";
259 for (
const NodeDistance& dst : destinations) VLOG(2) << dst.DebugString();
261 if (sources.empty() || destinations.empty())
return {-1, {}, {}};
263 for (
const Direction dir : {FORWARD, BACKWARD}) {
264 const std::vector<NodeDistance>& srcs =
265 dir == FORWARD ? sources : destinations;
266 CHECK(queue_[dir].empty());
267 QCHECK_EQ(reached_nodes_[dir].size(), 0);
269 for (
bool b : is_reached_[dir]) QCHECK(!b);
270 for (
bool b : is_settled_[dir]) QCHECK(!b);
273 CHECK_GE(src.node, 0);
274 CHECK_LT(src.node, graph_[dir]->num_nodes());
275 is_source_[dir][src.node] =
true;
276 if (!is_reached_[dir][src.node]) {
277 is_reached_[dir][src.node] =
true;
278 reached_nodes_[dir].push_back(src.node);
279 parent_arc_[dir][src.node] = -1;
280 }
else if (src.distance >= distances_[dir][src.node]) {
285 distances_[dir][src.node] = src.distance;
286 queue_[dir].push(src);
291 best_meeting_point_ = {-1, infinity()};
292 absl::Notification search_has_ended[2];
293 search_threads_.Schedule([
this, &search_has_ended, &sources]() {
294 PerformHalfSearch<FORWARD>(&search_has_ended[FORWARD]);
296 search_threads_.Schedule([
this, &search_has_ended, &destinations]() {
297 PerformHalfSearch<BACKWARD>(&search_has_ended[BACKWARD]);
301 search_has_ended[FORWARD].WaitForNotification();
302 search_has_ended[BACKWARD].WaitForNotification();
311 for (
const Direction dir : {FORWARD, BACKWARD}) {
312 current_search_radius_[dir] = -infinity();
313 for (
const int node : reached_nodes_[dir]) {
314 is_reached_[dir][node] =
false;
315 is_settled_[dir][node] =
false;
317 reached_nodes_[dir].clear();
320 is_source_[FORWARD][src.node] =
false;
323 is_source_[BACKWARD][dst.node] =
false;
327 Path path = {best_meeting_point_.node, {}, {}};
330 for (
const Direction dir : {FORWARD, BACKWARD}) {
332 std::vector<int>* arc_path =
335 const int arc = parent_arc_[dir][node];
337 arc_path->push_back(arc);
338 node = graph_[dir]->Tail(arc);
340 std::reverse(arc_path->begin(), arc_path->end());
345template <
typename GraphType,
typename DistanceType>
347 typename BidirectionalDijkstra<GraphType, DistanceType>::Direction dir>
348void BidirectionalDijkstra<GraphType, DistanceType>::PerformHalfSearch(
349 absl::Notification* search_has_ended) {
350 while (!queue_[dir].empty()) {
351 const NodeDistance top = queue_[dir].top();
356 if (is_settled_[dir][top.node])
continue;
357 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Popped "
358 << top.DebugString();
364 node_mutex_[top.node].Lock();
365 is_settled_[dir][top.node] =
true;
369 if (is_source_[Reverse(dir)][top.node]) {
370 const DistanceType meeting_distance =
371 top.distance + distances_[Reverse(dir)][top.node];
374 node_mutex_[top.node].Unlock();
375 absl::MutexLock search_lock(search_mutex_);
376 if (meeting_distance < best_meeting_point_.distance) {
377 best_meeting_point_ = {top.node, meeting_distance};
378 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
379 <<
": New best: " << best_meeting_point_.DebugString();
382 node_mutex_[top.node].Unlock();
388 DistanceType potentially_interesting_distance_upper_bound;
390 absl::MutexLock lock(search_mutex_);
391 current_search_radius_[dir] = top.distance;
392 potentially_interesting_distance_upper_bound =
393 best_meeting_point_.distance - current_search_radius_[
Reverse(dir)];
395 if (top.distance >= potentially_interesting_distance_upper_bound) {
396 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Stopping.";
401 for (
const int arc : graph_[dir]->OutgoingArcs(top.node)) {
402 const DistanceType candidate_distance =
403 top.distance + (*arc_lengths_[dir])[arc];
404 const int head = graph_[dir]->Head(arc);
405 if (!is_reached_[dir][head] ||
406 candidate_distance < distances_[dir][head]) {
407 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Pushing: "
408 << NodeDistance({head, candidate_distance}).DebugString();
409 if (!is_reached_[dir][head]) {
410 is_reached_[dir][head] =
true;
411 reached_nodes_[dir].push_back(head);
413 parent_arc_[dir][head] = arc;
421 if (candidate_distance < potentially_interesting_distance_upper_bound) {
422 queue_[dir].push({head, candidate_distance});
427 DistanceType meeting_distance = infinity();
429 absl::MutexLock node_lock(node_mutex_[head]);
430 distances_[dir][head] = candidate_distance;
432 if (is_settled_[
Reverse(dir)][head]) {
434 candidate_distance + distances_[
Reverse(dir)][head];
435 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
436 <<
": Found meeting point!";
442 if (meeting_distance != infinity()) {
443 absl::MutexLock search_lock(search_mutex_);
444 if (meeting_distance < best_meeting_point_.distance) {
445 best_meeting_point_ = {head, meeting_distance};
446 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
447 <<
": New best: " << best_meeting_point_.DebugString();
453 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Done. Cleaning up...";
456 while (!queue_[dir].empty()) queue_[dir].pop();
459 search_has_ended->Notify();
GraphType::NodeIndex NodeIndex
GraphType::ArcIndex ArcIndex
std::string PathDebugString(const Path &path) const
Path SetToSetShortestPath(const std::vector< NodeDistance > &sources, const std::vector< NodeDistance > &destinations)
std::vector< NodeIndex > PathToNodePath(const Path &path) const
Path OneToOneShortestPath(NodeIndex from, NodeIndex to)
BidirectionalDijkstra(const GraphType *forward_graph, const std::vector< DistanceType > *forward_arc_lengths, const GraphType *backward_graph, const std::vector< DistanceType > *backward_arc_lengths)
ReverseView< Container > reversed_view(const Container &c)
BeginEndReverseIteratorWrapper< Container > Reverse(const Container &c)
trees with all degrees equal to
bool operator<(const NodeDistance &other) const
std::string DebugString() const
std::vector< ArcIndex > backward_arc_path
std::vector< ArcIndex > forward_arc_path