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 <queue>
17
18#include "graph.hpp"
19
20namespace nxpp {
21
41template <typename NodeID, typename Distance = double>
44 std::map<NodeID, Distance> distance;
46 std::map<NodeID, NodeID> predecessor;
47
49 [[nodiscard]] bool has_path_to(const NodeID& target) const {
50 const auto it = distance.find(target);
51 return it != distance.end() && it->second != std::numeric_limits<Distance>::max();
52 }
53
62 [[nodiscard]] std::vector<NodeID> path_to(const NodeID& target) const {
63 const auto distance_it = distance.find(target);
64 if (distance_it == distance.end()) {
65 throw std::runtime_error("Path reconstruction failed: target node not found in result.");
66 }
67 if (distance_it->second == std::numeric_limits<Distance>::max()) {
68 throw std::runtime_error("Path reconstruction failed: target node is unreachable.");
69 }
70
71 std::vector<NodeID> path;
72 NodeID current = target;
73 size_t hops = 0;
74 const size_t max_hops = predecessor.size() + 1;
75
76 while (true) {
77 path.push_back(current);
78
79 const auto pred_it = predecessor.find(current);
80 if (pred_it == predecessor.end()) {
81 throw std::runtime_error("Path reconstruction failed: predecessor map is incomplete.");
82 }
83 if (pred_it->second == current) {
84 break;
85 }
86
87 current = pred_it->second;
88 ++hops;
89 if (hops > max_hops) {
90 throw std::runtime_error("Path reconstruction failed: predecessor cycle detected.");
91 }
92 }
93
94 std::reverse(path.begin(), path.end());
95 return path;
96 }
97};
98
99template <typename Distance>
100Distance normalize_weighted_distance(Distance value) {
101 if constexpr (std::is_integral_v<Distance> && std::is_signed_v<Distance>) {
102 if (value == std::numeric_limits<Distance>::lowest()) {
103 return std::numeric_limits<Distance>::max();
104 }
105 }
106 return value;
107}
108
109template <typename Distance>
110using shortest_path_calc_type = std::conditional_t<std::is_integral_v<Distance>, double, Distance>;
111
112template <typename GraphWrapper, typename VertexDesc>
113std::vector<typename GraphWrapper::NodeType> reconstruct_vertex_path(
114 const GraphWrapper& graph,
115 VertexDesc source,
116 VertexDesc target,
117 const std::vector<VertexDesc>& predecessor
118) {
119 std::vector<typename GraphWrapper::NodeType> path;
120 const auto max_hops = predecessor.size() + 1;
121 size_t hops = 0;
122
123 for (VertexDesc current = target;; current = predecessor[graph.get_vertex_index(current)]) {
124 path.push_back(graph.get_node_id(current));
125 if (current == source) {
126 break;
127 }
128 ++hops;
129 if (hops > max_hops) {
130 throw std::runtime_error("Path reconstruction failed: predecessor cycle detected.");
131 }
132 }
133
134 std::reverse(path.begin(), path.end());
135 return path;
136}
137
138template <typename Distance, typename CalcDistance>
139Distance convert_shortest_path_distance(CalcDistance value) {
140 if constexpr (std::is_same_v<CalcDistance, Distance>) {
141 return normalize_weighted_distance(value);
142 } else {
143 if constexpr (std::is_integral_v<Distance> && std::is_floating_point_v<CalcDistance>) {
144 if (!std::isfinite(value) || value >= static_cast<CalcDistance>(std::numeric_limits<Distance>::max())) {
145 return std::numeric_limits<Distance>::max();
146 }
147 if (value <= static_cast<CalcDistance>(std::numeric_limits<Distance>::lowest())) {
148 return std::numeric_limits<Distance>::lowest();
149 }
150 }
151 if (value == std::numeric_limits<CalcDistance>::max()) {
152 return std::numeric_limits<Distance>::max();
153 }
154 return static_cast<Distance>(value);
155 }
156}
157
158inline WeightMode parse_shortest_path_weight_mode(const std::string& weight) {
159 if (weight.empty()) {
160 return WeightMode::Unweighted;
161 }
162 if (weight == "weight") {
163 return WeightMode::BuiltIn;
164 }
165 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
166}
167
168inline WeightMode parse_builtin_shortest_path_weight_mode(const std::string& weight) {
169 if (weight.empty() || weight == "weight") {
170 return WeightMode::BuiltIn;
171 }
172 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
173}
174
175// Algorithms: Shortest Paths
176
177template <typename GraphWrapper>
186[[deprecated("Use G.shortest_path(source, target) instead.")]]
187auto shortest_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
188 return G.shortest_path(source_id, target_id);
189}
190
191template <typename GraphWrapper>
192requires(GraphWrapper::has_builtin_edge_weight)
201[[deprecated("Use G.dijkstra_path(source, target) instead.")]]
202auto dijkstra_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
203 return G.dijkstra_path(source_id, target_id);
204}
205
206template <typename GraphWrapper>
207requires(GraphWrapper::has_builtin_edge_weight)
216[[deprecated("Use G.dijkstra_shortest_paths(source) instead.")]]
217auto dijkstra_shortest_paths(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
218 return G.dijkstra_shortest_paths(source_id);
219}
220
221template <typename GraphWrapper>
222requires(GraphWrapper::has_builtin_edge_weight)
231[[deprecated("Use G.dijkstra_shortest_paths(source) instead.")]]
232auto single_source_dijkstra(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
233 return G.dijkstra_shortest_paths(source_id);
234}
235
236template <typename GraphWrapper>
246[[deprecated("Use G.shortest_path(source, target, weight) instead.")]]
247auto shortest_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
248 return G.shortest_path(source_id, target_id, weight);
249}
250
251template <typename GraphWrapper>
252requires(GraphWrapper::has_builtin_edge_weight)
262[[deprecated("Use G.dijkstra_path(source, target, weight) instead.")]]
263auto dijkstra_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
264 return G.dijkstra_path(source_id, target_id, weight);
265}
266
267template <typename GraphWrapper>
276[[deprecated("Use G.shortest_path_length(source, target) instead.")]]
277double shortest_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
278 return G.shortest_path_length(source_id, target_id);
279}
280
281template <typename GraphWrapper>
291[[deprecated("Use G.shortest_path_length(source, target, weight) instead.")]]
292double shortest_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
293 return G.shortest_path_length(source_id, target_id, weight);
294}
295
296template <typename GraphWrapper>
297requires(GraphWrapper::has_builtin_edge_weight)
305[[deprecated("Use G.dijkstra_path_length(source) instead.")]]
306auto dijkstra_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
307 return G.dijkstra_path_length(source_id);
308}
309
310template <typename GraphWrapper>
311requires(GraphWrapper::has_builtin_edge_weight)
321[[deprecated("Use G.dijkstra_path_length(source, target) instead.")]]
322auto dijkstra_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
323 return G.dijkstra_path_length(source_id, target_id);
324}
325
326template <typename GraphWrapper>
327requires(GraphWrapper::has_builtin_edge_weight)
338[[deprecated("Use G.dijkstra_path_length(source, target, weight) instead.")]]
339auto dijkstra_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
340 return G.dijkstra_path_length(source_id, target_id, weight);
341}
342
343
344template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
346 if (id_to_bgl.find(source_id) == id_to_bgl.end() || id_to_bgl.find(target_id) == id_to_bgl.end()) {
347 throw std::runtime_error("Shortest-path lookup failed: source or target node not found.");
348 }
349
350 const VertexDesc source = id_to_bgl.at(source_id);
351 const VertexDesc target = id_to_bgl.at(target_id);
352
353 if (source == target) {
354 return std::vector<NodeID>{source_id};
355 }
356
357 const size_t n = boost::num_vertices(g);
358 const auto source_index = get_vertex_index(source);
359 const auto target_index = get_vertex_index(target);
360 std::vector<bool> visited(n, false);
361 std::vector<VertexDesc> pred(n);
362 std::queue<VertexDesc> q;
363
364 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
365 pred[get_vertex_index(*v)] = *v;
366 }
367
368 visited[source_index] = true;
369 q.push(source);
370
371 while (!q.empty()) {
372 const VertexDesc current = q.front();
373 q.pop();
374
375 for (auto [e, eend] = boost::out_edges(current, g); e != eend; ++e) {
376 const VertexDesc next = boost::target(*e, g);
377 const auto next_index = get_vertex_index(next);
378 if (!visited[next_index]) {
379 visited[next_index] = true;
380 pred[next_index] = current;
381 if (next == target) {
382 q = {};
383 break;
384 }
385 q.push(next);
386 }
387 }
388 }
389
390 if (!visited[target_index]) {
391 throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
392 }
393
394 return reconstruct_vertex_path(*this, source, target, pred);
395}
396
397template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
398auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::shortest_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
399 return shortest_path(source_id, target_id, parse_shortest_path_weight_mode(weight));
400}
401
402template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
404 if (mode == WeightMode::Unweighted) {
405 return shortest_path(source_id, target_id);
406 }
407 if (mode == WeightMode::BuiltIn) {
408 if constexpr (Weighted) {
409 return dijkstra_path(source_id, target_id);
410 } else {
411 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
412 }
413 }
414 throw std::runtime_error("Weight lookup failed: unsupported shortest-path weight mode.");
415}
416
417template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
419 const auto path = shortest_path(source_id, target_id);
420 if (path.empty()) {
421 throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
422 }
423 return static_cast<double>(path.size() - 1);
424}
425
426template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
427double Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::shortest_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
428 return shortest_path_length(source_id, target_id, parse_shortest_path_weight_mode(weight));
429}
430
431template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
433 if (mode == WeightMode::Unweighted) {
434 return shortest_path_length(source_id, target_id);
435 }
436 if (mode == WeightMode::BuiltIn) {
437 if constexpr (Weighted) {
438 return dijkstra_path_length(source_id, target_id);
439 } else {
440 throw std::runtime_error("Weight lookup failed: only the built-in edge weight property named 'weight' is supported.");
441 }
442 }
443 throw std::runtime_error("Weight lookup failed: unsupported shortest-path weight mode.");
444}
445
446template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
447template <bool W>
448requires(W)
450 if (id_to_bgl.find(source_id) == id_to_bgl.end() || id_to_bgl.find(target_id) == id_to_bgl.end()) {
451 throw std::runtime_error("Shortest-path lookup failed: source or target node not found.");
452 }
453
454 const VertexDesc source = id_to_bgl.at(source_id);
455 const VertexDesc target = id_to_bgl.at(target_id);
456 const size_t n = boost::num_vertices(g);
457 const auto target_index = get_vertex_index(target);
458 std::vector<EdgeWeight> dist(n);
459 std::vector<VertexDesc> pred(n);
460
461 boost::dijkstra_shortest_paths(
462 g,
463 source,
464 boost::distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
465 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
466 .vertex_index_map(vertex_index_map)
467 );
468
469 if (pred[target_index] == target && source != target) {
470 throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
471 }
472
473 return reconstruct_vertex_path(*this, source, target, pred);
474}
475
476template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
477template <bool W>
478requires(W)
479auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::dijkstra_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
480 return dijkstra_path(source_id, target_id, parse_builtin_shortest_path_weight_mode(weight));
481}
482
483template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
484template <bool W>
485requires(W)
486auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::dijkstra_path(const NodeID& source_id, const NodeID& target_id, WeightMode mode) const {
487 if (mode == WeightMode::Unweighted) {
488 return shortest_path(source_id, target_id);
489 }
490 if (mode == WeightMode::BuiltIn) {
491 return dijkstra_path(source_id, target_id);
492 }
493 throw std::runtime_error("Weight lookup failed: unsupported shortest-path weight mode.");
494}
495
496template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
497template <bool W>
498requires(W)
500 if (id_to_bgl.find(source_id) == id_to_bgl.end()) {
501 throw std::runtime_error("Shortest-path lookup failed: source node not found.");
502 }
503
504 const VertexDesc source = id_to_bgl.at(source_id);
505 const size_t n = boost::num_vertices(g);
506 std::vector<EdgeWeight> dist(n, std::numeric_limits<EdgeWeight>::max());
507 std::vector<VertexDesc> pred(n);
508 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
509 pred[get_vertex_index(*v)] = *v;
510 }
511
512 boost::dijkstra_shortest_paths(
513 g,
514 source,
515 boost::distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
516 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
517 .vertex_index_map(vertex_index_map)
518 );
519
521 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
522 const auto index = get_vertex_index(*v);
523 const auto& id = get_node_id(*v);
524 result.distance[id] = dist[index];
525 result.predecessor[id] = get_node_id(pred[index]);
526 }
527 return result;
528}
529
530template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
531template <bool W>
532requires(W)
534 return dijkstra_shortest_paths(source_id).distance;
535}
536
537template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
538template <bool W>
539requires(W)
541 const auto distances = dijkstra_path_length(source_id);
542 const auto it = distances.find(target_id);
543 if (it == distances.end()) {
544 throw std::runtime_error("Shortest-path lookup failed: target node not found.");
545 }
546 return it->second;
547}
548
549template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
550template <bool W>
551requires(W)
552auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::dijkstra_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
553 return dijkstra_path_length(source_id, target_id, parse_builtin_shortest_path_weight_mode(weight));
554}
555
556template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
557template <bool W>
558requires(W)
560 if (mode == WeightMode::Unweighted) {
561 return static_cast<EdgeWeight>(shortest_path_length(source_id, target_id));
562 }
563 if (mode == WeightMode::BuiltIn) {
564 return dijkstra_path_length(source_id, target_id);
565 }
566 throw std::runtime_error("Weight lookup failed: unsupported shortest-path weight mode.");
567}
568
569template <typename GraphWrapper>
570requires(GraphWrapper::has_builtin_edge_weight)
579[[deprecated("Use G.bellman_ford_path(source, target) instead.")]]
580auto bellman_ford_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
581 return G.bellman_ford_path(source_id, target_id);
582}
583
584template <typename GraphWrapper>
585requires(GraphWrapper::has_builtin_edge_weight)
594[[deprecated("Use G.bellman_ford_shortest_paths(source) instead.")]]
595auto bellman_ford_shortest_paths(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
596 return G.bellman_ford_shortest_paths(source_id);
597}
598
599template <typename GraphWrapper>
600requires(GraphWrapper::has_builtin_edge_weight)
609[[deprecated("Use G.bellman_ford_shortest_paths(source) instead.")]]
610auto single_source_bellman_ford(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
611 return G.bellman_ford_shortest_paths(source_id);
612}
613
614template <typename GraphWrapper>
615requires(GraphWrapper::has_builtin_edge_weight)
625[[deprecated("Use G.bellman_ford_path(source, target, weight) instead.")]]
626auto bellman_ford_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
627 return G.bellman_ford_path(source_id, target_id, weight);
628}
629
630template <typename GraphWrapper>
631requires(GraphWrapper::has_builtin_edge_weight)
641[[deprecated("Use G.bellman_ford_path_length(source, target) instead.")]]
642auto bellman_ford_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id) {
643 return G.bellman_ford_path_length(source_id, target_id);
644}
645
646template <typename GraphWrapper>
647requires(GraphWrapper::has_builtin_edge_weight)
658[[deprecated("Use G.bellman_ford_path_length(source, target, weight) instead.")]]
659auto bellman_ford_path_length(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& weight) {
660 return G.bellman_ford_path_length(source_id, target_id, weight);
661}
662
663template <typename GraphWrapper>
664requires(GraphWrapper::has_builtin_edge_weight)
673[[deprecated("Use G.dag_shortest_paths(source) instead.")]]
674auto dag_shortest_paths(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id) {
675 return G.dag_shortest_paths(source_id);
676}
677
678template <typename GraphWrapper>
679requires(GraphWrapper::has_builtin_edge_weight)
687[[deprecated("Use G.floyd_warshall_all_pairs_shortest_paths() instead.")]]
688auto floyd_warshall_all_pairs_shortest_paths(const GraphWrapper& G) {
689 return G.floyd_warshall_all_pairs_shortest_paths();
690}
691
692template <typename GraphWrapper>
693requires(GraphWrapper::has_builtin_edge_weight)
701[[deprecated("Use G.floyd_warshall_all_pairs_shortest_paths_map() instead.")]]
702auto floyd_warshall_all_pairs_shortest_paths_map(const GraphWrapper& G) {
703 return G.floyd_warshall_all_pairs_shortest_paths_map();
704}
705
706
707template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
708template <bool W>
709requires(W)
711 if (id_to_bgl.find(source_id) == id_to_bgl.end() || id_to_bgl.find(target_id) == id_to_bgl.end()) {
712 throw std::runtime_error("Shortest-path lookup failed: source or target node not found.");
713 }
714
715 const VertexDesc source = id_to_bgl.at(source_id);
716 const VertexDesc target = id_to_bgl.at(target_id);
717 const size_t n = boost::num_vertices(g);
718 const auto source_index = get_vertex_index(source);
719 const auto target_index = get_vertex_index(target);
720 std::vector<EdgeWeight> dist(n, std::numeric_limits<EdgeWeight>::max());
721 std::vector<VertexDesc> pred(n);
722 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) pred[get_vertex_index(*v)] = *v;
723 dist[source_index] = EdgeWeight{};
724
725 const bool ok = boost::bellman_ford_shortest_paths(
726 g,
727 static_cast<int>(n),
728 boost::weight_map(boost::get(boost::edge_weight, g))
729 .distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
730 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
731 .root_vertex(source)
732 );
733 if (!ok) throw std::runtime_error("Bellman-Ford failed: negative cycle detected.");
734 if (dist[target_index] == std::numeric_limits<EdgeWeight>::max()) throw std::runtime_error("Shortest-path lookup failed: target node is unreachable.");
735
736 return reconstruct_vertex_path(*this, source, target, pred);
737}
738
739template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
740template <bool W>
741requires(W)
743 if (id_to_bgl.find(source_id) == id_to_bgl.end()) {
744 throw std::runtime_error("Shortest-path lookup failed: source node not found.");
745 }
746
747 const VertexDesc source = id_to_bgl.at(source_id);
748 const size_t n = boost::num_vertices(g);
749 const auto source_index = get_vertex_index(source);
750 std::vector<EdgeWeight> dist(n, std::numeric_limits<EdgeWeight>::max());
751 std::vector<VertexDesc> pred(n);
752 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) pred[get_vertex_index(*v)] = *v;
753 dist[source_index] = EdgeWeight{};
754
755 const bool ok = boost::bellman_ford_shortest_paths(
756 g,
757 static_cast<int>(n),
758 boost::weight_map(boost::get(boost::edge_weight, g))
759 .distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
760 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
761 .root_vertex(source)
762 );
763 if (!ok) throw std::runtime_error("Bellman-Ford failed: negative cycle detected.");
764
766 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
767 const auto index = get_vertex_index(*v);
768 const auto& id = get_node_id(*v);
769 result.distance[id] = dist[index];
770 result.predecessor[id] = get_node_id(pred[index]);
771 }
772 return result;
773}
774
775template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
776template <bool W>
777requires(W)
778auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::bellman_ford_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const {
779 return bellman_ford_path(source_id, target_id, parse_builtin_shortest_path_weight_mode(weight));
780}
781
782template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
783template <bool W>
784requires(W)
786 if (mode == WeightMode::Unweighted) {
787 return shortest_path(source_id, target_id);
788 }
789 if (mode == WeightMode::BuiltIn) {
790 return bellman_ford_path(source_id, target_id);
791 }
792 throw std::runtime_error("Weight lookup failed: unsupported shortest-path weight mode.");
793}
794
795template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
796template <bool W>
797requires(W)
799 EdgeWeight total{};
800 const auto path = bellman_ford_path(source_id, target_id);
801 for (size_t i = 0; i + 1 < path.size(); ++i) total += get_edge_weight(path[i], path[i + 1]);
802 return total;
803}
804
805template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
806template <bool W>
807requires(W)
808auto 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 {
809 return bellman_ford_path_length(source_id, target_id, parse_builtin_shortest_path_weight_mode(weight));
810}
811
812template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
813template <bool W>
814requires(W)
816 if (mode == WeightMode::Unweighted) {
817 return static_cast<EdgeWeight>(shortest_path_length(source_id, target_id));
818 }
819 if (mode == WeightMode::BuiltIn) {
820 return bellman_ford_path_length(source_id, target_id);
821 }
822 throw std::runtime_error("Weight lookup failed: unsupported shortest-path weight mode.");
823}
824
825template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
826template <bool W>
827requires(W)
829 using CalcDistance = shortest_path_calc_type<EdgeWeight>;
830 if (id_to_bgl.find(source_id) == id_to_bgl.end()) throw std::runtime_error("Shortest-path lookup failed: source node not found.");
831
832 const VertexDesc source = id_to_bgl.at(source_id);
833 const size_t n = boost::num_vertices(g);
834 std::vector<CalcDistance> dist(n, std::numeric_limits<CalcDistance>::max());
835 std::vector<VertexDesc> pred(n);
836 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) pred[get_vertex_index(*v)] = *v;
837
838 boost::dag_shortest_paths(
839 g, source,
840 boost::distance_map(boost::make_iterator_property_map(dist.begin(), vertex_index_map))
841 .predecessor_map(boost::make_iterator_property_map(pred.begin(), vertex_index_map))
842 .distance_compare(std::less<CalcDistance>())
843 .distance_combine(boost::closed_plus<CalcDistance>(std::numeric_limits<CalcDistance>::max()))
844 .distance_inf(std::numeric_limits<CalcDistance>::max())
845 .distance_zero(CalcDistance{})
846 );
847
848 std::vector<EdgeWeight> normalized_dist(n);
849 for (size_t i = 0; i < n; ++i) normalized_dist[i] = convert_shortest_path_distance<EdgeWeight>(dist[i]);
850
852 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
853 const auto index = get_vertex_index(*v);
854 const auto& id = get_node_id(*v);
855 result.distance[id] = normalized_dist[index];
856 result.predecessor[id] = get_node_id(pred[index]);
857 }
858 return result;
859}
860
861template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
863 using CalcDistance = std::conditional_t<std::is_integral_v<EdgeWeight>, long long, EdgeWeight>;
864 const size_t n = boost::num_vertices(g);
865 const CalcDistance inf = std::numeric_limits<CalcDistance>::max() / 4;
866 std::vector<std::vector<CalcDistance>> internal_matrix(n, std::vector<CalcDistance>(n, inf));
867 for (size_t i = 0; i < n; ++i) internal_matrix[i][i] = CalcDistance{};
868
869 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
870 const size_t u = get_vertex_index(boost::source(*e, g));
871 const size_t v = get_vertex_index(boost::target(*e, g));
872 const CalcDistance w = static_cast<CalcDistance>(boost::get(boost::edge_weight, g, *e));
873 internal_matrix[u][v] = std::min(internal_matrix[u][v], w);
874 if constexpr (!Directed) internal_matrix[v][u] = std::min(internal_matrix[v][u], w);
875 }
876
877 for (size_t k = 0; k < n; ++k) for (size_t i = 0; i < n; ++i) if (internal_matrix[i][k] != inf)
878 for (size_t j = 0; j < n; ++j) if (internal_matrix[k][j] != inf) {
879 const CalcDistance through_k = internal_matrix[i][k] + internal_matrix[k][j];
880 if (through_k < internal_matrix[i][j]) internal_matrix[i][j] = through_k;
881 }
882
883 std::vector<size_t> order(n);
884 for (size_t i = 0; i < n; ++i) order[i] = i;
885 if constexpr (std::is_arithmetic_v<NodeID> || std::is_same_v<NodeID, std::string>) {
886 std::sort(order.begin(), order.end(), [&](size_t lhs, size_t rhs) { return bgl_to_id[lhs] < bgl_to_id[rhs]; });
887 }
888
889 std::vector<std::vector<EdgeWeight>> matrix(n, std::vector<EdgeWeight>(n));
890 for (size_t i = 0; i < n; ++i) for (size_t j = 0; j < n; ++j)
891 matrix[i][j] = internal_matrix[order[i]][order[j]] == inf ? std::numeric_limits<EdgeWeight>::max() : static_cast<EdgeWeight>(internal_matrix[order[i]][order[j]]);
892 return std::pair{std::move(matrix), std::move(order)};
893}
894
895template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
896template <bool W>
897requires(W)
899 auto [matrix, order] = floyd_warshall_matrix_with_order();
900 (void)order;
901 return matrix;
902}
903
904template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
905template <bool W>
906requires(W)
908 const auto [matrix, order] = floyd_warshall_matrix_with_order();
909 const size_t n = matrix.size();
910 std::map<NodeID, std::map<NodeID, EdgeWeight>> result;
911 for (size_t i = 0; i < n; ++i) {
912 for (size_t j = 0; j < n; ++j) {
913 result[bgl_to_id[order[i]]][bgl_to_id[order[j]]] = matrix[i][j];
914 }
915 }
916 return result;
917}
918} // namespace nxpp
Graph wrapper around Boost Graph Library with Python-inspired helpers.
Definition graph.hpp:288
auto shortest_path(const NodeID &source_id, const NodeID &target_id) const
Computes an unweighted shortest path between two nodes.
Definition shortest_paths.hpp:345
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:418
Core graph wrapper, proxy surface, and public alias presets.
WeightMode
Explicit source-target shortest-path weighting mode.
Definition graph.hpp:117
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:187
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:277
Result container for single-source shortest-path routines.
Definition shortest_paths.hpp:42
std::vector< NodeID > path_to(const NodeID &target) const
Reconstructs the path from the original source to target.
Definition shortest_paths.hpp:62
bool has_path_to(const NodeID &target) const
Returns true when the target exists in the result and is reachable.
Definition shortest_paths.hpp:49
std::map< NodeID, NodeID > predecessor
Predecessor tree keyed by target node ID.
Definition shortest_paths.hpp:46
std::map< NodeID, Distance > distance
Final shortest-path distance for each node known to the result.
Definition shortest_paths.hpp:44