nxpp
Header-only graph utilities on top of Boost Graph Library
Loading...
Searching...
No Matches
graph.hpp
Go to the documentation of this file.
1#pragma once
2
11#if !__has_include(<boost/graph/adjacency_list.hpp>)
12#error "FATAL: nxpp requires the Boost Graph Library (BGL)."
13#endif
14
15#if defined(__GNUC__) && !defined(__clang__)
16#pragma GCC diagnostic push
17#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
18#endif
19#include <boost/graph/adjacency_list.hpp>
20#include <boost/functional/hash.hpp>
21#if defined(__GNUC__) && !defined(__clang__)
22#pragma GCC diagnostic pop
23#endif
24#include <functional>
25#include <vector>
26#include <stdexcept>
27#include <tuple>
28#include <type_traits>
29#include <string>
30#include <algorithm>
31#include <cmath>
32#include <concepts>
33#include <utility>
34#include <iostream>
35#include <any>
36#include <map>
37#include <random>
38#include <optional>
39#include <limits>
40#include <queue>
41#include <memory>
42
43namespace nxpp {
44
45template <
46 typename NodeID,
47 typename EdgeWeight,
48 bool Directed,
49 bool Multi,
50 bool Weighted,
51 typename OutEdgeSelector,
52 typename VertexSelector
53>
54class Graph;
55
56namespace detail {
57
58template <typename GraphWrapper>
60 static void invalidate(const void*) {}
61 static void clear(const void*) {}
62};
63
64} // namespace detail
65
66} // namespace nxpp
67
68namespace boost {
69
70enum vertex_wrapper_index_t { vertex_wrapper_index };
71BOOST_INSTALL_PROPERTY(vertex, wrapper_index);
72
73} // namespace boost
74
75namespace nxpp {
76
77template <typename GraphType, bool HasWeight>
79
80namespace detail {
81
82template <typename T>
84 requires(const T& lhs, const T& rhs) {
85 { std::less<T>{}(lhs, rhs) } -> std::convertible_to<bool>;
86 };
87
88} // namespace detail
89
90template <typename GraphType>
91struct built_in_weight_traits<GraphType, true> {
92 using map_type = typename boost::property_map<GraphType, boost::edge_weight_t>::type;
93
94 static map_type get(GraphType& g) {
95 return boost::get(boost::edge_weight, g);
96 }
97};
98
99template <typename GraphType>
100struct built_in_weight_traits<GraphType, false> {
101 struct map_type {};
102
103 static map_type get(GraphType&) {
104 return {};
105 }
106};
107
108template <typename Key, typename Value>
110public:
111 using storage_type = std::map<Key, Value>;
112 using iterator = typename storage_type::iterator;
113 using const_iterator = typename storage_type::const_iterator;
114
115 Value& operator[](const Key& key) {
116 return data[key];
117 }
118
119 const Value& operator[](const Key& key) const {
120 return data.at(key);
121 }
122
123 Value& at(const Key& key) {
124 return data.at(key);
125 }
126
127 const Value& at(const Key& key) const {
128 return data.at(key);
129 }
130
131 iterator begin() { return data.begin(); }
132 iterator end() { return data.end(); }
133 const_iterator begin() const { return data.begin(); }
134 const_iterator end() const { return data.end(); }
135 const_iterator cbegin() const { return data.cbegin(); }
136 const_iterator cend() const { return data.cend(); }
137
138private:
139 storage_type data;
140};
141
142template <typename Key, typename Value>
144public:
145 using storage_type = std::vector<std::pair<Key, Value>>;
146 using iterator = typename storage_type::iterator;
147 using const_iterator = typename storage_type::const_iterator;
148
158
159 void reserve(std::size_t count) {
160 data.reserve(count);
161 }
162
163 void push_back(const Key& key, const Value& value) {
164 data.emplace_back(key, value);
165 }
166
167 void push_back(const Key& key, Value&& value) {
168 data.emplace_back(key, std::move(value));
169 }
170
171 Value& at(const Key& key) {
172 auto it = find(key);
173 if (it == data.end()) {
174 throw std::out_of_range("indexed_lookup_map::at");
175 }
176 return it->second;
177 }
178
179 const Value& at(const Key& key) const {
180 auto it = find(key);
181 if (it == data.end()) {
182 throw std::out_of_range("indexed_lookup_map::at");
183 }
184 return it->second;
185 }
186
187 Value& operator[](const Key& key) {
188 return at(key);
189 }
190
191 const Value& operator[](const Key& key) const {
192 return at(key);
193 }
194
195 bool contains(const Key& key) const {
196 return find(key) != data.end();
197 }
198
199 iterator find(const Key& key) {
200 auto it = lower_bound_for(key);
201 if (it == data.end() || key < it->first) {
202 return data.end();
203 }
204 return it;
205 }
206
207 const_iterator find(const Key& key) const {
208 auto it = lower_bound_for(key);
209 if (it == data.end() || key < it->first) {
210 return data.end();
211 }
212 return it;
213 }
214
215 iterator begin() { return data.begin(); }
216 iterator end() { return data.end(); }
217 const_iterator begin() const { return data.begin(); }
218 const_iterator end() const { return data.end(); }
219 const_iterator cbegin() const { return data.cbegin(); }
220 const_iterator cend() const { return data.cend(); }
221
222 bool empty() const { return data.empty(); }
223 std::size_t size() const { return data.size(); }
224
225private:
226 iterator lower_bound_for(const Key& key) {
227 return std::lower_bound(
228 data.begin(),
229 data.end(),
230 key,
231 [](const auto& entry, const Key& value) { return entry.first < value; }
232 );
233 }
234
235 const_iterator lower_bound_for(const Key& key) const {
236 return std::lower_bound(
237 data.begin(),
238 data.end(),
239 key,
240 [](const auto& entry, const Key& value) { return entry.first < value; }
241 );
242 }
243
244 storage_type data;
245};
246
247// Core Graph Class
248
249template <
250 typename NodeID = std::string,
251 typename EdgeWeight = double,
252 bool Directed = false,
253 bool Multi = false,
254 bool Weighted = true,
255 typename OutEdgeSelector = boost::vecS,
256 typename VertexSelector = boost::vecS
257>
272class Graph {
273public:
274 using NodeType = NodeID;
275 using EdgeWeightType = EdgeWeight;
276 using OutEdgeListSelector = OutEdgeSelector;
277 using VertexListSelector = VertexSelector;
278 using DirectedSelector = typename std::conditional<Directed, boost::bidirectionalS, boost::undirectedS>::type;
279 using VertexProperty = boost::property<
280 boost::vertex_name_t,
281 NodeID,
282 boost::property<boost::vertex_wrapper_index_t, std::size_t>
283 >;
284 using EdgeProperty = typename std::conditional<
285 Weighted,
286 boost::property<boost::edge_weight_t, EdgeWeight, boost::property<boost::edge_index_t, std::size_t>>,
287 boost::property<boost::edge_index_t, std::size_t>
288 >::type;
289 using GraphType = boost::adjacency_list<OutEdgeSelector, VertexSelector, DirectedSelector,
290 VertexProperty, EdgeProperty>;
291 using VertexDesc = typename boost::graph_traits<GraphType>::vertex_descriptor;
292 using EdgeDesc = typename boost::graph_traits<GraphType>::edge_descriptor;
294 using VertexNameMap = typename boost::property_map<GraphType, boost::vertex_name_t>::type;
295 using WrapperIndexMap = typename boost::property_map<GraphType, boost::vertex_wrapper_index_t>::type;
296 using VertexIndexMap = WrapperIndexMap;
297 using EdgeIdMap = typename boost::property_map<GraphType, boost::edge_index_t>::type;
298 using IdMap = std::map<NodeID, VertexDesc>;
299 using AttrMap = std::map<std::string, std::any>;
300 using NodeAttrStorage = std::map<NodeID, AttrMap>;
301 using EdgeAttrStorage = std::map<std::size_t, AttrMap>;
302 static constexpr bool is_directed = Directed;
303 static constexpr bool has_builtin_edge_weight = Weighted;
304
305private:
306 static_assert(
307 std::copy_constructible<NodeID>,
308 "nxpp::Graph requires NodeID to be copy-constructible because node IDs are stored and materialized across wrapper-owned containers."
309 );
310
311 static_assert(
312 std::equality_comparable<NodeID>,
313 "nxpp::Graph requires NodeID to support operator== because several wrapper result helpers compare reconstructed node IDs directly."
314 );
315
316 static_assert(
318 "nxpp::Graph requires NodeID to be orderable with std::less because wrapper-owned maps and key-sorted result containers rely on that ordering."
319 );
320
321 GraphType g;
322 WeightMap weight_map;
323 VertexNameMap vertex_name_map;
324 VertexIndexMap vertex_index_map;
325 EdgeIdMap edge_id_map;
326 IdMap id_to_bgl;
327 std::vector<NodeID> bgl_to_id;
328 std::size_t next_edge_id = 0;
329 NodeAttrStorage node_properties;
330 EdgeAttrStorage edge_properties;
331
332private:
333 static_assert(
334 !(Multi && std::is_same_v<OutEdgeSelector, boost::setS>),
335 "nxpp does not support Multi=true with boost::setS because setS suppresses parallel edges."
336 );
337
338 using EdgeAttrMap = AttrMap;
339
340 static std::any normalize_attr_any(std::any value) {
341 if (value.type() == typeid(const char*)) {
342 return std::string(std::any_cast<const char*>(value));
343 }
344 if (value.type() == typeid(char*)) {
345 return std::string(std::any_cast<char*>(value));
346 }
347 return value;
348 }
349
350 template <typename T>
351 static std::any make_attr_any(const T& value) {
352 if constexpr (std::is_convertible_v<T, const char*> && !std::is_same_v<std::decay_t<T>, std::string>) {
353 return std::string(value);
354 } else {
355 return std::any(value);
356 }
357 }
358
359 std::size_t vertex_index_of(VertexDesc v) const {
360 return boost::get(vertex_index_map, v);
361 }
362
363 const NodeID& node_id_of(VertexDesc v) const {
364 return boost::get(vertex_name_map, v);
365 }
366
367 template <typename Value, typename BuildValue>
368 indexed_lookup_map<NodeID, Value> build_node_indexed_result(BuildValue&& build_value) const {
370 result.reserve(id_to_bgl.size());
371 for (const auto& [id, desc] : id_to_bgl) {
372 result.push_back(id, build_value(desc));
373 }
374 return result;
375 }
376
377 template <typename Value>
378 indexed_lookup_map<NodeID, Value> build_sparse_node_indexed_result(
379 const std::vector<std::optional<Value>>& values
380 ) const {
382 for (const auto& [id, desc] : id_to_bgl) {
383 const auto index = get_vertex_index(desc);
384 if (index < values.size() && values[index].has_value()) {
385 result.push_back(id, *values[index]);
386 }
387 }
388 return result;
389 }
390
391 void rebuild_vertex_maps() {
392 id_to_bgl.clear();
393 bgl_to_id.clear();
394
395 std::size_t index = 0;
396 for (auto [v, vend] = boost::vertices(g); v != vend; ++v, ++index) {
397 boost::put(vertex_index_map, *v, index);
398 NodeID id = boost::get(vertex_name_map, *v);
399 id_to_bgl[id] = *v;
400 bgl_to_id.push_back(id);
401 }
402 }
403
404 VertexDesc get_or_create_vertex(const NodeID& id) {
405 auto it = id_to_bgl.find(id);
406 if (it != id_to_bgl.end()) {
407 return it->second;
408 }
409 VertexDesc v = boost::add_vertex(g);
410 boost::put(vertex_name_map, v, id);
411 boost::put(vertex_index_map, v, bgl_to_id.size());
412 id_to_bgl[id] = v;
413 bgl_to_id.push_back(id);
414 return v;
415 }
416
417 std::size_t get_edge_id(EdgeDesc e) const {
418 return boost::get(edge_id_map, e);
419 }
420
421 std::optional<EdgeDesc> try_find_edge_desc_by_id(std::size_t edge_id) const {
422 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
423 if (get_edge_id(*e) == edge_id) {
424 return *e;
425 }
426 }
427 return std::nullopt;
428 }
429
430 EdgeDesc get_edge_desc_by_id(std::size_t edge_id) const {
431 auto edge_desc = try_find_edge_desc_by_id(edge_id);
432 if (!edge_desc.has_value()) {
433 throw std::runtime_error("Edge lookup failed: edge not found.");
434 }
435 return *edge_desc;
436 }
437
438 std::vector<std::size_t> collect_edge_ids_between(VertexDesc u, VertexDesc v) const {
439 std::vector<std::size_t> edge_ids;
440 for (auto [e, eend] = boost::out_edges(u, g); e != eend; ++e) {
441 if (boost::target(*e, g) == v) {
442 edge_ids.push_back(get_edge_id(*e));
443 }
444 }
445 if constexpr (!Directed) {
446 if (u != v) {
447 for (auto [e, eend] = boost::out_edges(v, g); e != eend; ++e) {
448 if (boost::target(*e, g) == u) {
449 edge_ids.push_back(get_edge_id(*e));
450 }
451 }
452 }
453 }
454 return edge_ids;
455 }
456
457 void erase_incident_edge_properties(VertexDesc v) {
458 std::vector<std::size_t> edge_ids;
459 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
460 if (boost::source(*e, g) == v || boost::target(*e, g) == v) {
461 edge_ids.push_back(get_edge_id(*e));
462 }
463 }
464 for (auto edge_id : edge_ids) {
465 edge_properties.erase(edge_id);
466 }
467 }
468
469 void assign_edge_attrs(EdgeDesc e, const EdgeAttrMap& attrs) {
470 auto& edge_attr_map = edge_properties[get_edge_id(e)];
471 for (const auto& [key, value] : attrs) {
472 edge_attr_map[key] = normalize_attr_any(value);
473 }
474 }
475
476 void assign_edge_attrs(EdgeDesc e, std::initializer_list<std::pair<std::string, std::any>> attrs) {
477 auto& edge_attr_map = edge_properties[get_edge_id(e)];
478 for (const auto& [key, value] : attrs) {
479 edge_attr_map[key] = normalize_attr_any(value);
480 }
481 }
482
483 void assign_edge_attr(EdgeDesc e, const std::pair<std::string, std::any>& attr) {
484 edge_properties[get_edge_id(e)][attr.first] = normalize_attr_any(attr.second);
485 }
486
487public:
488 Graph()
489 : g(),
491 vertex_name_map(boost::get(boost::vertex_name, g)),
492 vertex_index_map(boost::get(boost::vertex_wrapper_index, g)),
493 edge_id_map(boost::get(boost::edge_index, g)),
494 id_to_bgl() {}
495
496 ~Graph() {
497 clear_min_cost_flow_state();
498 }
499
507 void add_node(const NodeID& id) {
508 invalidate_min_cost_flow_state();
509 get_or_create_vertex(id);
510 }
511
518 void add_nodes_from(const std::vector<NodeID>& nodes) {
519 for (const auto& n : nodes) {
520 add_node(n);
521 }
522 }
523
530 EdgeDesc get_edge_desc(const NodeID& u, const NodeID& v) const {
531 auto it_u = id_to_bgl.find(u);
532 auto it_v = id_to_bgl.find(v);
533 if (it_u == id_to_bgl.end() || it_v == id_to_bgl.end()) throw std::runtime_error("Node lookup failed: node not found.");
534 auto [e, exists] = boost::edge(it_u->second, it_v->second, g);
535 if (!exists) throw std::runtime_error("Edge lookup failed: edge not found.");
536 return e;
537 }
538
546 bool has_edge(const NodeID& u, const NodeID& v) const {
547 auto it_u = id_to_bgl.find(u);
548 auto it_v = id_to_bgl.find(v);
549 if (it_u == id_to_bgl.end() || it_v == id_to_bgl.end()) return false;
550 auto [e, exists] = boost::edge(it_u->second, it_v->second, g);
551 return exists;
552 }
553
555 bool has_edge_id(std::size_t edge_id) const;
557 std::vector<std::size_t> edge_ids() const;
559 std::vector<std::size_t> edge_ids(const NodeID& u, const NodeID& v) const;
561 std::pair<NodeID, NodeID> get_edge_endpoints(std::size_t edge_id) const;
562
563 template <bool W = Weighted>
564 requires(W)
576 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w = 1.0) {
577 invalidate_min_cost_flow_state();
578 VertexDesc bu = get_or_create_vertex(u);
579 VertexDesc bv = get_or_create_vertex(v);
580
581 if constexpr (!Multi) {
582 auto [e, exists] = boost::edge(bu, bv, g);
583 if (exists) {
584 weight_map[e] = w;
585 return;
586 }
587 }
588
589 auto [e, added] = boost::add_edge(bu, bv, g);
590 weight_map[e] = w;
591 edge_id_map[e] = next_edge_id++;
592 }
593
594 template <bool W = Weighted>
595 requires(W)
602 std::size_t add_edge_with_id(const NodeID& u, const NodeID& v, EdgeWeight w = 1.0);
603
604 template <bool W = Weighted>
605 requires(!W)
615 void add_edge(const NodeID& u, const NodeID& v) {
616 invalidate_min_cost_flow_state();
617 VertexDesc bu = get_or_create_vertex(u);
618 VertexDesc bv = get_or_create_vertex(v);
619
620 if constexpr (!Multi) {
621 auto [e, exists] = boost::edge(bu, bv, g);
622 if (exists) {
623 return;
624 }
625 }
626
627 auto [e, added] = boost::add_edge(bu, bv, g);
628 (void)added;
629 edge_id_map[e] = next_edge_id++;
630 }
631
632 template <bool W = Weighted>
633 requires(!W)
640 std::size_t add_edge_with_id(const NodeID& u, const NodeID& v);
641
642
651 template <bool W = Weighted>
652 requires(W)
653 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w, const EdgeAttrMap& attrs);
654
662 void add_edge(const NodeID& u, const NodeID& v, const EdgeAttrMap& attrs);
663
664 template <bool W = Weighted>
665 requires(W)
674 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w, const std::pair<std::string, std::any>& attr);
675
677 void add_edge(const NodeID& u, const NodeID& v, const std::pair<std::string, std::any>& attr);
678
679 template <bool W = Weighted>
680 requires(W)
689 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w, std::initializer_list<std::pair<std::string, std::any>> attrs);
690
692 void add_edge(const NodeID& u, const NodeID& v, std::initializer_list<std::pair<std::string, std::any>> attrs);
693 template <bool W = Weighted>
694 requires(W)
696 void add_edges_from(const std::vector<std::tuple<NodeID, NodeID, EdgeWeight>>& edges) {
697 for (const auto& edge : edges) {
698 add_edge(std::get<0>(edge), std::get<1>(edge), std::get<2>(edge));
699 }
700 }
701
703 void add_edges_from(const std::vector<std::pair<NodeID, NodeID>>& edges) {
704 for (const auto& edge : edges) {
705 add_edge(edge.first, edge.second);
706 }
707 }
708
716 void clear() {
717 invalidate_min_cost_flow_state();
718 g = GraphType();
719 id_to_bgl.clear();
720 bgl_to_id.clear();
721 node_properties.clear();
722 edge_properties.clear();
724 vertex_name_map = boost::get(boost::vertex_name, g);
725 vertex_index_map = boost::get(boost::vertex_wrapper_index, g);
726 edge_id_map = boost::get(boost::edge_index, g);
727 next_edge_id = 0;
728 }
729
739 void remove_edge(const NodeID& u, const NodeID& v) {
740 invalidate_min_cost_flow_state();
741 auto it_u = id_to_bgl.find(u);
742 auto it_v = id_to_bgl.find(v);
743 if (it_u == id_to_bgl.end() || it_v == id_to_bgl.end()) {
744 throw std::runtime_error("Node lookup failed: node not found.");
745 }
746 auto [e, exists] = boost::edge(it_u->second, it_v->second, g);
747 if (!exists) {
748 throw std::runtime_error("Edge lookup failed: edge not found.");
749 }
750 for (auto edge_id : collect_edge_ids_between(it_u->second, it_v->second)) {
751 edge_properties.erase(edge_id);
752 }
753 boost::remove_edge(it_u->second, it_v->second, g);
754 }
755
761 void remove_edge(std::size_t edge_id);
762
772 void remove_node(const NodeID& u) {
773 invalidate_min_cost_flow_state();
774 auto it = id_to_bgl.find(u);
775 if (it == id_to_bgl.end()) {
776 throw std::runtime_error("Node lookup failed: node not found.");
777 }
778 VertexDesc v = it->second;
779
780 erase_incident_edge_properties(v);
781 boost::clear_vertex(v, g);
782 boost::remove_vertex(v, g);
783
784 node_properties.erase(u);
785 rebuild_vertex_maps();
786 }
787
794 std::vector<NodeID> neighbors(const NodeID& u) const {
795 auto it = id_to_bgl.find(u);
796 if (it == id_to_bgl.end()) {
797 throw std::runtime_error("Node lookup failed: node not found.");
798 }
799 std::vector<NodeID> res;
800 for (auto [e, eend] = boost::out_edges(it->second, g); e != eend; ++e) {
801 res.push_back(node_id_of(boost::target(*e, g)));
802 }
803 return res;
804 }
805
807 std::vector<NodeID> successors(const NodeID& u) const {
808 return neighbors(u);
809 }
810
817 std::vector<NodeID> predecessors(const NodeID& u) const {
818 auto it = id_to_bgl.find(u);
819 if (it == id_to_bgl.end()) {
820 throw std::runtime_error("Node lookup failed: node not found.");
821 }
822
823 std::vector<NodeID> res;
824 if constexpr (Directed) {
825 for (auto [e, eend] = boost::in_edges(it->second, g); e != eend; ++e) {
826 res.push_back(node_id_of(boost::source(*e, g)));
827 }
828 } else {
829 for (auto [e, eend] = boost::out_edges(it->second, g); e != eend; ++e) {
830 res.push_back(node_id_of(boost::target(*e, g)));
831 }
832 }
833 return res;
834 }
835
837 bool has_node(const NodeID& u) const {
838 return id_to_bgl.find(u) != id_to_bgl.end();
839 }
840
841
848 bool has_node_attr(const NodeID& u, const std::string& key) const;
856 bool has_edge_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
858 bool has_edge_attr(std::size_t edge_id, const std::string& key) const;
859
860 template <typename T>
867 T get_node_attr(const NodeID& u, const std::string& key) const;
868
869 template <typename T>
876 T get_edge_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
877
878 template <typename T>
884 T get_edge_attr(std::size_t edge_id, const std::string& key) const;
885
886 template <typename T>
893 std::optional<T> try_get_node_attr(const NodeID& u, const std::string& key) const;
894
895 template <typename T>
903 std::optional<T> try_get_edge_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
904
905 template <typename T>
907 std::optional<T> try_get_edge_attr(std::size_t edge_id, const std::string& key) const;
908
916 double get_edge_numeric_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
918 double get_edge_numeric_attr(std::size_t edge_id, const std::string& key) const;
919
920 template <bool W = Weighted>
921 requires(W)
929 EdgeWeight get_edge_weight(const NodeID& u, const NodeID& v) const;
930
931 template <bool W = Weighted>
932 requires(W)
934 EdgeWeight get_edge_weight(std::size_t edge_id) const;
935
936 template <bool W = Weighted>
937 requires(W)
939 void set_edge_weight(std::size_t edge_id, EdgeWeight w);
940
941 template <typename T>
948 void set_edge_attr(std::size_t edge_id, const std::string& key, const T& value);
949
956 std::vector<NodeID> nodes() const {
957 std::vector<NodeID> res;
958 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
959 res.push_back(node_id_of(*v));
960 }
961 return res;
962 }
963
970 auto edges() const {
971 if constexpr (Weighted) {
972 std::vector<std::tuple<NodeID, NodeID, EdgeWeight>> res;
973 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
974 NodeID source_id = node_id_of(boost::source(*e, g));
975 NodeID target_id = node_id_of(boost::target(*e, g));
976 res.emplace_back(source_id, target_id, weight_map[*e]);
977 }
978 return res;
979 } else {
980 std::vector<std::pair<NodeID, NodeID>> res;
981 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
982 NodeID source_id = node_id_of(boost::source(*e, g));
983 NodeID target_id = node_id_of(boost::target(*e, g));
984 res.emplace_back(source_id, target_id);
985 }
986 return res;
987 }
988 }
989
991 std::vector<std::pair<NodeID, NodeID>> edge_pairs() const {
992 std::vector<std::pair<NodeID, NodeID>> res;
993 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
994 NodeID source_id = node_id_of(boost::source(*e, g));
995 NodeID target_id = node_id_of(boost::target(*e, g));
996 res.emplace_back(source_id, target_id);
997 }
998 return res;
999 }
1000
1002 const GraphType& get_impl() const { return g; }
1004 const std::vector<NodeID>& get_bgl_to_id_map() const { return bgl_to_id; }
1006 const IdMap& get_id_to_bgl_map() const { return id_to_bgl; }
1008 const NodeID& get_node_id(VertexDesc v) const { return node_id_of(v); }
1010 std::size_t get_vertex_index(VertexDesc v) const { return vertex_index_of(v); }
1011
1012 // Proxy Pattern per simulare G[u][v] = weight
1014 Graph* graph;
1015 NodeID u, v;
1016 std::string key;
1017
1023 template <typename T>
1024 EdgeAttrProxy& operator=(const T& val) {
1025 if (!graph->has_edge(u, v)) {
1026 if constexpr (Weighted) graph->add_edge(u, v, static_cast<EdgeWeight>(1.0));
1027 else graph->add_edge(u, v);
1028 }
1029 auto e = graph->get_edge_desc(u, v);
1030 graph->edge_properties[graph->get_edge_id(e)][key] = Graph::make_attr_any(val);
1031 return *this;
1032 }
1033
1034 template <typename T>
1035 operator T() const {
1036 return graph->template get_edge_attr<T>(u, v, key);
1037 }
1038 };
1039
1041 Graph* graph;
1042 NodeID u, v;
1043 public:
1044 EdgeProxy(Graph* g, NodeID u, NodeID v) : graph(g), u(u), v(v) {}
1045
1047 template <bool W = Weighted>
1048 requires(W)
1049 EdgeProxy& operator=(EdgeWeight w) {
1050 graph->add_edge(u, v, w);
1051 return *this;
1052 }
1053
1054 template <bool W = Weighted>
1055 requires(W)
1056 operator EdgeWeight() const {
1057 return graph->get_edge_weight(u, v);
1058 }
1059
1060 EdgeAttrProxy operator[](const std::string& key) {
1061 return {graph, u, v, key};
1062 }
1063
1064 EdgeAttrProxy operator[](const char* key) {
1065 return {graph, u, v, std::string(key)};
1066 }
1067 };
1068
1070 Graph* graph;
1071 NodeID u;
1072 std::string key;
1073
1078 template <typename T>
1079 NodeAttrProxy& operator=(const T& val) {
1080 if (!graph->has_node(u)) graph->add_node(u);
1081 graph->node_properties[u][key] = Graph::make_attr_any(val);
1082 return *this;
1083 }
1084
1085 template <typename T>
1086 operator T() const {
1087 return graph->template get_node_attr<T>(u, key);
1088 }
1089 };
1090
1092 Graph* graph;
1093 NodeID u;
1094 public:
1095 NodeProxy(Graph* g, NodeID u) : graph(g), u(u) {}
1096
1097 EdgeProxy operator[](const NodeID& v) {
1098 return EdgeProxy(graph, u, v);
1099 }
1100 };
1101
1103 Graph* graph;
1104 NodeID u;
1105 public:
1106 NodeAttrBaseProxy(Graph* g, NodeID u) : graph(g), u(u) {}
1107
1108 NodeAttrProxy operator[](const std::string& key) {
1109 return {graph, u, key};
1110 }
1111
1112 NodeAttrProxy operator[](const char* key) {
1113 return {graph, u, std::string(key)};
1114 }
1115 };
1116
1130 NodeProxy operator[](const NodeID& u) {
1131 if (!has_node(u)) {
1132 add_node(u);
1133 }
1134 return NodeProxy(this, u);
1135 }
1136
1142 NodeAttrBaseProxy node(const NodeID& u) {
1143 if (!has_node(u) && !Multi) add_node(u);
1144 else if (!has_node(u)) add_node(u);
1145 return NodeAttrBaseProxy(this, u);
1146 }
1147
1156 auto bfs_edges(const NodeID& start) const;
1167 auto bfs_tree(const NodeID& start) const;
1179 auto bfs_successors(const NodeID& start) const;
1191 template <typename Visitor>
1192 void breadth_first_search(const NodeID& start, Visitor& visitor) const;
1207 template <typename OnVertex, typename OnTreeEdge>
1208 void bfs_visit(const NodeID& start, OnVertex&& on_vertex, OnTreeEdge&& on_tree_edge) const;
1217 auto dfs_edges(const NodeID& start) const;
1228 auto dfs_tree(const NodeID& start) const;
1239 auto dfs_predecessors(const NodeID& start) const;
1251 auto dfs_successors(const NodeID& start) const;
1263 template <typename Visitor>
1264 void depth_first_search(const NodeID& start, Visitor& visitor) const;
1279 template <typename OnTreeEdge, typename OnBackEdge>
1280 void dfs_visit(const NodeID& start, OnTreeEdge&& on_tree_edge, OnBackEdge&& on_back_edge) const;
1281
1290 auto shortest_path(const NodeID& source_id, const NodeID& target_id) const;
1303 auto shortest_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1312 double shortest_path_length(const NodeID& source_id, const NodeID& target_id) const;
1325 double shortest_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1326
1335 template <bool W = Weighted>
1336 requires(W)
1337 auto dijkstra_path(const NodeID& source_id, const NodeID& target_id) const;
1338
1351 template <bool W = Weighted>
1352 requires(W)
1353 auto dijkstra_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1354
1363 template <bool W = Weighted>
1364 requires(W)
1365 auto dijkstra_shortest_paths(const NodeID& source_id) const;
1366
1374 template <bool W = Weighted>
1375 requires(W)
1376 auto dijkstra_path_length(const NodeID& source_id) const;
1377
1386 template <bool W = Weighted>
1387 requires(W)
1388 auto dijkstra_path_length(const NodeID& source_id, const NodeID& target_id) const;
1389
1402 template <bool W = Weighted>
1403 requires(W)
1404 auto dijkstra_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1405
1414 template <bool W = Weighted>
1415 requires(W)
1416 auto bellman_ford_path(const NodeID& source_id, const NodeID& target_id) const;
1417
1426 template <bool W = Weighted>
1427 requires(W)
1428 auto bellman_ford_shortest_paths(const NodeID& source_id) const;
1429
1439 template <bool W = Weighted>
1440 requires(W)
1441 auto bellman_ford_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1442
1451 template <bool W = Weighted>
1452 requires(W)
1453 auto bellman_ford_path_length(const NodeID& source_id, const NodeID& target_id) const;
1454
1464 template <bool W = Weighted>
1465 requires(W)
1466 auto bellman_ford_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1467
1476 template <bool W = Weighted>
1477 requires(W)
1478 auto dag_shortest_paths(const NodeID& source_id) const;
1479
1490 template <bool W = Weighted>
1491 requires(W)
1493
1504 template <bool W = Weighted>
1505 requires(W)
1507
1509 auto connected_component_groups() const;
1511 auto connected_components() const;
1515 auto strong_component_map() const;
1517 auto strong_components() const;
1519 auto connected_component_map() const;
1521 auto strongly_connected_components() const;
1527 auto topological_sort() const;
1528
1536 template <bool W = Weighted>
1537 requires(W)
1538 auto kruskal_minimum_spanning_tree() const;
1539
1547 template <bool W = Weighted>
1548 requires(W)
1549 auto prim_minimum_spanning_tree(const NodeID& root_id) const;
1550
1558 template <bool W = Weighted>
1559 requires(W)
1560 auto minimum_spanning_tree() const;
1561
1569 template <bool W = Weighted>
1570 requires(W)
1571 auto minimum_spanning_tree(const NodeID& root_id) const;
1572
1583 auto edmonds_karp_maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
1594 auto push_relabel_maximum_flow_result(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
1605 auto maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
1616 auto minimum_cut(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
1628 auto max_flow_min_cost_cycle_canceling(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") const;
1630 long push_relabel_maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") const;
1641 auto cycle_canceling(const std::string& weight_attr = "weight") const;
1653 auto successive_shortest_path_nonnegative_weights(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") const;
1665 auto max_flow_min_cost_successive_shortest_path(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") const;
1677 auto max_flow_min_cost(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") const;
1678
1681 auto num_vertices() const;
1692 auto degree_centrality() const;
1705 auto pagerank() const;
1716 auto betweenness_centrality() const;
1717
1718private:
1719 void invalidate_min_cost_flow_state() const {
1721 static_cast<const void*>(this)
1722 );
1723 }
1724
1725 void clear_min_cost_flow_state() const {
1726 detail::MinCostFlowCacheHooks<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>::clear(
1727 static_cast<const void*>(this)
1728 );
1729 }
1730};
1731
1732template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
1734 return static_cast<int>(boost::num_vertices(g));
1735}
1736
1737
1738template <typename GraphWrapper>
1740[[deprecated("Use G.num_vertices() instead.")]]
1741auto num_vertices(const GraphWrapper& G) {
1742 return G.num_vertices();
1743}
1744
1745
1757
1761
1774
1778
1793
1794} // namespace nxpp
Definition graph.hpp:1040
Definition graph.hpp:1102
Definition graph.hpp:1091
Graph wrapper around Boost Graph Library with Python-inspired helpers.
Definition graph.hpp:272
const IdMap & get_id_to_bgl_map() const
Returns the maintained node-ID-to-vertex translation table.
Definition graph.hpp:1006
auto bellman_ford_path(const NodeID &source_id, const NodeID &target_id) const
Computes a shortest path using Bellman-Ford and built-in weights.
Definition shortest_paths.hpp:647
auto minimum_spanning_tree() const
Convenience alias for kruskal_minimum_spanning_tree().
Definition spanning_tree.hpp:112
double get_edge_numeric_attr(const NodeID &u, const NodeID &v, const std::string &key) const
Reads an endpoint-selected edge attribute as a numeric value.
Definition attributes.hpp:219
void add_nodes_from(const std::vector< NodeID > &nodes)
Adds a batch of nodes from a vector of node IDs.
Definition graph.hpp:518
auto edmonds_karp_maximum_flow(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes a maximum flow using the Edmonds-Karp algorithm.
Definition flow.hpp:377
std::optional< T > try_get_node_attr(const NodeID &u, const std::string &key) const
Attempts to read a typed node attribute without throwing.
Definition attributes.hpp:182
void add_edges_from(const std::vector< std::pair< NodeID, NodeID > > &edges)
Adds a batch of unweighted edges from (u, v) pairs.
Definition graph.hpp:703
auto successive_shortest_path_nonnegative_weights(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Computes a max-flow min-cost result using successive shortest path.
Definition flow.hpp:671
auto dijkstra_path(const NodeID &source_id, const NodeID &target_id) const
Computes the shortest path using the built-in edge-weight property.
Definition shortest_paths.hpp:400
auto bellman_ford_shortest_paths(const NodeID &source_id) const
Returns distances and predecessors for all nodes using Bellman-Ford.
Definition shortest_paths.hpp:683
auto dijkstra_path_length(const NodeID &source_id) const
Returns built-in-weight shortest-path distances from a source node.
Definition shortest_paths.hpp:480
auto dfs_predecessors(const NodeID &start) const
Returns the DFS predecessor assigned to each discovered node.
Definition traversal.hpp:466
bool has_node_attr(const NodeID &u, const std::string &key) const
Returns whether a node has the named attribute.
Definition attributes.hpp:113
auto dijkstra_shortest_paths(const NodeID &source_id) const
Returns distances and predecessors for all nodes reachable from a source.
Definition shortest_paths.hpp:446
auto bfs_tree(const NodeID &start) const
Materializes the breadth-first-search tree rooted at start.
Definition traversal.hpp:371
void set_edge_attr(std::size_t edge_id, const std::string &key, const T &value)
Stores a typed attribute on an edge identified by edge ID.
Definition multigraph.hpp:186
EdgeDesc get_edge_desc(const NodeID &u, const NodeID &v) const
Returns the underlying Boost edge descriptor for an existing edge.
Definition graph.hpp:530
void remove_edge(const NodeID &u, const NodeID &v)
Removes all edges between two endpoints.
Definition graph.hpp:739
std::vector< NodeID > neighbors(const NodeID &u) const
Returns the outgoing neighbors of a node.
Definition graph.hpp:794
auto num_vertices() const
Returns the number of vertices currently stored in the graph.
Definition graph.hpp:1733
std::pair< NodeID, NodeID > get_edge_endpoints(std::size_t edge_id) const
Returns the endpoints of an edge identified by its wrapper-managed ID.
Definition multigraph.hpp:40
auto strongly_connected_component_roots() const
Returns one representative root node for each strong component.
Definition components.hpp:233
T get_node_attr(const NodeID &u, const std::string &key) const
Reads a typed node attribute.
Definition attributes.hpp:145
auto max_flow_min_cost(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Convenience alias for the default max-flow min-cost wrapper.
Definition flow.hpp:732
std::vector< NodeID > predecessors(const NodeID &u) const
Returns predecessor nodes for directed graphs.
Definition graph.hpp:817
void bfs_visit(const NodeID &start, OnVertex &&on_vertex, OnTreeEdge &&on_tree_edge) const
Runs breadth-first search with lightweight callback hooks.
Definition traversal.hpp:416
auto betweenness_centrality() const
Computes normalized betweenness centrality for every node.
Definition centrality.hpp:118
auto pagerank() const
Computes PageRank scores for every node.
Definition centrality.hpp:46
std::vector< NodeID > nodes() const
Returns all node IDs currently stored in the graph.
Definition graph.hpp:956
auto prim_minimum_spanning_tree(const NodeID &root_id) const
Returns the parent map produced by Prim's minimum-spanning-tree algorithm.
Definition spanning_tree.hpp:90
bool has_edge_attr(const NodeID &u, const NodeID &v, const std::string &key) const
Returns whether an endpoint-selected edge has the named attribute.
Definition attributes.hpp:122
std::optional< T > try_get_edge_attr(const NodeID &u, const NodeID &v, const std::string &key) const
Attempts to read a typed edge attribute selected by endpoints.
Definition attributes.hpp:199
void dfs_visit(const NodeID &start, OnTreeEdge &&on_tree_edge, OnBackEdge &&on_back_edge) const
Runs depth-first search with lightweight callback hooks.
Definition traversal.hpp:507
auto dfs_successors(const NodeID &start) const
Groups DFS tree children by their discovered parent.
Definition traversal.hpp:476
auto bfs_edges(const NodeID &start) const
Returns the tree edges discovered by breadth-first search.
Definition traversal.hpp:356
T get_edge_attr(const NodeID &u, const NodeID &v, const std::string &key) const
Reads a typed edge attribute selected by endpoints.
Definition attributes.hpp:163
bool has_node(const NodeID &u) const
Returns whether the graph already contains the given node ID.
Definition graph.hpp:837
const std::vector< NodeID > & get_bgl_to_id_map() const
Returns the maintained vertex-index-to-node-ID translation table.
Definition graph.hpp:1004
auto edges() const
Returns the public edge list.
Definition graph.hpp:970
void depth_first_search(const NodeID &start, Visitor &visitor) const
Runs depth-first search with an object-style visitor.
Definition traversal.hpp:491
auto max_flow_min_cost_successive_shortest_path(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Convenience alias for successive_shortest_path_nonnegative_weights().
Definition flow.hpp:727
void add_node(const NodeID &id)
Ensures that a node with the given ID exists in the graph.
Definition graph.hpp:507
auto minimum_cut(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes a minimum cut between source and target using the named capacity attribute.
Definition flow.hpp:482
auto maximum_flow(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes the maximum flow value and edge assignments using the named capacity attribute.
Definition flow.hpp:479
auto topological_sort() const
Returns a topological ordering for a directed acyclic graph.
Definition topological_sort.hpp:29
auto dag_shortest_paths(const NodeID &source_id) const
Returns shortest-path distances in a directed acyclic graph.
Definition shortest_paths.hpp:745
const GraphType & get_impl() const
Exposes the underlying Boost graph for advanced integrations.
Definition graph.hpp:1002
auto strongly_connected_component_map() const
Alias for strong_component_map().
Definition components.hpp:230
void clear()
Clears the entire graph and all wrapper-managed metadata.
Definition graph.hpp:716
std::size_t get_vertex_index(VertexDesc v) const
Returns the wrapper-maintained dense vertex index used by algorithms.
Definition graph.hpp:1010
void breadth_first_search(const NodeID &start, Visitor &visitor) const
Runs breadth-first search with an object-style visitor.
Definition traversal.hpp:401
auto connected_component_groups() const
Groups each connected component as a vector of node IDs.
Definition components.hpp:136
long push_relabel_maximum_flow(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Runs push-relabel and stages residual state for a later cycle_canceling() call. Any later graph mutat...
Definition flow.hpp:610
auto dfs_edges(const NodeID &start) const
Returns the tree edges discovered by depth-first search.
Definition traversal.hpp:430
auto bfs_successors(const NodeID &start) const
Groups BFS tree children by their discovered parent.
Definition traversal.hpp:386
void remove_node(const NodeID &u)
Removes a node and all of its incident edges.
Definition graph.hpp:772
auto cycle_canceling(const std::string &weight_attr="weight") const
Runs cycle canceling on the previously cached residual network.
Definition flow.hpp:649
const NodeID & get_node_id(VertexDesc v) const
Returns the wrapper-side node ID associated with a Boost vertex descriptor.
Definition graph.hpp:1008
auto connected_component_map() const
Returns the connected-component index assigned to each node.
Definition components.hpp:224
auto strong_component_map() const
Returns the component index assigned to each node in a directed graph.
Definition components.hpp:190
auto bellman_ford_path_length(const NodeID &source_id, const NodeID &target_id) const
Returns the Bellman-Ford shortest-path length with built-in weights.
Definition shortest_paths.hpp:727
std::vector< std::size_t > edge_ids() const
Returns all wrapper-managed edge IDs currently present in the graph.
Definition multigraph.hpp:21
auto max_flow_min_cost_cycle_canceling(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Computes a max-flow min-cost result using cycle canceling.
Definition flow.hpp:549
bool has_edge_id(std::size_t edge_id) const
Returns whether an edge with the given wrapper-managed ID exists.
Definition multigraph.hpp:16
auto degree_centrality() const
Computes normalized degree centrality for every node.
Definition centrality.hpp:16
std::vector< std::pair< NodeID, NodeID > > edge_pairs() const
Returns all edges as endpoint pairs, ignoring built-in weights.
Definition graph.hpp:991
auto strong_components() const
Returns the number of strongly connected components in a directed graph.
Definition components.hpp:206
auto dfs_tree(const NodeID &start) const
Materializes the depth-first-search tree rooted at start.
Definition traversal.hpp:451
bool has_edge(const NodeID &u, const NodeID &v) const
Returns whether at least one edge exists between two endpoints.
Definition graph.hpp:546
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
auto kruskal_minimum_spanning_tree() const
Returns the edges selected by Kruskal's minimum-spanning-tree algorithm.
Definition spanning_tree.hpp:78
std::vector< NodeID > successors(const NodeID &u) const
Alias for neighbors(), mainly to mirror directed-graph terminology.
Definition graph.hpp:807
auto connected_components() const
Returns the number of connected components in an undirected graph.
Definition components.hpp:155
auto floyd_warshall_all_pairs_shortest_paths_map() const
Returns all-pairs shortest-path distances keyed by node IDs.
Definition shortest_paths.hpp:817
auto push_relabel_maximum_flow_result(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes a maximum flow using push-relabel and returns edge assignments.
Definition flow.hpp:428
auto floyd_warshall_all_pairs_shortest_paths() const
Returns all-pairs shortest-path distances as a dense matrix.
Definition shortest_paths.hpp:781
NodeAttrBaseProxy node(const NodeID &u)
Returns the proxy used for G.node(u)[key] node-attribute syntax.
Definition graph.hpp:1142
NodeProxy operator[](const NodeID &u)
Returns the proxy used for G[u][v] edge-access syntax.
Definition graph.hpp:1130
auto strongly_connected_component_groups() const
Groups each strongly connected component as a vector of node IDs.
Definition components.hpp:171
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
auto strongly_connected_components() const
Alias for strongly_connected_component_groups().
Definition components.hpp:227
Definition graph.hpp:143
indexed_lookup_map()=default
Default-constructs an empty ordered lookup map.
Definition graph.hpp:109
Minimal visitor interface for wrapper-level traversal callbacks.
Definition traversal.hpp:26
Definition graph.hpp:83
auto num_vertices(const GraphWrapper &G)
Deprecated free-function alias for num_vertices().
Definition graph.hpp:1741
Definition graph.hpp:1013
EdgeAttrProxy & operator=(const T &val)
Sets attribute key on edge (u,v), creating the edge if it does not exist.
Definition graph.hpp:1024
Definition graph.hpp:1069
NodeAttrProxy & operator=(const T &val)
Sets attribute key on node u, creating the node if it does not exist.
Definition graph.hpp:1079
Definition graph.hpp:78