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/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);
209 search_threads_.StartWorkers();
212template <
typename GraphType,
typename DistanceType>
214 const Path& path)
const {
218 absl::StrAppend(&out, graph_[FORWARD]->Tail(arc),
" --(#", arc,
":",
219 ((*arc_lengths_[FORWARD])[arc]),
")--> ");
223 absl::StrAppend(&out,
" <--(#", arc,
":", ((*arc_lengths_[BACKWARD])[arc]),
224 ")-- ", graph_[BACKWARD]->Tail(arc));
229template <
typename GraphType,
typename DistanceType>
230std::vector<typename GraphType::NodeIndex>
235 std::vector<int> nodes;
237 nodes.push_back(graph_[FORWARD]->Tail(arc));
241 nodes.push_back(graph_[BACKWARD]->Tail(arc));
246template <
typename GraphType,
typename DistanceType>
249 const std::vector<NodeDistance>& sources,
250 const std::vector<NodeDistance>& destinations)
254 ABSL_NO_THREAD_SAFETY_ANALYSIS {
256 VLOG(2) <<
"Starting search with " << sources.size() <<
" sources and "
257 << destinations.size() <<
" destinations. Sources:";
258 for (
const NodeDistance& src : sources) VLOG(2) << src.DebugString();
259 VLOG(2) <<
"Destinations:";
260 for (
const NodeDistance& dst : destinations) VLOG(2) << dst.DebugString();
262 if (sources.empty() || destinations.empty())
return {-1, {}, {}};
264 for (
const Direction dir : {FORWARD, BACKWARD}) {
265 const std::vector<NodeDistance>& srcs =
266 dir == FORWARD ? sources : destinations;
267 CHECK(queue_[dir].empty());
268 QCHECK_EQ(reached_nodes_[dir].size(), 0);
270 for (
bool b : is_reached_[dir]) QCHECK(!b);
271 for (
bool b : is_settled_[dir]) QCHECK(!b);
274 CHECK_GE(src.node, 0);
275 CHECK_LT(src.node, graph_[dir]->num_nodes());
276 is_source_[dir][src.node] =
true;
277 if (!is_reached_[dir][src.node]) {
278 is_reached_[dir][src.node] =
true;
279 reached_nodes_[dir].push_back(src.node);
280 parent_arc_[dir][src.node] = -1;
281 }
else if (src.distance >= distances_[dir][src.node]) {
286 distances_[dir][src.node] = src.distance;
287 queue_[dir].push(src);
292 best_meeting_point_ = {-1, infinity()};
293 absl::Notification search_has_ended[2];
294 search_threads_.Schedule([
this, &search_has_ended, &sources]() {
295 PerformHalfSearch<FORWARD>(&search_has_ended[FORWARD]);
297 search_threads_.Schedule([
this, &search_has_ended, &destinations]() {
298 PerformHalfSearch<BACKWARD>(&search_has_ended[BACKWARD]);
302 search_has_ended[FORWARD].WaitForNotification();
303 search_has_ended[BACKWARD].WaitForNotification();
312 for (
const Direction dir : {FORWARD, BACKWARD}) {
313 current_search_radius_[dir] = -infinity();
314 for (
const int node : reached_nodes_[dir]) {
315 is_reached_[dir][node] =
false;
316 is_settled_[dir][node] =
false;
318 reached_nodes_[dir].clear();
321 is_source_[FORWARD][src.node] =
false;
324 is_source_[BACKWARD][dst.node] =
false;
328 Path path = {best_meeting_point_.node, {}, {}};
331 for (
const Direction dir : {FORWARD, BACKWARD}) {
333 std::vector<int>* arc_path =
336 const int arc = parent_arc_[dir][node];
338 arc_path->push_back(arc);
339 node = graph_[dir]->Tail(arc);
341 std::reverse(arc_path->begin(), arc_path->end());
346template <
typename GraphType,
typename DistanceType>
348 typename BidirectionalDijkstra<GraphType, DistanceType>::Direction dir>
349void BidirectionalDijkstra<GraphType, DistanceType>::PerformHalfSearch(
350 absl::Notification* search_has_ended) {
351 while (!queue_[dir].empty()) {
352 const NodeDistance top = queue_[dir].top();
357 if (is_settled_[dir][top.node])
continue;
358 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Popped "
359 << top.DebugString();
365 node_mutex_[top.node].Lock();
366 is_settled_[dir][top.node] =
true;
370 if (is_source_[Reverse(dir)][top.node]) {
371 const DistanceType meeting_distance =
372 top.distance + distances_[Reverse(dir)][top.node];
375 node_mutex_[top.node].Unlock();
376 absl::MutexLock search_lock(&search_mutex_);
377 if (meeting_distance < best_meeting_point_.distance) {
378 best_meeting_point_ = {top.node, meeting_distance};
379 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
380 <<
": New best: " << best_meeting_point_.DebugString();
383 node_mutex_[top.node].Unlock();
389 DistanceType potentially_interesting_distance_upper_bound;
391 absl::MutexLock lock(&search_mutex_);
392 current_search_radius_[dir] = top.distance;
393 potentially_interesting_distance_upper_bound =
394 best_meeting_point_.distance - current_search_radius_[
Reverse(dir)];
396 if (top.distance >= potentially_interesting_distance_upper_bound) {
397 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Stopping.";
402 for (
const int arc : graph_[dir]->OutgoingArcs(top.node)) {
403 const DistanceType candidate_distance =
404 top.distance + (*arc_lengths_[dir])[arc];
405 const int head = graph_[dir]->Head(arc);
406 if (!is_reached_[dir][head] ||
407 candidate_distance < distances_[dir][head]) {
408 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Pushing: "
409 << NodeDistance({head, candidate_distance}).DebugString();
410 if (!is_reached_[dir][head]) {
411 is_reached_[dir][head] =
true;
412 reached_nodes_[dir].push_back(head);
414 parent_arc_[dir][head] = arc;
422 if (candidate_distance < potentially_interesting_distance_upper_bound) {
423 queue_[dir].push({head, candidate_distance});
428 DistanceType meeting_distance = infinity();
430 absl::MutexLock node_lock(&node_mutex_[head]);
431 distances_[dir][head] = candidate_distance;
433 if (is_settled_[
Reverse(dir)][head]) {
435 candidate_distance + distances_[
Reverse(dir)][head];
436 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
437 <<
": Found meeting point!";
443 if (meeting_distance != infinity()) {
444 absl::MutexLock search_lock(&search_mutex_);
445 if (meeting_distance < best_meeting_point_.distance) {
446 best_meeting_point_ = {head, meeting_distance};
447 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD")
448 <<
": New best: " << best_meeting_point_.DebugString();
454 DVLOG(2) << (dir ?
"BACKWARD" :
"FORWARD") <<
": Done. Cleaning up...";
457 while (!queue_[dir].empty()) queue_[dir].pop();
460 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)
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.