14#ifndef OR_TOOLS_GRAPH_BIDIRECTIONAL_DIJKSTRA_H_
15#define OR_TOOLS_GRAPH_BIDIRECTIONAL_DIJKSTRA_H_
23#include "absl/base/thread_annotations.h"
24#include "absl/strings/str_cat.h"
25#include "absl/synchronization/mutex.h"
26#include "absl/synchronization/notification.h"
41template <
typename GraphType,
typename DistanceType>
55 const std::vector<DistanceType>* forward_arc_lengths,
56 const GraphType* backward_graph,
57 const std::vector<DistanceType>* backward_arc_lengths);
116 const std::vector<NodeDistance>& destinations);
130 inline static Direction Reverse(Direction d) {
131 return d == FORWARD ? BACKWARD : FORWARD;
134 inline static DistanceType infinity() {
135 return std::numeric_limits<DistanceType>::infinity();
138 template <Direction dir>
139 void PerformHalfSearch(absl::Notification* search_has_ended);
142 const GraphType* graph_[2];
143 const std::vector<DistanceType>* arc_lengths_[2];
146 std::priority_queue<NodeDistance> queue_[2];
148 std::vector<bool> is_source_[2];
149 std::vector<bool> is_reached_[2];
153 std::vector<char> is_settled_[2];
155 std::vector<DistanceType> distances_[2];
156 std::vector<ArcIndex> parent_arc_[2];
157 std::vector<NodeIndex> reached_nodes_[2];
172 std::vector<absl::Mutex> node_mutex_;
174 absl::Mutex search_mutex_;
175 NodeDistance best_meeting_point_ ABSL_GUARDED_BY(search_mutex_);
176 DistanceType current_search_radius_[2] ABSL_GUARDED_BY(search_mutex_);
178 ThreadPool search_threads_;
181template <
typename GraphType,
typename DistanceType>
183 const GraphType* forward_graph,
184 const std::vector<DistanceType>* forward_arc_lengths,
185 const GraphType* backward_graph,
186 const std::vector<DistanceType>* backward_arc_lengths)
187 : node_mutex_(forward_graph->num_nodes()), search_threads_(2) {
188 CHECK_EQ(forward_graph->num_nodes(), backward_graph->num_nodes());
189 const int num_nodes = forward_graph->num_nodes();
191 for (
int i = 0; i < num_nodes; ++i) {
192 CHECK_GE((*forward_arc_lengths)[i], 0) << i;
193 CHECK_GE((*backward_arc_lengths)[i], 0) << i;
195 graph_[FORWARD] = forward_graph;
196 graph_[BACKWARD] = backward_graph;
197 arc_lengths_[FORWARD] = forward_arc_lengths;
198 arc_lengths_[BACKWARD] = backward_arc_lengths;
199 for (
const Direction dir : {FORWARD, BACKWARD}) {
200 current_search_radius_[dir] = -infinity();
201 is_source_[dir].assign(num_nodes,
false);
202 is_reached_[dir].assign(num_nodes,
false);
203 is_settled_[dir].assign(num_nodes,
false);
204 distances_[dir].assign(num_nodes, infinity());
205 parent_arc_[dir].assign(num_nodes, -1);
210template <
typename GraphType,
typename DistanceType>
212 const Path& path)
const {
216 absl::StrAppend(&out, graph_[FORWARD]->Tail(
arc),
" --(#",
arc,
":",
217 ((*arc_lengths_[FORWARD])[
arc]),
")--> ");
221 absl::StrAppend(&out,
" <--(#",
arc,
":", ((*arc_lengths_[BACKWARD])[
arc]),
222 ")-- ", graph_[BACKWARD]->Tail(
arc));
227template <
typename GraphType,
typename DistanceType>
228std::vector<typename GraphType::NodeIndex>
233 std::vector<int>
nodes;
235 nodes.push_back(graph_[FORWARD]->Tail(
arc));
239 nodes.push_back(graph_[BACKWARD]->Tail(
arc));
244template <
typename GraphType,
typename DistanceType>
247 const std::vector<NodeDistance>& sources,
248 const std::vector<NodeDistance>& destinations)
252 ABSL_NO_THREAD_SAFETY_ANALYSIS {
254 VLOG(2) <<
"Starting search with " << sources.size() <<
" sources and "
255 << destinations.size() <<
" destinations. Sources:";
256 for (
const NodeDistance& src : sources) VLOG(2) << src.DebugString();
257 VLOG(2) <<
"Destinations:";
258 for (
const NodeDistance& dst : destinations) VLOG(2) << dst.DebugString();
260 if (sources.empty() || destinations.empty())
return {-1, {}, {}};
262 for (
const Direction dir : {FORWARD, BACKWARD}) {
263 const std::vector<NodeDistance>& srcs =
264 dir == FORWARD ? sources : destinations;
265 CHECK(queue_[dir].empty());
266 QCHECK_EQ(reached_nodes_[dir].
size(), 0);
268 for (
bool b : is_reached_[dir]) QCHECK(!
b);
269 for (
bool b : is_settled_[dir]) QCHECK(!
b);
272 CHECK_GE(src.node, 0);
273 CHECK_LT(src.node, graph_[dir]->num_nodes());
274 is_source_[dir][src.node] =
true;
275 if (!is_reached_[dir][src.node]) {
276 is_reached_[dir][src.node] =
true;
277 reached_nodes_[dir].push_back(src.node);
278 parent_arc_[dir][src.node] = -1;
279 }
else if (src.distance >= distances_[dir][src.node]) {
284 distances_[dir][src.node] = src.distance;
285 queue_[dir].push(src);
290 best_meeting_point_ = {-1, infinity()};
291 absl::Notification search_has_ended[2];
292 search_threads_.Schedule([
this, &search_has_ended, &sources]() {
293 PerformHalfSearch<FORWARD>(&search_has_ended[FORWARD]);
295 search_threads_.Schedule([
this, &search_has_ended, &destinations]() {
296 PerformHalfSearch<BACKWARD>(&search_has_ended[BACKWARD]);
300 search_has_ended[FORWARD].WaitForNotification();
301 search_has_ended[BACKWARD].WaitForNotification();
310 for (
const Direction dir : {FORWARD, BACKWARD}) {
311 current_search_radius_[dir] = -infinity();
312 for (
const int node : reached_nodes_[dir]) {
313 is_reached_[dir][node] =
false;
314 is_settled_[dir][node] =
false;
316 reached_nodes_[dir].clear();
319 is_source_[FORWARD][src.node] =
false;
322 is_source_[BACKWARD][dst.node] =
false;
326 Path path = {best_meeting_point_.node, {}, {}};
329 for (
const Direction dir : {FORWARD, BACKWARD}) {
331 std::vector<int>* arc_path =
334 const int arc = parent_arc_[dir][node];
336 arc_path->push_back(
arc);
337 node = graph_[dir]->Tail(
arc);
339 std::reverse(arc_path->begin(), arc_path->end());
344template <
typename GraphType,
typename DistanceType>
346 typename BidirectionalDijkstra<GraphType, DistanceType>::Direction dir>
348 absl::Notification* search_has_ended) {
349 while (!queue_[dir].empty()) {
350 const NodeDistance top = queue_[dir].top();
355 if (is_settled_[dir][top.node])
continue;
356 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Popped "
357 << top.DebugString();
363 node_mutex_[top.node].Lock();
364 is_settled_[dir][top.node] =
true;
368 if (is_source_[Reverse(dir)][top.node]) {
369 const DistanceType meeting_distance =
370 top.distance + distances_[Reverse(dir)][top.node];
373 node_mutex_[top.node].Unlock();
374 absl::MutexLock search_lock(&search_mutex_);
375 if (meeting_distance < best_meeting_point_.distance) {
376 best_meeting_point_ = {top.node, meeting_distance};
377 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
378 <<
": New best: " << best_meeting_point_.DebugString();
381 node_mutex_[top.node].Unlock();
387 DistanceType potentially_interesting_distance_upper_bound;
389 absl::MutexLock lock(&search_mutex_);
390 current_search_radius_[dir] = top.distance;
391 potentially_interesting_distance_upper_bound =
392 best_meeting_point_.distance - current_search_radius_[
Reverse(dir)];
394 if (top.distance >= potentially_interesting_distance_upper_bound) {
395 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Stopping.";
400 for (
const int arc : graph_[dir]->OutgoingArcs(top.node)) {
401 const DistanceType candidate_distance =
402 top.distance + (*arc_lengths_[dir])[
arc];
403 const int head = graph_[dir]->Head(
arc);
404 if (!is_reached_[dir][
head] ||
405 candidate_distance < distances_[dir][
head]) {
406 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Pushing: "
407 << NodeDistance({
head, candidate_distance}).DebugString();
408 if (!is_reached_[dir][
head]) {
409 is_reached_[dir][
head] =
true;
410 reached_nodes_[dir].push_back(
head);
420 if (candidate_distance < potentially_interesting_distance_upper_bound) {
421 queue_[dir].push({
head, candidate_distance});
426 DistanceType meeting_distance = infinity();
428 absl::MutexLock node_lock(&node_mutex_[
head]);
429 distances_[dir][
head] = candidate_distance;
433 candidate_distance + distances_[
Reverse(dir)][
head];
434 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
435 <<
": Found meeting point!";
441 if (meeting_distance != infinity()) {
442 absl::MutexLock search_lock(&search_mutex_);
443 if (meeting_distance < best_meeting_point_.distance) {
444 best_meeting_point_ = {
head, meeting_distance};
445 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
446 <<
": New best: " << best_meeting_point_.DebugString();
452 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Done. Cleaning up...";
455 while (!queue_[dir].empty()) queue_[dir].pop();
458 search_has_ended->Notify();
GraphType::NodeIndex NodeIndex
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)
GraphType::ArcIndex ArcIndex
ReverseView< Container > reversed_view(const Container &c)
In SWIG mode, we don't want anything besides these top-level includes.
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
NodeIndex meeting_point
The node where the two half-paths meet. -1 if no path exists.