279 const GraphType& graph,
const std::vector<PathDistance>& arc_lengths,
280 typename GraphType::NodeIndex source,
281 typename GraphType::NodeIndex destination,
unsigned k) {
282 using NodeIndex =
typename GraphType::NodeIndex;
286 CHECK_GE(k, 0) <<
"k must be nonnegative. Input value: " << k;
287 CHECK_NE(k, 0) <<
"k cannot be zero: you are requesting zero paths!";
289 CHECK_GT(graph.num_nodes(), 0) <<
"The graph is empty: it has no nodes";
290 CHECK_GT(graph.num_arcs(), 0) <<
"The graph is empty: it has no arcs";
292 CHECK_GE(source, 0) <<
"The source node must be nonnegative. Input value: "
294 CHECK_LT(source, graph.num_nodes())
295 <<
"The source node must be a valid node. Input value: " << source
296 <<
". Number of nodes in the input graph: " << graph.num_nodes();
297 CHECK_GE(destination, 0)
298 <<
"The source node must be nonnegative. Input value: " << destination;
299 CHECK_LT(destination, graph.num_nodes())
300 <<
"The destination node must be a valid node. Input value: "
302 <<
". Number of nodes in the input graph: " << graph.num_nodes();
308 std::tuple<std::vector<NodeIndex>,
PathDistance> first_path =
310 if (std::get<0>(first_path).empty())
return paths;
311 paths.paths.push_back(std::move(std::get<0>(first_path)));
312 paths.distances.push_back(std::get<1>(first_path));
321 std::priority_queue<internal::PathWithPriority<GraphType>>>
327 VLOG(1) <<
"k: " << k;
330 const absl::Span<NodeIndex> last_shortest_path =
331 absl::MakeSpan(paths.paths.back());
336 for (
int spur_node_position = 0;
337 spur_node_position < last_shortest_path.size() - 1;
338 ++spur_node_position) {
339 VLOG(4) <<
" spur_node_position: " << spur_node_position;
340 VLOG(4) <<
" last_shortest_path: "
341 << absl::StrJoin(last_shortest_path,
" - ") <<
" ("
342 << last_shortest_path.size() <<
")";
343 if (spur_node_position > 0) {
344 DCHECK_NE(last_shortest_path[spur_node_position], source);
346 DCHECK_NE(last_shortest_path[spur_node_position], destination);
348 const NodeIndex spur_node = last_shortest_path[spur_node_position];
352 const absl::Span<NodeIndex> root_path =
353 last_shortest_path.subspan(0, spur_node_position + 1);
354 DCHECK_GE(root_path.length(), 1);
355 DCHECK_NE(root_path.back(), destination);
366 std::vector<PathDistance> arc_lengths_for_detour = arc_lengths;
367 for (absl::Span<const NodeIndex> previous_path : paths.paths) {
373 if (previous_path.size() <= root_path.length())
continue;
374 const bool has_same_prefix_as_root_path = std::equal(
375 root_path.begin(), root_path.end(), previous_path.begin(),
376 previous_path.begin() + root_path.length());
377 if (!has_same_prefix_as_root_path)
continue;
379 const typename GraphType::ArcIndex after_spur_node_arc =
381 previous_path[spur_node_position + 1]);
382 VLOG(4) <<
" after_spur_node_arc: " << graph.Tail(after_spur_node_arc)
383 <<
" - " << graph.Head(after_spur_node_arc) <<
" (" << source
384 <<
" - " << destination <<
")";
385 arc_lengths_for_detour[after_spur_node_arc] =
393 for (
int node_position = 0; node_position < spur_node_position;
395 for (
const int arc : graph.OutgoingArcs(root_path[node_position])) {
399 VLOG(3) <<
" arc_lengths_for_detour: "
400 << absl::StrJoin(arc_lengths_for_detour,
" - ");
405 std::tuple<std::vector<NodeIndex>,
PathDistance> detour_path =
407 spur_node, destination);
409 if (std::get<0>(detour_path).empty()) {
413 VLOG(2) <<
" detour_path: "
414 << absl::StrJoin(std::get<0>(detour_path),
" - ") <<
" ("
415 << std::get<0>(detour_path).size()
416 <<
"): " << std::get<1>(detour_path);
417 std::vector<NodeIndex> spur_path = std::move(std::get<0>(detour_path));
418 if (ABSL_PREDICT_FALSE(spur_path.empty()))
continue;
421 CHECK_EQ(root_path.back(), spur_path.front());
422 CHECK_EQ(spur_node, spur_path.front());
424 if (spur_path.size() == 1) {
425 CHECK_EQ(spur_path.front(), destination);
430 const bool root_path_leads_to_spur_path = absl::c_any_of(
431 graph.OutgoingArcs(root_path.back()),
432 [&graph, node_after_spur_in_spur_path = *(spur_path.begin() + 1)](
433 const typename GraphType::ArcIndex arc_index) {
434 return graph.Head(arc_index) == node_after_spur_in_spur_path;
436 CHECK(root_path_leads_to_spur_path);
441 for (absl::Span<const NodeIndex> previous_path : paths.paths) {
442 if (previous_path.size() <= spur_node_position + 1)
continue;
443 const bool has_same_prefix_as_root_path = std::equal(
444 root_path.begin(), root_path.end(), previous_path.begin(),
445 previous_path.begin() + root_path.length());
446 if (has_same_prefix_as_root_path) {
447 CHECK_NE(spur_path[1], previous_path[spur_node_position + 1])
448 <<
"Forbidden arc " << previous_path[spur_node_position]
449 <<
" - " << previous_path[spur_node_position + 1]
450 <<
" is present in the spur path "
451 << absl::StrJoin(spur_path,
" - ");
457 std::vector<NodeIndex> new_path;
458 absl::c_copy(root_path.subspan(0, spur_node_position),
459 std::back_inserter(new_path));
460 absl::c_copy(spur_path, std::back_inserter(new_path));
462 DCHECK_EQ(new_path.front(), source);
463 DCHECK_EQ(new_path.back(), destination);
468 absl::flat_hash_set<NodeIndex> visited_nodes(new_path.begin(),
470 CHECK_EQ(visited_nodes.size(), new_path.size());
486 const bool is_new_path_already_known = std::any_of(
487 variant_path_queue.container().cbegin(),
488 variant_path_queue.container().cend(),
490 return element.path() == new_path;
492 if (is_new_path_already_known)
continue;
496 VLOG(5) <<
" New potential path generated: "
497 << absl::StrJoin(new_path,
" - ") <<
" (" << new_path.size()
499 VLOG(5) <<
" Root: " << absl::StrJoin(root_path,
" - ") <<
" ("
500 << root_path.size() <<
")";
501 VLOG(5) <<
" Spur: " << absl::StrJoin(spur_path,
" - ") <<
" ("
502 << spur_path.size() <<
")";
503 variant_path_queue.emplace(
504 path_length, std::move(new_path));
511 if (variant_path_queue.empty())
break;
514 variant_path_queue.top();
515 VLOG(5) <<
"> New path generated: "
516 << absl::StrJoin(next_shortest_path.
path(),
" - ") <<
" ("
517 << next_shortest_path.
path().size() <<
")";
518 paths.paths.emplace_back(next_shortest_path.
path());
519 paths.distances.push_back(next_shortest_path.
priority());
520 variant_path_queue.pop();