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#if defined(__GNUC__) && !defined(__clang__)
21#pragma GCC diagnostic pop
22#endif
23#include <functional>
24#include <vector>
25#include <stdexcept>
26#include <tuple>
27#include <type_traits>
28#include <string>
29#include <algorithm>
30#include <cmath>
31#include <concepts>
32#include <utility>
33#include <any>
34#include <map>
35#include <optional>
36#include <limits>
37#include <set>
38#include <initializer_list>
39
40namespace nxpp {
41
42template <typename T>
43concept ValidNodeID =
44 std::copy_constructible<T> &&
45 std::equality_comparable<T> &&
46 requires(const T& lhs, const T& rhs) {
47 { std::less<T>{}(lhs, rhs) } -> std::convertible_to<bool>;
48 };
49
50template <typename T>
51concept NumericNodeID = ValidNodeID<T> && std::constructible_from<T, std::size_t>;
52
53template <
54 typename NodeID = std::string,
55 typename EdgeWeight = double,
56 bool Directed = false,
57 bool Multi = false,
58 bool Weighted = true,
59 typename OutEdgeSelector = boost::vecS,
60 typename VertexSelector = boost::vecS
61>
62class Graph;
63
64namespace detail {
65
66template <typename GraphWrapper>
68 static void invalidate(const void*) {}
69 static void clear(const void*) {}
70};
71
72} // namespace detail
73
74} // namespace nxpp
75
76namespace boost {
77
78// This is an intentional BGL extension point. BGL custom vertex properties are
79// installed globally in namespace boost, so keep the nxpp-specific name stable
80// and close to the wrapper storage that relies on it.
81enum vertex_wrapper_index_t { vertex_wrapper_index };
82BOOST_INSTALL_PROPERTY(vertex, wrapper_index);
83static_assert(
84 std::is_same_v<
85 property_kind<vertex_wrapper_index_t>::type,
86 vertex_property_tag
87 >,
88 "nxpp Boost property extension must install vertex_wrapper_index_t as a vertex property"
89);
90
91} // namespace boost
92
93namespace nxpp {
94
95template <typename GraphType, bool HasWeight>
97
98template <typename GraphType>
99struct built_in_weight_traits<GraphType, true> {
100 using map_type = typename boost::property_map<GraphType, boost::edge_weight_t>::type;
101
102 static map_type get(GraphType& g) {
103 return boost::get(boost::edge_weight, g);
104 }
105};
106
107template <typename GraphType>
108struct built_in_weight_traits<GraphType, false> {
109 struct map_type {};
110
111 static map_type get(GraphType&) {
112 return {};
113 }
114};
115
117enum class WeightMode {
118 Unweighted,
119 BuiltIn
120};
121
122template <typename Key, typename Value>
124public:
125 using storage_type = std::map<Key, Value>;
126 using iterator = typename storage_type::iterator;
127 using const_iterator = typename storage_type::const_iterator;
128
129 Value& operator[](const Key& key) {
130 return data[key];
131 }
132
134 [[nodiscard]] const Value& operator[](const Key& key) const {
135 return data.at(key);
136 }
137
138 Value& at(const Key& key) {
139 return data.at(key);
140 }
141
142 [[nodiscard]] const Value& at(const Key& key) const {
143 return data.at(key);
144 }
145
146 iterator begin() { return data.begin(); }
147 iterator end() { return data.end(); }
148 const_iterator begin() const { return data.begin(); }
149 const_iterator end() const { return data.end(); }
150 const_iterator cbegin() const { return data.cbegin(); }
151 const_iterator cend() const { return data.cend(); }
152
153private:
154 storage_type data;
155};
156
157template <typename Key, typename Value>
159public:
160 using storage_type = std::vector<std::pair<Key, Value>>;
161 using iterator = typename storage_type::iterator;
162 using const_iterator = typename storage_type::const_iterator;
163
173
174 void reserve(std::size_t count) {
175 data.reserve(count);
176 }
177
178 void push_back(const Key& key, const Value& value) {
179 data.emplace_back(key, value);
180 }
181
182 void push_back(const Key& key, Value&& value) {
183 data.emplace_back(key, std::move(value));
184 }
185
186 Value& at(const Key& key) {
187 auto it = find(key);
188 if (it == data.end()) {
189 throw std::out_of_range("indexed_lookup_map::at");
190 }
191 return it->second;
192 }
193
194 [[nodiscard]] const Value& at(const Key& key) const {
195 auto it = find(key);
196 if (it == data.end()) {
197 throw std::out_of_range("indexed_lookup_map::at");
198 }
199 return it->second;
200 }
201
202 Value& operator[](const Key& key) {
203 return at(key);
204 }
205
207 [[nodiscard]] const Value& operator[](const Key& key) const {
208 return at(key);
209 }
210
211 [[nodiscard]] bool contains(const Key& key) const {
212 return find(key) != data.end();
213 }
214
215 iterator find(const Key& key) {
216 auto it = lower_bound_for(key);
217 if (it == data.end() || key < it->first) {
218 return data.end();
219 }
220 return it;
221 }
222
223 [[nodiscard]] const_iterator find(const Key& key) const {
224 auto it = lower_bound_for(key);
225 if (it == data.end() || key < it->first) {
226 return data.end();
227 }
228 return it;
229 }
230
231 iterator begin() { return data.begin(); }
232 iterator end() { return data.end(); }
233 const_iterator begin() const { return data.begin(); }
234 const_iterator end() const { return data.end(); }
235 const_iterator cbegin() const { return data.cbegin(); }
236 const_iterator cend() const { return data.cend(); }
237
238 [[nodiscard]] bool empty() const { return data.empty(); }
239 [[nodiscard]] std::size_t size() const { return data.size(); }
240
241private:
242 iterator lower_bound_for(const Key& key) {
243 return std::lower_bound(
244 data.begin(),
245 data.end(),
246 key,
247 [](const auto& entry, const Key& value) { return entry.first < value; }
248 );
249 }
250
251 const_iterator lower_bound_for(const Key& key) const {
252 return std::lower_bound(
253 data.begin(),
254 data.end(),
255 key,
256 [](const auto& entry, const Key& value) { return entry.first < value; }
257 );
258 }
259
260 storage_type data;
261};
262
263// Core Graph Class
264
265template <
266 typename NodeID,
267 typename EdgeWeight,
268 bool Directed,
269 bool Multi,
270 bool Weighted,
271 typename OutEdgeSelector,
272 typename VertexSelector
273>
288class Graph {
289public:
290 using NodeType = NodeID;
291 using EdgeWeightType = EdgeWeight;
292 using OutEdgeListSelector = OutEdgeSelector;
293 using VertexListSelector = VertexSelector;
294 using DirectedSelector = typename std::conditional<Directed, boost::bidirectionalS, boost::undirectedS>::type;
295 using VertexProperty = boost::property<
296 boost::vertex_name_t,
297 NodeID,
298 boost::property<boost::vertex_wrapper_index_t, std::size_t>
299 >;
300 using EdgeProperty = typename std::conditional<
301 Weighted,
302 boost::property<boost::edge_weight_t, EdgeWeight, boost::property<boost::edge_index_t, std::size_t>>,
303 boost::property<boost::edge_index_t, std::size_t>
304 >::type;
305 using GraphType = boost::adjacency_list<OutEdgeSelector, VertexSelector, DirectedSelector,
306 VertexProperty, EdgeProperty>;
307 using VertexDesc = typename boost::graph_traits<GraphType>::vertex_descriptor;
308 using EdgeDesc = typename boost::graph_traits<GraphType>::edge_descriptor;
310 using VertexNameMap = typename boost::property_map<GraphType, boost::vertex_name_t>::type;
311 using WrapperIndexMap = typename boost::property_map<GraphType, boost::vertex_wrapper_index_t>::type;
312 using VertexIndexMap = WrapperIndexMap;
313 using EdgeIdMap = typename boost::property_map<GraphType, boost::edge_index_t>::type;
314 using IdMap = std::map<NodeID, VertexDesc>;
315 using AttrMap = std::map<std::string, std::any>;
316 using NodeAttrStorage = std::map<NodeID, AttrMap>;
317 using EdgeAttrStorage = std::map<std::size_t, AttrMap>;
318 using EdgeIdIndex = std::map<std::size_t, EdgeDesc>;
319 static constexpr bool is_directed = Directed;
320 static constexpr bool has_builtin_edge_weight = Weighted;
321
322private:
323 static_assert(
325 "nxpp::Graph requires NodeID to satisfy nxpp::ValidNodeID: copy-constructible, equality comparable, and orderable with std::less."
326 );
327
328 GraphType g;
329 WeightMap weight_map;
330 VertexNameMap vertex_name_map;
331 VertexIndexMap vertex_index_map;
332 EdgeIdMap edge_id_map;
333 IdMap id_to_bgl;
334 std::vector<NodeID> bgl_to_id;
335 std::size_t next_edge_id = 0;
336 NodeAttrStorage node_properties;
337 EdgeAttrStorage edge_properties;
338 EdgeIdIndex edge_id_to_desc;
339
340private:
341 static_assert(
342 !(Multi && std::is_same_v<OutEdgeSelector, boost::setS>),
343 "nxpp does not support Multi=true with boost::setS because setS suppresses parallel edges."
344 );
345
346 using EdgeAttrMap = AttrMap;
347
348 static std::any normalize_attr_any(std::any value) {
349 if (value.type() == typeid(const char*)) {
350 return std::string(std::any_cast<const char*>(value));
351 }
352 if (value.type() == typeid(char*)) {
353 return std::string(std::any_cast<char*>(value));
354 }
355 return value;
356 }
357
358 template <typename T>
359 static std::any make_attr_any(const T& value) {
360 if constexpr (std::is_convertible_v<T, const char*> && !std::is_same_v<std::decay_t<T>, std::string>) {
361 return std::string(value);
362 } else {
363 return std::any(value);
364 }
365 }
366
367 static std::runtime_error invalid_flow_capacity_error() {
368 return std::runtime_error("Flow capacity setup failed: capacity must be a non-negative integral value representable as long.");
369 }
370
371 template <typename T>
372 static long checked_flow_capacity_value(T value) {
373 if constexpr (std::is_same_v<T, bool>) {
374 throw invalid_flow_capacity_error();
375 } else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
376 if (value < 0 || static_cast<long double>(value) > static_cast<long double>(std::numeric_limits<long>::max())) {
377 throw invalid_flow_capacity_error();
378 }
379 return static_cast<long>(value);
380 } else if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
381 if (static_cast<long double>(value) > static_cast<long double>(std::numeric_limits<long>::max())) {
382 throw invalid_flow_capacity_error();
383 }
384 return static_cast<long>(value);
385 } else if constexpr (std::is_floating_point_v<T>) {
386 if (!std::isfinite(value) || value < 0 || value > static_cast<T>(std::numeric_limits<long>::max()) ||
387 std::trunc(value) != value) {
388 throw invalid_flow_capacity_error();
389 }
390 return static_cast<long>(value);
391 } else {
392 throw invalid_flow_capacity_error();
393 }
394 }
395
396 template <typename T>
397 static std::optional<long> try_flow_capacity_any_cast(const std::any& value) {
398 if (const auto* typed_value = std::any_cast<T>(&value)) {
399 return checked_flow_capacity_value(*typed_value);
400 }
401 return std::nullopt;
402 }
403
404 long get_edge_flow_capacity(std::size_t edge_id, const std::string& key) const {
405 if (key == "weight") {
406 if constexpr (Weighted) {
407 return checked_flow_capacity_value(get_edge_weight(edge_id));
408 } else {
409 throw std::runtime_error("Edge attribute lookup failed: graph has no built-in edge weight.");
410 }
411 }
412
413 auto edge_it = edge_properties.find(edge_id);
414 if (edge_it == edge_properties.end()) {
415 throw std::runtime_error("Edge attribute lookup failed: edge has no attributes.");
416 }
417 auto attr_it = edge_it->second.find(key);
418 if (attr_it == edge_it->second.end()) {
419 throw std::runtime_error("Edge attribute lookup failed: key not found.");
420 }
421
422 const auto& value = attr_it->second;
423 if (auto capacity = try_flow_capacity_any_cast<int>(value)) return *capacity;
424 if (auto capacity = try_flow_capacity_any_cast<long>(value)) return *capacity;
425 if (auto capacity = try_flow_capacity_any_cast<long long>(value)) return *capacity;
426 if (auto capacity = try_flow_capacity_any_cast<unsigned int>(value)) return *capacity;
427 if (auto capacity = try_flow_capacity_any_cast<unsigned long>(value)) return *capacity;
428 if (auto capacity = try_flow_capacity_any_cast<unsigned long long>(value)) return *capacity;
429 if (auto capacity = try_flow_capacity_any_cast<float>(value)) return *capacity;
430 if (auto capacity = try_flow_capacity_any_cast<double>(value)) return *capacity;
431 if (auto capacity = try_flow_capacity_any_cast<long double>(value)) return *capacity;
432
433 throw std::runtime_error("Flow capacity setup failed: capacity attribute is not numeric.");
434 }
435
436 std::size_t vertex_index_of(VertexDesc v) const {
437 return boost::get(vertex_index_map, v);
438 }
439
440 const NodeID& node_id_of(VertexDesc v) const {
441 return boost::get(vertex_name_map, v);
442 }
443
444 template <typename Value, typename BuildValue>
445 indexed_lookup_map<NodeID, Value> build_node_indexed_result(BuildValue&& build_value) const {
447 result.reserve(id_to_bgl.size());
448 for (const auto& [id, desc] : id_to_bgl) {
449 result.push_back(id, build_value(desc));
450 }
451 return result;
452 }
453
454 template <typename Value>
455 indexed_lookup_map<NodeID, Value> build_sparse_node_indexed_result(
456 const std::vector<std::optional<Value>>& values
457 ) const {
459 for (const auto& [id, desc] : id_to_bgl) {
460 const auto index = get_vertex_index(desc);
461 if (index < values.size() && values[index].has_value()) {
462 result.push_back(id, *values[index]);
463 }
464 }
465 return result;
466 }
467
468 void rebuild_vertex_maps() {
469 id_to_bgl.clear();
470 bgl_to_id.clear();
471
472 std::size_t index = 0;
473 for (auto [v, vend] = boost::vertices(g); v != vend; ++v, ++index) {
474 boost::put(vertex_index_map, *v, index);
475 NodeID id = boost::get(vertex_name_map, *v);
476 id_to_bgl[id] = *v;
477 bgl_to_id.push_back(id);
478 }
479 }
480
481 void rebind_property_maps() {
483 vertex_name_map = boost::get(boost::vertex_name, g);
484 vertex_index_map = boost::get(boost::vertex_wrapper_index, g);
485 edge_id_map = boost::get(boost::edge_index, g);
486 }
487
488 VertexDesc get_or_create_vertex(const NodeID& id) {
489 auto it = id_to_bgl.find(id);
490 if (it != id_to_bgl.end()) {
491 return it->second;
492 }
493 VertexDesc v = boost::add_vertex(g);
494 boost::put(vertex_name_map, v, id);
495 boost::put(vertex_index_map, v, bgl_to_id.size());
496 id_to_bgl[id] = v;
497 bgl_to_id.push_back(id);
498 return v;
499 }
500
501 std::optional<VertexDesc> find_vertex_by_id(const NodeID& id) const {
502 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
503 if (boost::get(vertex_name_map, *v) == id) {
504 return *v;
505 }
506 }
507 return std::nullopt;
508 }
509
510 std::size_t get_edge_id(EdgeDesc e) const {
511 return boost::get(edge_id_map, e);
512 }
513
514 void set_edge_id(EdgeDesc e, std::size_t edge_id) {
515 edge_id_map[e] = edge_id;
516 edge_id_to_desc[edge_id] = e;
517 }
518
519 std::size_t assign_next_edge_id(EdgeDesc e) {
520 const auto edge_id = next_edge_id++;
521 set_edge_id(e, edge_id);
522 return edge_id;
523 }
524
525 void erase_edge_id_index(std::size_t edge_id) {
526 edge_id_to_desc.erase(edge_id);
527 }
528
529 void rebuild_edge_id_index() {
530 edge_id_to_desc.clear();
531 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
532 edge_id_to_desc[get_edge_id(*e)] = *e;
533 }
534 }
535
536 std::optional<EdgeDesc> try_find_edge_desc_by_id(std::size_t edge_id) const {
537 auto edge_it = edge_id_to_desc.find(edge_id);
538 if (edge_it != edge_id_to_desc.end()) {
539 return edge_it->second;
540 }
541 return std::nullopt;
542 }
543
544 EdgeDesc get_edge_desc_by_id(std::size_t edge_id) const {
545 auto edge_desc = try_find_edge_desc_by_id(edge_id);
546 if (!edge_desc.has_value()) {
547 throw std::runtime_error("Edge lookup failed: edge not found.");
548 }
549 return *edge_desc;
550 }
551
552 std::vector<std::size_t> collect_edge_ids_between(VertexDesc u, VertexDesc v) const {
553 std::vector<std::size_t> edge_ids;
554 for (auto [e, eend] = boost::out_edges(u, g); e != eend; ++e) {
555 if constexpr (Directed) {
556 if (boost::target(*e, g) == v) {
557 edge_ids.push_back(get_edge_id(*e));
558 }
559 } else {
560 const auto source = boost::source(*e, g);
561 const auto target = boost::target(*e, g);
562 if ((source == u && target == v) || (source == v && target == u)) {
563 const auto edge_id = get_edge_id(*e);
564 if (std::find(edge_ids.begin(), edge_ids.end(), edge_id) == edge_ids.end()) {
565 edge_ids.push_back(edge_id);
566 }
567 }
568 }
569 }
570 return edge_ids;
571 }
572
573 void erase_incident_edge_properties(VertexDesc v) {
574 std::vector<std::size_t> edge_ids;
575 for (auto [e, eend] = boost::out_edges(v, g); e != eend; ++e) {
576 edge_ids.push_back(get_edge_id(*e));
577 }
578 if constexpr (Directed) {
579 for (auto [e, eend] = boost::in_edges(v, g); e != eend; ++e) {
580 if (boost::source(*e, g) != v) {
581 edge_ids.push_back(get_edge_id(*e));
582 }
583 }
584 }
585 for (auto edge_id : edge_ids) {
586 edge_properties.erase(edge_id);
587 erase_edge_id_index(edge_id);
588 }
589 }
590
591 void assign_edge_attrs(EdgeDesc e, const EdgeAttrMap& attrs) {
592 auto& edge_attr_map = edge_properties[get_edge_id(e)];
593 for (const auto& [key, value] : attrs) {
594 edge_attr_map[key] = normalize_attr_any(value);
595 }
596 }
597
598 void assign_edge_attrs(EdgeDesc e, std::initializer_list<std::pair<std::string, std::any>> attrs) {
599 auto& edge_attr_map = edge_properties[get_edge_id(e)];
600 for (const auto& [key, value] : attrs) {
601 edge_attr_map[key] = normalize_attr_any(value);
602 }
603 }
604
605 void assign_edge_attr(EdgeDesc e, const std::pair<std::string, std::any>& attr) {
606 edge_properties[get_edge_id(e)][attr.first] = normalize_attr_any(attr.second);
607 }
608
609 template <typename NodeRange>
610 Graph build_subgraph(const NodeRange& selected_nodes) const {
611 Graph result;
612 std::set<NodeID> selected;
613
614 for (const auto& node : selected_nodes) {
615 if (!has_node(node)) {
616 throw std::invalid_argument("Subgraph extraction failed: node not found.");
617 }
618 if (!selected.insert(node).second) {
619 continue;
620 }
621
622 result.add_node(node);
623 auto node_attr_it = node_properties.find(node);
624 if (node_attr_it != node_properties.end()) {
625 result.node_properties[node] = node_attr_it->second;
626 }
627 }
628
629 for (auto [edge, edge_end] = boost::edges(g); edge != edge_end; ++edge) {
630 const NodeID& source = node_id_of(boost::source(*edge, g));
631 const NodeID& target = node_id_of(boost::target(*edge, g));
632 if (!selected.contains(source) || !selected.contains(target)) {
633 continue;
634 }
635
636 auto [new_edge, added] = boost::add_edge(result.id_to_bgl.at(source), result.id_to_bgl.at(target), result.g);
637 (void)added;
638 if constexpr (Weighted) {
639 result.weight_map[new_edge] = weight_map[*edge];
640 }
641 const auto new_edge_id = result.next_edge_id++;
642 result.set_edge_id(new_edge, new_edge_id);
643
644 const auto old_edge_id = get_edge_id(*edge);
645 const auto edge_attr_it = edge_properties.find(old_edge_id);
646 if (edge_attr_it != edge_properties.end()) {
647 result.edge_properties[new_edge_id] = edge_attr_it->second;
648 }
649 }
650
651 return result;
652 }
653
654public:
655 Graph()
656 : g(),
658 vertex_name_map(boost::get(boost::vertex_name, g)),
659 vertex_index_map(boost::get(boost::vertex_wrapper_index, g)),
660 edge_id_map(boost::get(boost::edge_index, g)),
661 id_to_bgl() {}
662
663 Graph(const Graph& other)
664 : g(other.g),
666 vertex_name_map(boost::get(boost::vertex_name, g)),
667 vertex_index_map(boost::get(boost::vertex_wrapper_index, g)),
668 edge_id_map(boost::get(boost::edge_index, g)),
669 id_to_bgl(),
670 bgl_to_id(),
671 next_edge_id(other.next_edge_id),
672 node_properties(other.node_properties),
673 edge_properties(other.edge_properties) {
674 rebuild_vertex_maps();
675 rebuild_edge_id_index();
676 }
677
678 Graph(Graph&& other) noexcept
679 : g(std::move(other.g)),
681 vertex_name_map(boost::get(boost::vertex_name, g)),
682 vertex_index_map(boost::get(boost::vertex_wrapper_index, g)),
683 edge_id_map(boost::get(boost::edge_index, g)),
684 id_to_bgl(),
685 bgl_to_id(),
686 next_edge_id(other.next_edge_id),
687 node_properties(std::move(other.node_properties)),
688 edge_properties(std::move(other.edge_properties)) {
689 rebuild_vertex_maps();
690 rebuild_edge_id_index();
691 other.clear_min_cost_flow_state();
692 other.clear();
693 }
694
695 Graph& operator=(const Graph& other) {
696 if (this == &other) {
697 return *this;
698 }
699
700 clear_min_cost_flow_state();
701 g = other.g;
702 rebind_property_maps();
703 next_edge_id = other.next_edge_id;
704 node_properties = other.node_properties;
705 edge_properties = other.edge_properties;
706 rebuild_vertex_maps();
707 rebuild_edge_id_index();
708 return *this;
709 }
710
711 Graph& operator=(Graph&& other) noexcept {
712 if (this == &other) {
713 return *this;
714 }
715
716 clear_min_cost_flow_state();
717 g = std::move(other.g);
718 rebind_property_maps();
719 next_edge_id = other.next_edge_id;
720 node_properties = std::move(other.node_properties);
721 edge_properties = std::move(other.edge_properties);
722 rebuild_vertex_maps();
723 rebuild_edge_id_index();
724 other.clear_min_cost_flow_state();
725 other.clear();
726 return *this;
727 }
728
729 ~Graph() {
730 clear_min_cost_flow_state();
731 }
732
740 void add_node(const NodeID& id) {
741 invalidate_min_cost_flow_state();
742 get_or_create_vertex(id);
743 }
744
751 void add_nodes_from(const std::vector<NodeID>& nodes) {
752 for (const auto& n : nodes) {
753 add_node(n);
754 }
755 }
756
763 [[nodiscard]] EdgeDesc get_edge_desc(const NodeID& u, const NodeID& v) const {
764 auto it_u = id_to_bgl.find(u);
765 auto it_v = id_to_bgl.find(v);
766 if (it_u == id_to_bgl.end() || it_v == id_to_bgl.end()) throw std::runtime_error("Node lookup failed: node not found.");
767 auto [e, exists] = boost::edge(it_u->second, it_v->second, g);
768 if (!exists) throw std::runtime_error("Edge lookup failed: edge not found.");
769 return e;
770 }
771
779 [[nodiscard]] bool has_edge(const NodeID& u, const NodeID& v) const {
780 auto it_u = id_to_bgl.find(u);
781 auto it_v = id_to_bgl.find(v);
782 if (it_u == id_to_bgl.end() || it_v == id_to_bgl.end()) return false;
783 auto [e, exists] = boost::edge(it_u->second, it_v->second, g);
784 return exists;
785 }
786
788 [[nodiscard]] bool has_edge_id(std::size_t edge_id) const;
790 [[nodiscard]] std::vector<std::size_t> edge_ids() const;
792 [[nodiscard]] std::vector<std::size_t> edge_ids(const NodeID& u, const NodeID& v) const;
794 [[nodiscard]] std::pair<NodeID, NodeID> get_edge_endpoints(std::size_t edge_id) const;
795
796 template <bool W = Weighted>
797 requires(W)
809 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w = 1.0) {
810 invalidate_min_cost_flow_state();
811 VertexDesc bu = get_or_create_vertex(u);
812 VertexDesc bv = get_or_create_vertex(v);
813
814 if constexpr (!Multi) {
815 auto [e, exists] = boost::edge(bu, bv, g);
816 if (exists) {
817 weight_map[e] = w;
818 return;
819 }
820 }
821
822 auto [e, added] = boost::add_edge(bu, bv, g);
823 (void)added;
824 weight_map[e] = w;
825 assign_next_edge_id(e);
826 }
827
828 template <bool W = Weighted>
829 requires(W)
836 std::size_t add_edge_with_id(const NodeID& u, const NodeID& v, EdgeWeight w = 1.0);
837
838 template <bool W = Weighted>
839 requires(!W)
849 void add_edge(const NodeID& u, const NodeID& v) {
850 invalidate_min_cost_flow_state();
851 VertexDesc bu = get_or_create_vertex(u);
852 VertexDesc bv = get_or_create_vertex(v);
853
854 if constexpr (!Multi) {
855 auto [e, exists] = boost::edge(bu, bv, g);
856 if (exists) {
857 return;
858 }
859 }
860
861 auto [e, added] = boost::add_edge(bu, bv, g);
862 (void)added;
863 assign_next_edge_id(e);
864 }
865
866 template <bool W = Weighted>
867 requires(!W)
874 std::size_t add_edge_with_id(const NodeID& u, const NodeID& v);
875
876
885 template <bool W = Weighted>
886 requires(W)
887 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w, const EdgeAttrMap& attrs);
888
896 void add_edge(const NodeID& u, const NodeID& v, const EdgeAttrMap& attrs);
897
898 template <bool W = Weighted>
899 requires(W)
908 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w, const std::pair<std::string, std::any>& attr);
909
911 void add_edge(const NodeID& u, const NodeID& v, const std::pair<std::string, std::any>& attr);
912
913 template <bool W = Weighted>
914 requires(W)
923 void add_edge(const NodeID& u, const NodeID& v, EdgeWeight w, std::initializer_list<std::pair<std::string, std::any>> attrs);
924
926 void add_edge(const NodeID& u, const NodeID& v, std::initializer_list<std::pair<std::string, std::any>> attrs);
927 template <bool W = Weighted>
928 requires(W)
930 void add_edges_from(const std::vector<std::tuple<NodeID, NodeID, EdgeWeight>>& edges) {
931 for (const auto& edge : edges) {
932 add_edge(std::get<0>(edge), std::get<1>(edge), std::get<2>(edge));
933 }
934 }
935
937 void add_edges_from(const std::vector<std::pair<NodeID, NodeID>>& edges) {
938 for (const auto& edge : edges) {
939 add_edge(edge.first, edge.second);
940 }
941 }
942
950 void clear() {
951 invalidate_min_cost_flow_state();
952 g = GraphType();
953 id_to_bgl.clear();
954 bgl_to_id.clear();
955 node_properties.clear();
956 edge_properties.clear();
958 vertex_name_map = boost::get(boost::vertex_name, g);
959 vertex_index_map = boost::get(boost::vertex_wrapper_index, g);
960 edge_id_map = boost::get(boost::edge_index, g);
961 next_edge_id = 0;
962 }
963
978 template <typename NodeRange>
979 [[nodiscard]] Graph subgraph(const NodeRange& selected_nodes) const {
980 return build_subgraph(selected_nodes);
981 }
982
984 [[nodiscard]] Graph subgraph(std::initializer_list<NodeID> selected_nodes) const {
985 return build_subgraph(selected_nodes);
986 }
987
997 void remove_edge(const NodeID& u, const NodeID& v) {
998 invalidate_min_cost_flow_state();
999 auto it_u = id_to_bgl.find(u);
1000 auto it_v = id_to_bgl.find(v);
1001 if (it_u == id_to_bgl.end() || it_v == id_to_bgl.end()) {
1002 throw std::runtime_error("Node lookup failed: node not found.");
1003 }
1004 auto [e, exists] = boost::edge(it_u->second, it_v->second, g);
1005 if (!exists) {
1006 throw std::runtime_error("Edge lookup failed: edge not found.");
1007 }
1008 for (auto edge_id : collect_edge_ids_between(it_u->second, it_v->second)) {
1009 edge_properties.erase(edge_id);
1010 erase_edge_id_index(edge_id);
1011 }
1012 if constexpr (!Directed && Multi) {
1013 const auto bu = it_u->second;
1014 const auto bv = it_v->second;
1015 boost::remove_edge_if(
1016 [this, bu, bv](const EdgeDesc& edge) {
1017 const auto source = boost::source(edge, g);
1018 const auto target = boost::target(edge, g);
1019 return (source == bu && target == bv) || (source == bv && target == bu);
1020 },
1021 g
1022 );
1023 } else {
1024 boost::remove_edge(it_u->second, it_v->second, g);
1025 }
1026 }
1027
1033 void remove_edge(std::size_t edge_id);
1034
1051 void remove_node(const NodeID& u) {
1052 invalidate_min_cost_flow_state();
1053 auto it = id_to_bgl.find(u);
1054 if (it == id_to_bgl.end()) {
1055 throw std::runtime_error("Node lookup failed: node not found.");
1056 }
1057 VertexDesc v = it->second;
1058
1059 erase_incident_edge_properties(v);
1060 boost::clear_vertex(v, g);
1061 boost::remove_vertex(v, g);
1062
1063 node_properties.erase(u);
1064 rebuild_vertex_maps();
1065 rebuild_edge_id_index();
1066 }
1067
1079 void remove_nodes_from(const std::vector<NodeID>& nodes) {
1080 std::vector<NodeID> unique_nodes;
1081 std::set<NodeID> seen;
1082 for (const auto& node : nodes) {
1083 if (seen.insert(node).second) {
1084 unique_nodes.push_back(node);
1085 }
1086 }
1087
1088 for (const auto& node : unique_nodes) {
1089 if (!has_node(node)) {
1090 throw std::runtime_error("Node lookup failed: node not found.");
1091 }
1092 }
1093
1094 if (unique_nodes.empty()) {
1095 return;
1096 }
1097
1098 invalidate_min_cost_flow_state();
1099 for (const auto& node : unique_nodes) {
1100 auto vertex = find_vertex_by_id(node);
1101 if (!vertex.has_value()) {
1102 continue;
1103 }
1104
1105 erase_incident_edge_properties(*vertex);
1106 boost::clear_vertex(*vertex, g);
1107 boost::remove_vertex(*vertex, g);
1108 node_properties.erase(node);
1109 }
1110
1111 rebuild_vertex_maps();
1112 rebuild_edge_id_index();
1113 }
1114
1121 [[nodiscard]] std::vector<NodeID> neighbors(const NodeID& u) const {
1122 auto it = id_to_bgl.find(u);
1123 if (it == id_to_bgl.end()) {
1124 throw std::runtime_error("Node lookup failed: node not found.");
1125 }
1126 std::vector<NodeID> res;
1127 for (auto [e, eend] = boost::out_edges(it->second, g); e != eend; ++e) {
1128 res.push_back(node_id_of(boost::target(*e, g)));
1129 }
1130 return res;
1131 }
1132
1134 [[nodiscard]] std::vector<NodeID> successors(const NodeID& u) const {
1135 return neighbors(u);
1136 }
1137
1144 [[nodiscard]] std::vector<NodeID> predecessors(const NodeID& u) const {
1145 auto it = id_to_bgl.find(u);
1146 if (it == id_to_bgl.end()) {
1147 throw std::runtime_error("Node lookup failed: node not found.");
1148 }
1149
1150 std::vector<NodeID> res;
1151 if constexpr (Directed) {
1152 for (auto [e, eend] = boost::in_edges(it->second, g); e != eend; ++e) {
1153 res.push_back(node_id_of(boost::source(*e, g)));
1154 }
1155 } else {
1156 for (auto [e, eend] = boost::out_edges(it->second, g); e != eend; ++e) {
1157 res.push_back(node_id_of(boost::target(*e, g)));
1158 }
1159 }
1160 return res;
1161 }
1162
1164 [[nodiscard]] bool has_node(const NodeID& u) const {
1165 return id_to_bgl.find(u) != id_to_bgl.end();
1166 }
1167
1168
1175 [[nodiscard]] bool has_node_attr(const NodeID& u, const std::string& key) const;
1183 [[nodiscard]] bool has_edge_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
1185 [[nodiscard]] bool has_edge_attr(std::size_t edge_id, const std::string& key) const;
1186
1187 template <typename T>
1194 [[nodiscard]] T get_node_attr(const NodeID& u, const std::string& key) const;
1195
1196 template <typename T>
1203 [[nodiscard]] T get_edge_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
1204
1205 template <typename T>
1211 [[nodiscard]] T get_edge_attr(std::size_t edge_id, const std::string& key) const;
1212
1213 template <typename T>
1220 [[nodiscard]] std::optional<T> try_get_node_attr(const NodeID& u, const std::string& key) const;
1221
1222 template <typename T>
1230 [[nodiscard]] std::optional<T> try_get_edge_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
1231
1232 template <typename T>
1234 [[nodiscard]] std::optional<T> try_get_edge_attr(std::size_t edge_id, const std::string& key) const;
1235
1243 [[nodiscard]] double get_edge_numeric_attr(const NodeID& u, const NodeID& v, const std::string& key) const;
1245 [[nodiscard]] double get_edge_numeric_attr(std::size_t edge_id, const std::string& key) const;
1246
1247 template <bool W = Weighted>
1248 requires(W)
1256 [[nodiscard]] EdgeWeight get_edge_weight(const NodeID& u, const NodeID& v) const;
1257
1258 template <bool W = Weighted>
1259 requires(W)
1261 [[nodiscard]] EdgeWeight get_edge_weight(std::size_t edge_id) const;
1262
1263 template <bool W = Weighted>
1264 requires(W)
1266 void set_edge_weight(std::size_t edge_id, EdgeWeight w);
1267
1268 template <typename T>
1275 void set_edge_attr(std::size_t edge_id, const std::string& key, const T& value);
1276
1283 [[nodiscard]] std::vector<NodeID> nodes() const {
1284 std::vector<NodeID> res;
1285 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
1286 res.push_back(node_id_of(*v));
1287 }
1288 return res;
1289 }
1290
1298 [[nodiscard]] std::vector<std::pair<NodeID, NodeID>> edges() const {
1299 std::vector<std::pair<NodeID, NodeID>> res;
1300 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
1301 NodeID source_id = node_id_of(boost::source(*e, g));
1302 NodeID target_id = node_id_of(boost::target(*e, g));
1303 res.emplace_back(source_id, target_id);
1304 }
1305 return res;
1306 }
1307
1309 [[nodiscard]] std::vector<std::pair<NodeID, NodeID>> edge_pairs() const {
1310 return edges();
1311 }
1312
1314 template <bool W = Weighted>
1315 requires(W)
1316 [[nodiscard]] std::vector<std::tuple<NodeID, NodeID, EdgeWeight>> weighted_edges() const {
1317 std::vector<std::tuple<NodeID, NodeID, EdgeWeight>> res;
1318 for (auto [e, eend] = boost::edges(g); e != eend; ++e) {
1319 NodeID source_id = node_id_of(boost::source(*e, g));
1320 NodeID target_id = node_id_of(boost::target(*e, g));
1321 res.emplace_back(source_id, target_id, weight_map[*e]);
1322 }
1323 return res;
1324 }
1325
1327 [[nodiscard]] const GraphType& get_impl() const { return g; }
1329 [[nodiscard]] const std::vector<NodeID>& get_bgl_to_id_map() const { return bgl_to_id; }
1331 [[nodiscard]] const IdMap& get_id_to_bgl_map() const { return id_to_bgl; }
1333 [[nodiscard]] const AttrMap& node_attrs(const NodeID& u) const {
1334 static const AttrMap empty;
1335 auto it = node_properties.find(u);
1336 return it == node_properties.end() ? empty : it->second;
1337 }
1339 [[nodiscard]] const AttrMap& edge_attrs(std::size_t edge_id) const {
1340 static const AttrMap empty;
1341 auto it = edge_properties.find(edge_id);
1342 return it == edge_properties.end() ? empty : it->second;
1343 }
1345 [[nodiscard]] const NodeID& get_node_id(VertexDesc v) const { return node_id_of(v); }
1347 [[nodiscard]] std::size_t get_vertex_index(VertexDesc v) const { return vertex_index_of(v); }
1348
1349 // Proxy Pattern per simulare G[u][v] = weight
1351 Graph* graph;
1352 NodeID u, v;
1353 std::string key;
1354
1360 template <typename T>
1361 EdgeAttrProxy& operator=(const T& val) {
1362 if (!graph->has_edge(u, v)) {
1363 if constexpr (Weighted) graph->add_edge(u, v, static_cast<EdgeWeight>(1.0));
1364 else graph->add_edge(u, v);
1365 }
1366 auto e = graph->get_edge_desc(u, v);
1367 graph->edge_properties[graph->get_edge_id(e)][key] = Graph::make_attr_any(val);
1368 return *this;
1369 }
1370
1371 template <typename T>
1372 operator T() const {
1373 return graph->template get_edge_attr<T>(u, v, key);
1374 }
1375 };
1376
1378 Graph* graph;
1379 NodeID u, v;
1380 public:
1381 EdgeProxy(Graph* g, NodeID u, NodeID v) : graph(g), u(u), v(v) {}
1382
1384 template <bool W = Weighted>
1385 requires(W)
1386 EdgeProxy& operator=(EdgeWeight w) {
1387 graph->add_edge(u, v, w);
1388 return *this;
1389 }
1390
1391 template <bool W = Weighted>
1392 requires(W)
1393 operator EdgeWeight() const {
1394 return graph->get_edge_weight(u, v);
1395 }
1396
1397 EdgeAttrProxy operator[](const std::string& key) {
1398 return {graph, u, v, key};
1399 }
1400
1401 EdgeAttrProxy operator[](const char* key) {
1402 return {graph, u, v, std::string(key)};
1403 }
1404 };
1405
1407 Graph* graph;
1408 NodeID u;
1409 std::string key;
1410
1415 template <typename T>
1416 NodeAttrProxy& operator=(const T& val) {
1417 if (!graph->has_node(u)) graph->add_node(u);
1418 graph->node_properties[u][key] = Graph::make_attr_any(val);
1419 return *this;
1420 }
1421
1422 template <typename T>
1423 operator T() const {
1424 return graph->template get_node_attr<T>(u, key);
1425 }
1426 };
1427
1429 const Graph* graph;
1430 NodeID u, v;
1431 std::string key;
1432
1433 template <typename T>
1434 operator T() const {
1435 return graph->template get_edge_attr<T>(u, v, key);
1436 }
1437 };
1438
1440 const Graph* graph;
1441 NodeID u, v;
1442 public:
1443 ConstEdgeProxy(const Graph* g, NodeID u, NodeID v) : graph(g), u(u), v(v) {}
1444
1445 template <bool W = Weighted>
1446 requires(W)
1447 operator EdgeWeight() const {
1448 return graph->get_edge_weight(u, v);
1449 }
1450
1451 ConstEdgeAttrProxy operator[](const std::string& key) const {
1452 return {graph, u, v, key};
1453 }
1454
1455 ConstEdgeAttrProxy operator[](const char* key) const {
1456 return {graph, u, v, std::string(key)};
1457 }
1458 };
1459
1461 Graph* graph;
1462 NodeID u;
1463 public:
1464 NodeProxy(Graph* g, NodeID u) : graph(g), u(u) {}
1465
1466 EdgeProxy operator[](const NodeID& v) {
1467 return EdgeProxy(graph, u, v);
1468 }
1469 };
1470
1472 const Graph* graph;
1473 NodeID u;
1474 public:
1475 ConstNodeProxy(const Graph* g, NodeID u) : graph(g), u(u) {}
1476
1477 ConstEdgeProxy operator[](const NodeID& v) const {
1478 return ConstEdgeProxy(graph, u, v);
1479 }
1480 };
1481
1483 Graph* graph;
1484 NodeID u;
1485 public:
1486 NodeAttrBaseProxy(Graph* g, NodeID u) : graph(g), u(u) {}
1487
1488 NodeAttrProxy operator[](const std::string& key) {
1489 return {graph, u, key};
1490 }
1491
1492 NodeAttrProxy operator[](const char* key) {
1493 return {graph, u, std::string(key)};
1494 }
1495 };
1496
1510 NodeProxy operator[](const NodeID& u) {
1511 if (!has_node(u)) {
1512 add_node(u);
1513 }
1514 return NodeProxy(this, u);
1515 }
1516
1523 ConstNodeProxy operator[](const NodeID& u) const {
1524 if (!has_node(u)) {
1525 throw std::out_of_range("Graph::operator[] const: node not found");
1526 }
1527 return ConstNodeProxy(this, u);
1528 }
1529
1535 NodeAttrBaseProxy node(const NodeID& u) {
1536 if (!has_node(u)) add_node(u);
1537 return NodeAttrBaseProxy(this, u);
1538 }
1539
1548 [[nodiscard]] auto bfs_edges(const NodeID& start) const;
1556 [[nodiscard]] auto bfs_edges_view(const NodeID& start) const;
1567 [[nodiscard]] auto bfs_tree(const NodeID& start) const;
1579 [[nodiscard]] auto bfs_successors(const NodeID& start) const;
1591 template <typename Visitor>
1592 void breadth_first_search(const NodeID& start, Visitor& visitor) const;
1607 template <typename OnVertex, typename OnTreeEdge>
1608 void bfs_visit(const NodeID& start, OnVertex&& on_vertex, OnTreeEdge&& on_tree_edge) const;
1617 [[nodiscard]] auto dfs_edges(const NodeID& start) const;
1625 [[nodiscard]] auto dfs_edges_view(const NodeID& start) const;
1636 [[nodiscard]] auto dfs_tree(const NodeID& start) const;
1647 [[nodiscard]] auto dfs_predecessors(const NodeID& start) const;
1659 [[nodiscard]] auto dfs_successors(const NodeID& start) const;
1671 template <typename Visitor>
1672 void depth_first_search(const NodeID& start, Visitor& visitor) const;
1687 template <typename OnTreeEdge, typename OnBackEdge>
1688 void dfs_visit(const NodeID& start, OnTreeEdge&& on_tree_edge, OnBackEdge&& on_back_edge) const;
1689
1698 [[nodiscard]] auto shortest_path(const NodeID& source_id, const NodeID& target_id) const;
1708 [[nodiscard]] auto shortest_path(const NodeID& source_id, const NodeID& target_id, WeightMode mode) const;
1721 [[nodiscard]] auto shortest_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1730 [[nodiscard]] double shortest_path_length(const NodeID& source_id, const NodeID& target_id) const;
1740 [[nodiscard]] double shortest_path_length(const NodeID& source_id, const NodeID& target_id, WeightMode mode) const;
1753 [[nodiscard]] double shortest_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1754
1763 template <bool W = Weighted>
1764 requires(W)
1765 [[nodiscard]] auto dijkstra_path(const NodeID& source_id, const NodeID& target_id) const;
1766 template <bool W = Weighted>
1767 requires(W)
1768 [[nodiscard]] auto dijkstra_path(const NodeID& source_id, const NodeID& target_id, WeightMode mode) const;
1769
1782 template <bool W = Weighted>
1783 requires(W)
1784 [[nodiscard]] auto dijkstra_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1785
1794 template <bool W = Weighted>
1795 requires(W)
1796 [[nodiscard]] auto dijkstra_shortest_paths(const NodeID& source_id) const;
1797
1805 template <bool W = Weighted>
1806 requires(W)
1807 [[nodiscard]] auto dijkstra_path_length(const NodeID& source_id) const;
1808
1817 template <bool W = Weighted>
1818 requires(W)
1819 [[nodiscard]] auto dijkstra_path_length(const NodeID& source_id, const NodeID& target_id) const;
1820 template <bool W = Weighted>
1821 requires(W)
1822 [[nodiscard]] auto dijkstra_path_length(const NodeID& source_id, const NodeID& target_id, WeightMode mode) const;
1823
1836 template <bool W = Weighted>
1837 requires(W)
1838 [[nodiscard]] auto dijkstra_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1839
1848 template <bool W = Weighted>
1849 requires(W)
1850 [[nodiscard]] auto bellman_ford_path(const NodeID& source_id, const NodeID& target_id) const;
1851 template <bool W = Weighted>
1852 requires(W)
1853 [[nodiscard]] auto bellman_ford_path(const NodeID& source_id, const NodeID& target_id, WeightMode mode) const;
1854
1863 template <bool W = Weighted>
1864 requires(W)
1865 [[nodiscard]] auto bellman_ford_shortest_paths(const NodeID& source_id) const;
1866
1876 template <bool W = Weighted>
1877 requires(W)
1878 [[nodiscard]] auto bellman_ford_path(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1879
1888 template <bool W = Weighted>
1889 requires(W)
1890 [[nodiscard]] auto bellman_ford_path_length(const NodeID& source_id, const NodeID& target_id) const;
1891 template <bool W = Weighted>
1892 requires(W)
1893 [[nodiscard]] auto bellman_ford_path_length(const NodeID& source_id, const NodeID& target_id, WeightMode mode) const;
1894
1904 template <bool W = Weighted>
1905 requires(W)
1906 [[nodiscard]] auto bellman_ford_path_length(const NodeID& source_id, const NodeID& target_id, const std::string& weight) const;
1907
1916 template <bool W = Weighted>
1917 requires(W)
1918 [[nodiscard]] auto dag_shortest_paths(const NodeID& source_id) const;
1919
1930 template <bool W = Weighted>
1931 requires(W)
1932 [[nodiscard]] auto floyd_warshall_all_pairs_shortest_paths() const;
1933
1944 template <bool W = Weighted>
1945 requires(W)
1946 [[nodiscard]] auto floyd_warshall_all_pairs_shortest_paths_map() const;
1947
1949 [[nodiscard]] auto connected_component_groups() const;
1951 [[nodiscard]] auto connected_components() const;
1953 [[nodiscard]] auto strongly_connected_component_groups() const;
1955 [[nodiscard]] auto strong_component_map() const;
1957 [[nodiscard]] auto strong_components() const;
1959 [[nodiscard]] auto connected_component_map() const;
1961 [[nodiscard]] auto strongly_connected_components() const;
1963 [[nodiscard]] auto strongly_connected_component_map() const;
1965 [[nodiscard]] auto strongly_connected_component_roots() const;
1967 [[nodiscard]] auto topological_sort() const;
1968
1976 template <bool W = Weighted>
1977 requires(W)
1978 [[nodiscard]] auto kruskal_minimum_spanning_tree() const;
1979
1989 template <bool W = Weighted>
1990 requires(W)
1991 [[nodiscard]] auto prim_minimum_spanning_tree(const NodeID& root_id) const;
1992
2000 template <bool W = Weighted>
2001 requires(W)
2002 [[nodiscard]] auto minimum_spanning_tree() const;
2003
2012 template <bool W = Weighted>
2013 requires(W)
2014 [[nodiscard]] auto minimum_spanning_tree(const NodeID& root_id) const;
2015
2026 [[nodiscard]] auto edmonds_karp_maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
2037 [[nodiscard]] auto push_relabel_maximum_flow_result(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
2048 [[nodiscard]] auto maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
2059 [[nodiscard]] auto minimum_cut(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr = "capacity") const;
2071 [[nodiscard]] 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;
2073 [[nodiscard]] 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;
2086 [[nodiscard]] auto cycle_canceling(const std::string& weight_attr = "weight") const;
2098 [[nodiscard]] 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;
2110 [[nodiscard]] 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;
2122 [[nodiscard]] 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;
2123
2126 [[nodiscard]] auto num_vertices() const noexcept;
2129 [[nodiscard]] std::size_t num_edges() const noexcept;
2140 [[nodiscard]] auto degree_centrality() const;
2153 [[nodiscard]] auto pagerank(double tolerance = 1e-6, std::size_t max_iterations = 100) const;
2164 [[nodiscard]] auto betweenness_centrality() const;
2165
2166private:
2167 void invalidate_min_cost_flow_state() const {
2169 static_cast<const void*>(this)
2170 );
2171 }
2172
2173 void clear_min_cost_flow_state() const {
2174 detail::MinCostFlowCacheHooks<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>::clear(
2175 static_cast<const void*>(this)
2176 );
2177 }
2178
2179 [[nodiscard]] auto floyd_warshall_matrix_with_order() const;
2180};
2181
2182template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
2184 return static_cast<int>(boost::num_vertices(g));
2185}
2186
2187template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
2189 return static_cast<std::size_t>(boost::num_edges(g));
2190}
2191
2192
2193template <typename GraphWrapper>
2195[[deprecated("Use G.num_vertices() instead.")]]
2196[[nodiscard]] auto num_vertices(const GraphWrapper& G) {
2197 return G.num_vertices();
2198}
2199
2200template <typename GraphWrapper>
2202[[deprecated("Use G.num_edges() instead.")]]
2203[[nodiscard]] auto num_edges(const GraphWrapper& G) {
2204 return G.num_edges();
2205}
2206
2207
2222
2226
2239
2243
2258
2259} // namespace nxpp
Definition graph.hpp:1439
Definition graph.hpp:1471
Definition graph.hpp:1377
Definition graph.hpp:1482
Definition graph.hpp:1460
Graph wrapper around Boost Graph Library with Python-inspired helpers.
Definition graph.hpp:288
const IdMap & get_id_to_bgl_map() const
Returns the maintained node-ID-to-vertex translation table.
Definition graph.hpp:1331
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:710
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:220
void add_nodes_from(const std::vector< NodeID > &nodes)
Adds a batch of nodes from a vector of node IDs.
Definition graph.hpp:751
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:494
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:183
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:937
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:672
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:449
auto bellman_ford_shortest_paths(const NodeID &source_id) const
Returns distances and predecessors for all nodes using Bellman-Ford.
Definition shortest_paths.hpp:742
std::size_t num_edges() const noexcept
Returns the number of edges currently stored in the graph without materializing the edge list.
Definition graph.hpp:2188
auto dijkstra_path_length(const NodeID &source_id) const
Returns built-in-weight shortest-path distances from a source node.
Definition shortest_paths.hpp:533
auto dfs_predecessors(const NodeID &start) const
Returns the DFS predecessor assigned to each discovered node.
Definition traversal.hpp:671
bool has_node_attr(const NodeID &u, const std::string &key) const
Returns whether a node has the named attribute.
Definition attributes.hpp:114
auto dijkstra_shortest_paths(const NodeID &source_id) const
Returns distances and predecessors for all nodes reachable from a source.
Definition shortest_paths.hpp:499
auto bfs_tree(const NodeID &start) const
Materializes the breadth-first-search tree rooted at start.
Definition traversal.hpp:579
std::vector< std::tuple< NodeID, NodeID, EdgeWeight > > weighted_edges() const
Returns all weighted edges as (u, v, w) tuples.
Definition graph.hpp:1316
auto pagerank(double tolerance=1e-6, std::size_t max_iterations=100) const
Computes PageRank scores for every node.
Definition centrality.hpp:50
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:184
auto bfs_edges_view(const NodeID &start) const
Returns a lazy input range over breadth-first-search tree edges.
Definition traversal.hpp:570
EdgeDesc get_edge_desc(const NodeID &u, const NodeID &v) const
Returns the underlying Boost edge descriptor for an existing edge.
Definition graph.hpp:763
void remove_edge(const NodeID &u, const NodeID &v)
Removes all edges between two endpoints.
Definition graph.hpp:997
const AttrMap & edge_attrs(std::size_t edge_id) const
Returns the user-defined attributes stored on an edge ID, or an empty map.
Definition graph.hpp:1339
std::vector< NodeID > neighbors(const NodeID &u) const
Returns the outgoing neighbors of a node.
Definition graph.hpp:1121
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:146
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:704
Graph subgraph(std::initializer_list< NodeID > selected_nodes) const
Convenience overload for braced node lists such as G.subgraph({"A", "B"}).
Definition graph.hpp:984
std::vector< NodeID > predecessors(const NodeID &u) const
Returns predecessor nodes for directed graphs.
Definition graph.hpp:1144
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:618
auto num_vertices() const noexcept
Returns the number of vertices currently stored in the graph.
Definition graph.hpp:2183
auto betweenness_centrality() const
Computes normalized betweenness centrality for every node.
Definition centrality.hpp:133
std::vector< NodeID > nodes() const
Returns all node IDs currently stored in the graph.
Definition graph.hpp:1283
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:123
auto dfs_edges_view(const NodeID &start) const
Returns a lazy input range over depth-first-search tree edges.
Definition traversal.hpp:653
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:200
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:712
auto dfs_successors(const NodeID &start) const
Groups DFS tree children by their discovered parent.
Definition traversal.hpp:681
auto bfs_edges(const NodeID &start) const
Returns the tree edges discovered by breadth-first search.
Definition traversal.hpp:555
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:164
bool has_node(const NodeID &u) const
Returns whether the graph already contains the given node ID.
Definition graph.hpp:1164
const std::vector< NodeID > & get_bgl_to_id_map() const
Returns the maintained vertex-index-to-node-ID translation table.
Definition graph.hpp:1329
void depth_first_search(const NodeID &start, Visitor &visitor) const
Runs depth-first search with an object-style visitor.
Definition traversal.hpp:696
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:699
void add_node(const NodeID &id)
Ensures that a node with the given ID exists in the graph.
Definition graph.hpp:740
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:543
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:540
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:828
const GraphType & get_impl() const
Exposes the underlying Boost graph for advanced integrations.
Definition graph.hpp:1327
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:950
std::size_t get_vertex_index(VertexDesc v) const
Returns the wrapper-maintained dense vertex index used by algorithms.
Definition graph.hpp:1347
void breadth_first_search(const NodeID &start, Visitor &visitor) const
Runs breadth-first search with an object-style visitor.
Definition traversal.hpp:603
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:619
const AttrMap & node_attrs(const NodeID &u) const
Returns the user-defined attributes stored on a node, or an empty map.
Definition graph.hpp:1333
auto dfs_edges(const NodeID &start) const
Returns the tree edges discovered by depth-first search.
Definition traversal.hpp:632
auto bfs_successors(const NodeID &start) const
Groups BFS tree children by their discovered parent.
Definition traversal.hpp:588
void remove_node(const NodeID &u)
Removes a node and all of its incident edges.
Definition graph.hpp:1051
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:1345
void remove_nodes_from(const std::vector< NodeID > &nodes)
Removes a batch of nodes and all of their incident edges.
Definition graph.hpp:1079
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:798
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:589
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:20
std::vector< std::pair< NodeID, NodeID > > edge_pairs() const
Compatibility alias for edges().
Definition graph.hpp:1309
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:662
bool has_edge(const NodeID &u, const NodeID &v) const
Returns whether at least one edge exists between two endpoints.
Definition graph.hpp:779
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
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:1134
auto connected_components() const
Returns a node-to-component-label map for 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:907
std::vector< std::pair< NodeID, NodeID > > edges() const
Returns the public edge list.
Definition graph.hpp:1298
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:517
auto floyd_warshall_all_pairs_shortest_paths() const
Returns all-pairs shortest-path distances as a dense matrix.
Definition shortest_paths.hpp:898
Graph subgraph(const NodeRange &selected_nodes) const
Returns a materialized node-induced subgraph.
Definition graph.hpp:979
NodeAttrBaseProxy node(const NodeID &u)
Returns the proxy used for G.node(u)[key] node-attribute syntax.
Definition graph.hpp:1535
NodeProxy operator[](const NodeID &u)
Returns the proxy used for G[u][v] edge-access syntax.
Definition graph.hpp:1510
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:418
ConstNodeProxy operator[](const NodeID &u) const
Returns a non-mutating proxy for G[u][v] on const graphs.
Definition graph.hpp:1523
auto strongly_connected_components() const
Alias for strongly_connected_component_groups().
Definition components.hpp:227
Definition graph.hpp:158
const Value & operator[](const Key &key) const
Reads an existing value; this wrapper never inserts through operator[].
Definition graph.hpp:207
indexed_lookup_map()=default
Default-constructs an empty ordered lookup map.
Definition graph.hpp:123
const Value & operator[](const Key &key) const
Reads an existing value in const contexts; throws like at() if missing.
Definition graph.hpp:134
Minimal visitor interface for wrapper-level traversal callbacks.
Definition traversal.hpp:225
Definition graph.hpp:51
Definition graph.hpp:43
auto num_vertices(const GraphWrapper &G)
Deprecated free-function alias for num_vertices().
Definition graph.hpp:2196
auto num_edges(const GraphWrapper &G)
Deprecated free-function alias for num_edges().
Definition graph.hpp:2203
WeightMode
Explicit source-target shortest-path weighting mode.
Definition graph.hpp:117
Definition graph.hpp:1428
Definition graph.hpp:1350
EdgeAttrProxy & operator=(const T &val)
Sets attribute key on edge (u,v), creating the edge if it does not exist.
Definition graph.hpp:1361
Definition graph.hpp:1406
NodeAttrProxy & operator=(const T &val)
Sets attribute key on node u, creating the node if it does not exist.
Definition graph.hpp:1416
Definition graph.hpp:96