nxpp
Header-only graph utilities on top of Boost Graph Library
Loading...
Searching...
No Matches
shortest_paths.hpp
Go to the documentation of this file.
1#pragma once
2
11#include <boost/graph/dijkstra_shortest_paths.hpp>
12#include <boost/graph/bellman_ford_shortest_paths.hpp>
13#include <boost/graph/dag_shortest_paths.hpp>
14#include <boost/graph/floyd_warshall_shortest.hpp>
15
16#include "graph.hpp"
17
18namespace nxpp {
19
39template <typename NodeID, typename Distance = double>
42 std::map<NodeID, Distance> distance;
44 std::map<NodeID, NodeID> predecessor;
45
47 bool has_path_to(const NodeID& target) const {
48 const auto it = distance.find(target);
49 return it != distance.end() && it->second != std::numeric_limits<Distance>::max();
50 }
51
60 std::vector<NodeID> path_to(const NodeID& target) const {
61 const auto distance_it = distance.find(target);
62 if (distance_it == distance.end()) {
63 throw std::runtime_error("Path reconstruction failed: target node not found in result.");
64 }
65 if (distance_it->second == std::numeric_limits<Distance>::max()) {
66 throw std::runtime_error("Path reconstruction failed: target node is unreachable.");
67 }
68
69 std::vector<NodeID> path;
70 NodeID current = target;
71 size_t hops = 0;
72 const size_t max_hops = predecessor.size() + 1;
73
74 while (true) {
75 path.push_back(current);
76
77 const auto pred_it = predecessor.find(current);
78 if (pred_it == predecessor.end()) {
79 throw std::runtime_error("Path reconstruction failed: predecessor map is incomplete.");
80 }
81 if (pred_it->second == current) {
82 break;
83 }
84
85 current = pred_it->second;
86 ++hops;
87 if (hops > max_hops) {
88 throw std::runtime_error("Path reconstruction failed: predecessor cycle detected.");
89 }
90 }
91
92 std::reverse(path.begin(), path.end());
93 return path;
94 }
95};
96
97template <typename Distance>
98Distance normalize_weighted_distance(Distance value) {
99 if constexpr (std::is_integral_v<Distance> && std::is_signed_v<Distance>) {
100 if (value == std::numeric_limits<Distance>::lowest()) {
101 return std::numeric_limits<Distance>::max();
102 }
103 }
104 return value;
105}
106
107template <typename Distance>
108using shortest_path_calc_type = std::conditional_t<std::is_integral_v<Distance>, double, Distance>;
109
110template <typename Distance, typename CalcDistance>
111Distance convert_shortest_path_distance(CalcDistance value) {
112 if constexpr (std::is_same_v<CalcDistance, Distance>) {
113 return normalize_weighted_distance(value);
114 } else {
115 if constexpr (std::is_integral_v<Distance> && std::is_floating_point_v<CalcDistance>) {
116 if (!std::isfinite(value) || value >= static_cast<CalcDistance>(std::numeric_limits<Distance>::max())) {
117 return std::numeric_limits<Distance>::max();
118 }
119 if (value <= static_cast<CalcDistance>(std::numeric_limits<Distance>::lowest())) {
120 return std::numeric_limits<Distance>::lowest();
121 }
122 }
123 if (value == std::numeric_limits<CalcDistance>::max()) {
124 return std::numeric_limits<Distance>::max();
125 }
126 return static_cast<Distance>(value);
127 }
128}
129
130// Algorithms: Shortest Paths
131
132template <typename GraphWrapper>
141[[deprecated("Use G.shortest_path(source, target) instead.")]]
142auto shortest_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
143 return G.shortest_path(source_id, target_id);
144}
145
146template <typename GraphWrapper>
147requires(GraphWrapper::has_builtin_edge_weight)
156[[deprecated("Use G.dijkstra_path(source, target) instead.")]]
157auto dijkstra_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
158 return G.dijkstra_path(source_id, target_id);
159}
160
161template <typename GraphWrapper>
162requires(GraphWrapper::has_builtin_edge_weight)
171[[deprecated("Use G.dijkstra_shortest_paths(source) instead.")]]
172auto dijkstra_shortest_paths(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
173 return G.dijkstra_shortest_paths(source_id);
174}
175
176template <typename GraphWrapper>
177requires(GraphWrapper::has_builtin_edge_weight)
186[[deprecated("Use G.dijkstra_shortest_paths(source) instead.")]]
187auto single_source_dijkstra(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
188 return G.dijkstra_shortest_paths(source_id);
189}
190
191template <typename GraphWrapper>
201[[deprecated("Use G.shortest_path(source, target, weight) instead.")]]
202auto shortest_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
203 return G.shortest_path(source_id, target_id, weight);
204}
205
206template <typename GraphWrapper>
207requires(GraphWrapper::has_builtin_edge_weight)
217[[deprecated("Use G.dijkstra_path(source, target, weight) instead.")]]
218auto dijkstra_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
219 return G.dijkstra_path(source_id, target_id, weight);
220}
221
222template <typename GraphWrapper>
231[[deprecated("Use G.shortest_path_length(source, target) instead.")]]
232double shortest_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
233 return G.shortest_path_length(source_id, target_id);
234}
235
236template <typename GraphWrapper>
246[[deprecated("Use G.shortest_path_length(source, target, weight) instead.")]]
247double shortest_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
248 return G.shortest_path_length(source_id, target_id, weight);
249}
250
251template <typename GraphWrapper>
252requires(GraphWrapper::has_builtin_edge_weight)
260[[deprecated("Use G.dijkstra_path_length(source) instead.")]]
261auto dijkstra_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
262 return G.dijkstra_path_length(source_id);
263}
264
265template <typename GraphWrapper>
266requires(GraphWrapper::has_builtin_edge_weight)
276[[deprecated("Use G.dijkstra_path_length(source, target) instead.")]]
277auto dijkstra_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
278 return G.dijkstra_path_length(source_id, target_id);
279}
280
281template <typename GraphWrapper>
282requires(GraphWrapper::has_builtin_edge_weight)
293[[deprecated("Use G.dijkstra_path_length(source, target, weight) instead.")]]
294auto dijkstra_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
295 return G.dijkstra_path_length(source_id, target_id, weight);
296}
297
298
299template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
301 if (id_to_bgl.find(source_id) == id_to_bgl.end() || id_to_bgl.find(target_id) == id_to_bgl.end()) {
302 throw std::runtime_error("Shortest-path lookup failed: source or target node not found.");
303 }
304
305 const VertexDesc source = id_to_bgl.at(source_id);
306 const VertexDesc target = id_to_bgl.at(target_id);
307
308 if (source == target) {
309 return std::vector<NodeID>{source_id};
310 }
311
312 const size_t n = boost::num_vertices(g);
313 const auto source_index = get_vertex_index(source);
314 const auto target_index = get_vertex_index(target);
315 std::vector<bool> visited(n, false);
316 std::vector<VertexDesc> pred(n);
317 std::queue<VertexDesc> q;
318
319 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
320 pred[get_vertex_index(*v)] = *v;
321 }
322
323 visited[source_index] = true;
324 q.push(source);
325
326 while (!q.empty()) {
327 const VertexDesc current = q.front();
328 q.pop();
329
330 for (auto [e, eend] = boost::out_edges(current, g); e != eend; ++e) {
331 const VertexDesc next = boost::target(*e, g);
332 const auto next_index = get_vertex_index(next);
333 if (!visited[next_index]) {
334 visited[next_index] = true;
335 pred[next_index] = current;
336 if (next == target) {
337 q = {};
338 break;
339 }
340 q.push(next);
341 }
342 }
343 }
344
345 if (!visited[target_index]) {
346 throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
347 }
348
349 std::vector<NodeID> path;
350 for (VertexDesc curr = target; curr != source; curr = pred[get_vertex_index(curr)]) {
351 path.push_back(get_node_id(curr));
352 }
353 path.push_back(get_node_id(source));
354 std::reverse(path.begin(), path.end());
355 return path;
356}
357
358template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
359auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::shortest_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
360 if (weight.empty()) {
361 return shortest_path(source_id, target_id);
362 }
363 if (weight == "weight") {
364 if constexpr (Weighted) {
365 return dijkstra_path(source_id, target_id);
366 } else {
367 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
368 }
369 }
370 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
371}
372
373template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
375 const auto path = shortest_path(source_id, target_id);
376 if (path.empty()) {
377 throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
378 }
379 return static_cast<double>(path.size() - 1);
380}
381
382template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
383double Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::shortest_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
384 if (weight.empty()) {
385 return shortest_path_length(source_id, target_id);
386 }
387 if (weight == "weight") {
388 if constexpr (Weighted) {
389 return dijkstra_path_length(source_id, target_id);
390 } else {
391 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
392 }
393 }
394 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
395}
396
397template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
398template <bool W>
399requires(W)
401 if (id_to_bgl.find(source_id) == id_to_bgl.end() || id_to_bgl.find(target_id) == id_to_bgl.end()) {
402 throw std::runtime_error("Shortest-path lookup failed: source or target node not found.");
403 }
404
405 const VertexDesc source = id_to_bgl.at(source_id);
406 const VertexDesc target = id_to_bgl.at(target_id);
407 const size_t n = boost::num_vertices(g);
408 const auto target_index = get_vertex_index(target);
409 std::vector<EdgeWeight> dist(n);
410 std::vector<VertexDesc> pred(n);
411
412 boost::dijkstra_shortest_paths(
413 g,
414 source,
415 boost::distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
416 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
417 .vertex_index_map(vertex_index_map)
418 );
419
420 if (pred[target_index] == target && source != target) {
421 throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
422 }
423
424 std::vector<NodeID> path;
425 for (VertexDesc curr = target; curr != source; curr = pred[get_vertex_index(curr)]) {
426 path.push_back(get_node_id(curr));
427 }
428 path.push_back(get_node_id(source));
429 std::reverse(path.begin(), path.end());
430 return path;
431}
432
433template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
434template <bool W>
435requires(W)
436auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::dijkstra_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
437 if (weight.empty() || weight == "weight") {
438 return dijkstra_path(source_id, target_id);
439 }
440 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
441}
442
443template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
444template <bool W>
445requires(W)
447 if (id_to_bgl.find(source_id) == id_to_bgl.end()) {
448 throw std::runtime_error("Shortest-path lookup failed: source node not found.");
449 }
450
451 const VertexDesc source = id_to_bgl.at(source_id);
452 const size_t n = boost::num_vertices(g);
453 std::vector<EdgeWeight> dist(n, std::numeric_limits<EdgeWeight>::max());
454 std::vector<VertexDesc> pred(n);
455 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
456 pred[get_vertex_index(*v)] = *v;
457 }
458
459 boost::dijkstra_shortest_paths(
460 g,
461 source,
462 boost::distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
463 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
464 .vertex_index_map(vertex_index_map)
465 );
466
468 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
469 const auto index = get_vertex_index(*v);
470 const auto& id = get_node_id(*v);
471 result.distance[id] = dist[index];
472 result.predecessor[id] = get_node_id(pred[index]);
473 }
474 return result;
475}
476
477template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
478template <bool W>
479requires(W)
481 return dijkstra_shortest_paths(source_id).distance;
482}
483
484template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
485template <bool W>
486requires(W)
488 const auto distances = dijkstra_path_length(source_id);
489 const auto it = distances.find(target_id);
490 if (it == distances.end()) {
491 throw std::runtime_error("Shortest-path lookup failed: target node not found.");
492 }
493 return it->second;
494}
495
496template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
497template <bool W>
498requires(W)
499auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::dijkstra_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
500 if (weight.empty() || weight == "weight") {
501 return dijkstra_path_length(source_id, target_id);
502 }
503 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
504}
505
506template <typename GraphWrapper>
507requires(GraphWrapper::has_builtin_edge_weight)
516[[deprecated("Use G.bellman_ford_path(source, target) instead.")]]
517auto bellman_ford_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
518 return G.bellman_ford_path(source_id, target_id);
519}
520
521template <typename GraphWrapper>
522requires(GraphWrapper::has_builtin_edge_weight)
531[[deprecated("Use G.bellman_ford_shortest_paths(source) instead.")]]
532auto bellman_ford_shortest_paths(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
533 return G.bellman_ford_shortest_paths(source_id);
534}
535
536template <typename GraphWrapper>
537requires(GraphWrapper::has_builtin_edge_weight)
546[[deprecated("Use G.bellman_ford_shortest_paths(source) instead.")]]
547auto single_source_bellman_ford(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
548 return G.bellman_ford_shortest_paths(source_id);
549}
550
551template <typename GraphWrapper>
552requires(GraphWrapper::has_builtin_edge_weight)
562[[deprecated("Use G.bellman_ford_path(source, target, weight) instead.")]]
563auto bellman_ford_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
564 return G.bellman_ford_path(source_id, target_id, weight);
565}
566
567template <typename GraphWrapper>
568requires(GraphWrapper::has_builtin_edge_weight)
578[[deprecated("Use G.bellman_ford_path_length(source, target) instead.")]]
579auto bellman_ford_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
580 return G.bellman_ford_path_length(source_id, target_id);
581}
582
583template <typename GraphWrapper>
584requires(GraphWrapper::has_builtin_edge_weight)
595[[deprecated("Use G.bellman_ford_path_length(source, target, weight) instead.")]]
596auto bellman_ford_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
597 return G.bellman_ford_path_length(source_id, target_id, weight);
598}
599
600template <typename GraphWrapper>
601requires(GraphWrapper::has_builtin_edge_weight)
610[[deprecated("Use G.dag_shortest_paths(source) instead.")]]
611auto dag_shortest_paths(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
612 return G.dag_shortest_paths(source_id);
613}
614
615template <typename GraphWrapper>
616requires(GraphWrapper::has_builtin_edge_weight)
624[[deprecated("Use G.floyd_warshall_all_pairs_shortest_paths() instead.")]]
625auto floyd_warshall_all_pairs_shortest_paths(const GraphWrapper& G) {
626 return G.floyd_warshall_all_pairs_shortest_paths();
627}
628
629template <typename GraphWrapper>
630requires(GraphWrapper::has_builtin_edge_weight)
638[[deprecated("Use G.floyd_warshall_all_pairs_shortest_paths_map() instead.")]]
639auto floyd_warshall_all_pairs_shortest_paths_map(const GraphWrapper& G) {
640 return G.floyd_warshall_all_pairs_shortest_paths_map();
641}
642
643
644template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
645template <bool W>
646requires(W)
648 if (id_to_bgl.find(source_id) == id_to_bgl.end() || id_to_bgl.find(target_id) == id_to_bgl.end()) {
649 throw std::runtime_error("Shortest-path lookup failed: source or target node not found.");
650 }
651
652 const VertexDesc source = id_to_bgl.at(source_id);
653 const VertexDesc target = id_to_bgl.at(target_id);
654 const size_t n = boost::num_vertices(g);
655 const auto source_index = get_vertex_index(source);
656 const auto target_index = get_vertex_index(target);
657 std::vector<EdgeWeight> dist(n, std::numeric_limits<EdgeWeight>::max());
658 std::vector<VertexDesc> pred(n);
659 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) pred[get_vertex_index(*v)] = *v;
660 dist[source_index] = EdgeWeight{};
661
662 const bool ok = boost::bellman_ford_shortest_paths(
663 g,
664 static_cast<int>(n),
665 boost::weight_map(boost::get(boost::edge_weight, g))
666 .distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
667 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
668 .root_vertex(source)
669 );
670 if (!ok) throw std::runtime_error("Bellman-Ford failed: negative cycle detected.");
671 if (dist[target_index] == std::numeric_limits<EdgeWeight>::max()) throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
672
673 std::vector<NodeID> path;
674 for (VertexDesc curr = target; curr != source; curr = pred[get_vertex_index(curr)]) path.push_back(get_node_id(curr));
675 path.push_back(get_node_id(source));
676 std::reverse(path.begin(), path.end());
677 return path;
678}
679
680template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
681template <bool W>
682requires(W)
684 if (id_to_bgl.find(source_id) == id_to_bgl.end()) {
685 throw std::runtime_error("Shortest-path lookup failed: source node not found.");
686 }
687
688 const VertexDesc source = id_to_bgl.at(source_id);
689 const size_t n = boost::num_vertices(g);
690 const auto source_index = get_vertex_index(source);
691 std::vector<EdgeWeight> dist(n, std::numeric_limits<EdgeWeight>::max());
692 std::vector<VertexDesc> pred(n);
693 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) pred[get_vertex_index(*v)] = *v;
694 dist[source_index] = EdgeWeight{};
695
696 const bool ok = boost::bellman_ford_shortest_paths(
697 g,
698 static_cast<int>(n),
699 boost::weight_map(boost::get(boost::edge_weight, g))
700 .distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
701 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
702 .root_vertex(source)
703 );
704 if (!ok) throw std::runtime_error("Bellman-Ford failed: negative cycle detected.");
705
707 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
708 const auto index = get_vertex_index(*v);
709 const auto& id = get_node_id(*v);
710 result.distance[id] = dist[index];
711 result.predecessor[id] = get_node_id(pred[index]);
712 }
713 return result;
714}
715
716template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
717template <bool W>
718requires(W)
719auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::bellman_ford_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
720 if (weight.empty() || weight == "weight") return bellman_ford_path(source_id, target_id);
721 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
722}
723
724template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
725template <bool W>
726requires(W)
728 EdgeWeight total{};
729 const auto path = bellman_ford_path(source_id, target_id);
730 for (size_t i = 0; i + 1 < path.size(); ++i) total += get_edge_weight(path[i], path[i + 1]);
731 return total;
732}
733
734template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
735template <bool W>
736requires(W)
737auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::bellman_ford_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
738 if (weight.empty() || weight == "weight") return bellman_ford_path_length(source_id, target_id);
739 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
740}
741
742template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
743template <bool W>
744requires(W)
746 using CalcDistance = shortest_path_calc_type<EdgeWeight>;
747 if (id_to_bgl.find(source_id) == id_to_bgl.end()) throw std::runtime_error("Shortest-path lookup failed: source node not found.");
748
749 const VertexDesc source = id_to_bgl.at(source_id);
750 const size_t n = boost::num_vertices(g);
751 std::vector<CalcDistance> dist(n, std::numeric_limits<CalcDistance>::max());
752 std::vector<VertexDesc> pred(n);
753 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) pred[get_vertex_index(*v)] = *v;
754
755 boost::dag_shortest_paths(
756 g, source,
757 boost::distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
758 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
759 .distance_compare(std::less<CalcDistance>())
760 .distance_combine(boost::closed_plus<CalcDistance>(std::numeric_limits<CalcDistance>::max()))
761 .distance_inf(std::numeric_limits<CalcDistance>::max())
762 .distance_zero(CalcDistance{})
763 );
764
765 std::vector<EdgeWeight> normalized_dist(n);
766 for (size_t i = 0; i < n; ++i) normalized_dist[i] = convert_shortest_path_distance<EdgeWeight>(dist[i]);
767
769 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
770 const auto index = get_vertex_index(*v);
771 const auto& id = get_node_id(*v);
772 result.distance[id] = normalized_dist[index];
773 result.predecessor[id] = get_node_id(pred[index]);
774 }
775 return result;
776}
777
778template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
779template <bool W>
780requires(W)
782 using CalcDistance = std::conditional_t<std::is_integral_v<EdgeWeight>, long long, EdgeWeight>;
783 const size_t n = boost::num_vertices(g);
784 const CalcDistance inf = std::numeric_limits<CalcDistance>::max() / 4;
785 std::vector<std::vector<CalcDistance>> internal_matrix(n, std::vector<CalcDistance>(n, inf));
786 for (size_t i = 0; i < n; ++i) internal_matrix[i][i] = CalcDistance{};
787
788 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
789 const size_t u = get_vertex_index(boost::source(*e, g));
790 const size_t v = get_vertex_index(boost::target(*e, g));
791 const CalcDistance w = static_cast<CalcDistance>(boost::get(boost::edge_weight, g, *e));
792 internal_matrix[u][v] = std::min(internal_matrix[u][v], w);
793 if constexpr (!Directed) internal_matrix[v][u] = std::min(internal_matrix[v][u], w);
794 }
795
796 for (size_t k = 0; k < n; ++k) for (size_t i = 0; i < n; ++i) if (internal_matrix[i][k] != inf)
797 for (size_t j = 0; j < n; ++j) if (internal_matrix[k][j] != inf) {
798 const CalcDistance through_k = internal_matrix[i][k] + internal_matrix[k][j];
799 if (through_k < internal_matrix[i][j]) internal_matrix[i][j] = through_k;
800 }
801
802 std::vector<size_t> order(n);
803 for (size_t i = 0; i < n; ++i) order[i] = i;
804 if constexpr (std::is_arithmetic_v<NodeID> || std::is_same_v<NodeID, std::string>) {
805 std::sort(order.begin(), order.end(), [&](size_t lhs, size_t rhs) { return bgl_to_id[lhs] < bgl_to_id[rhs]; });
806 }
807
808 std::vector<std::vector<EdgeWeight>> matrix(n, std::vector<EdgeWeight>(n));
809 for (size_t i = 0; i < n; ++i) for (size_t j = 0; j < n; ++j)
810 matrix[i][j] = internal_matrix[order[i]][order[j]] == inf ? std::numeric_limits<EdgeWeight>::max() : static_cast<EdgeWeight>(internal_matrix[order[i]][order[j]]);
811 return matrix;
812}
813
814template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
815template <bool W>
816requires(W)
818 const size_t n = boost::num_vertices(g);
819 std::vector<size_t> order(n);
820 for (size_t i = 0; i < n; ++i) order[i] = i;
821 if constexpr (std::is_arithmetic_v<NodeID> || std::is_same_v<NodeID, std::string>) {
822 std::sort(order.begin(), order.end(), [&](size_t lhs, size_t rhs) { return bgl_to_id[lhs] < bgl_to_id[rhs]; });
823 }
824 const auto matrix = floyd_warshall_all_pairs_shortest_paths();
825 std::map<NodeID, std::map<NodeID, EdgeWeight>> result;
826 for (size_t i = 0; i < n; ++i) {
827 for (size_t j = 0; j < n; ++j) {
828 result[bgl_to_id[order[i]]][bgl_to_id[order[j]]] = matrix[i][j];
829 }
830 }
831 return result;
832}
833} // namespace nxpp
Graph wrapper around Boost Graph Library with Python-inspired helpers.
Definition graph.hpp:272
auto shortest_path(const NodeID &source_id, const NodeID &target_id) const
Computes an unweighted shortest path between two nodes.
Definition shortest_paths.hpp:300
double shortest_path_length(const NodeID &source_id, const NodeID &target_id) const
Returns the length of an unweighted shortest path.
Definition shortest_paths.hpp:374
Core graph wrapper, proxy surface, and public alias presets.
auto shortest_path(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id)
Deprecated free-function alias for G.shortest_path(source, target).
Definition shortest_paths.hpp:142
double shortest_path_length(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id)
Deprecated free-function alias for G.shortest_path_length(source, target).
Definition shortest_paths.hpp:232
Result container for single-source shortest-path routines.
Definition shortest_paths.hpp:40
std::vector< NodeID > path_to(const NodeID &target) const
Reconstructs the path from the original source to target.
Definition shortest_paths.hpp:60
bool has_path_to(const NodeID &target) const
Returns true when the target exists in the result and is reachable.
Definition shortest_paths.hpp:47
std::map< NodeID, NodeID > predecessor
Predecessor tree keyed by target node ID.
Definition shortest_paths.hpp:44
std::map< NodeID, Distance > distance
Final shortest-path distance for each node known to the result.
Definition shortest_paths.hpp:42