NuriKit v0.1.0b2
Loading...
Searching...
No Matches
graph.h
Go to the documentation of this file.
1//
2// Project NuriKit - Copyright 2023 SNU Compbio Lab.
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#ifndef NURI_CORE_GRAPH_GRAPH_H_
7#define NURI_CORE_GRAPH_GRAPH_H_
8
10#include <algorithm>
11#include <initializer_list>
12#include <iterator>
13#include <queue>
14#include <type_traits>
15#include <utility>
16#include <vector>
17
18#include <absl/algorithm/container.h>
19#include <absl/base/attributes.h>
20#include <absl/base/optimization.h>
21#include <absl/container/flat_hash_set.h>
22#include <absl/log/absl_check.h>
23#include <absl/log/absl_log.h>
24#include <boost/iterator/iterator_facade.hpp>
26
29#include "nuri/iterator.h"
30#include "nuri/meta.h"
31#include "nuri/utils.h"
32
33namespace nuri {
34namespace internal {
35 template <class Derived, class GT, class DT, bool is_const>
36 class DataIteratorBase
37 : public ProxyIterator<Derived, DT, std::random_access_iterator_tag, int> {
38 using Traits =
39 std::iterator_traits<typename DataIteratorBase::iterator_facade_>;
40
41 public:
42 using parent_type = const_if_t<is_const, GT>;
43
44 using iterator_category = typename Traits::iterator_category;
45 using value_type = typename Traits::value_type;
46 using difference_type = typename Traits::difference_type;
47 using pointer = typename Traits::pointer;
48 using reference = typename Traits::reference;
49
50 constexpr DataIteratorBase() noexcept = default;
51
52 constexpr DataIteratorBase(parent_type &graph,
53 difference_type index) noexcept
54 : graph_(&graph), index_(index) { }
55
56 protected:
57 using Parent = DataIteratorBase;
58
59 template <class Other,
60 std::enable_if_t<!std::is_same_v<Derived, Other>, int> = 0>
61 constexpr DataIteratorBase(const Other &other) noexcept
62 : graph_(other.graph_), index_(other.index_) { }
63
64 template <class Other,
65 std::enable_if_t<!std::is_same_v<Derived, Other>, int> = 0>
66 constexpr DataIteratorBase &operator=(const Other &other) noexcept {
67 graph_ = other.graph_;
68 index_ = other.index_;
69 return *this;
70 }
71
72 constexpr parent_type &graph() const noexcept { return *graph_; }
73
74 constexpr difference_type index() const noexcept { return index_; }
75
76 template <class Other,
77 std::enable_if_t<std::is_convertible_v<Other, Derived>, int> = 0>
78 constexpr bool equal(const Other &other) const noexcept {
79 return index_ == other.index_;
80 }
81
82 template <class Other,
83 std::enable_if_t<std::is_convertible_v<Other, Derived>, int> = 0>
84 constexpr difference_type distance_to(const Other &other) const noexcept {
85 return other.index_ - index_;
86 }
87
88 void increment() noexcept { ++index_; }
89 void decrement() noexcept { --index_; }
90 void advance(difference_type n) noexcept { index_ += n; }
91
92 private:
93 template <class, class, class, bool>
94 friend class DataIteratorBase;
95
96 friend class boost::iterator_core_access;
97
98 parent_type *graph_;
99 difference_type index_;
100 };
101
102 template <class GT, bool is_const>
103 class NodeWrapper;
104
105 template <class GT, bool is_const>
106 class AdjWrapper {
107 public:
108 using edge_value_type = const_if_t<is_const, typename GT::edge_data_type>;
109 using parent_type = const_if_t<is_const, GT>;
110
111 template <bool other_const>
112 using Other = AdjWrapper<GT, other_const>;
113
114 constexpr AdjWrapper(parent_type &graph, int src, int dst, int eid) noexcept
115 : src_(src), dst_(dst), eid_(eid), graph_(&graph) { }
116
117 template <bool other_const,
118 std::enable_if_t<is_const && !other_const, int> = 0>
119 constexpr AdjWrapper(const Other<other_const> &other) noexcept
120 : src_(other.src_), dst_(other.dst_), eid_(other.eid_),
121 graph_(other.graph_) { }
122
123 constexpr auto src() const noexcept { return graph_->node(src_); }
124 constexpr auto dst() const noexcept { return graph_->node(dst_); }
125
126 constexpr int eid() const noexcept { return eid_; }
127 constexpr edge_value_type &edge_data() const noexcept {
128 return graph_->edge_data(eid_);
129 }
130
131 constexpr Other<true> as_const() const noexcept { return *this; }
132
133 private:
134 template <class, bool>
135 friend class AdjWrapper;
136
137 friend GT;
138
139 int src_;
140 int dst_;
141 int eid_;
142 parent_type *graph_;
143 };
144
145 template <class GT, bool is_const>
146 class AdjIterator
147 : public DataIteratorBase<AdjIterator<GT, is_const>, GT,
148 AdjWrapper<GT, is_const>, is_const> {
149 using Base = typename AdjIterator::Parent;
150
151 public:
152 using typename Base::difference_type;
153 using typename Base::iterator_category;
154 using typename Base::pointer;
155 using typename Base::reference;
156 using typename Base::value_type;
157
158 using typename Base::parent_type;
159
160 template <bool other_const>
161 using Other = AdjIterator<GT, other_const>;
162
163 constexpr AdjIterator() noexcept = default;
164
165 constexpr AdjIterator(parent_type &graph, difference_type idx,
166 difference_type nid) noexcept
167 : Base(graph, idx), nid_(nid) { }
168
169 template <bool other_const,
170 std::enable_if_t<is_const && !other_const, int> = 0>
171 constexpr AdjIterator(const Other<other_const> &other) noexcept
172 : Base(other), nid_(other.nid_) { }
173
174 template <bool other_const,
175 std::enable_if_t<is_const && !other_const, int> = 0>
176 constexpr AdjIterator &operator=(const Other<other_const> &other) noexcept {
177 Base::operator=(other);
178 nid_ = other.nid_;
179 return *this;
180 }
181
182 constexpr bool begin() const noexcept {
183 return Base::equal(this->graph().adj_begin(nid_));
184 }
185
186 constexpr bool end() const noexcept {
187 return Base::equal(this->graph().adj_end(nid_));
188 }
189
190 private:
191 friend Base;
192
193 friend class boost::iterator_core_access;
194
195 template <class, bool>
196 friend class AdjIterator;
197
198 template <
199 class Other,
200 std::enable_if_t<std::is_convertible_v<Other, AdjIterator>, int> = 0>
201 constexpr bool equal(const Other &other) const noexcept {
202 return nid_ == other.nid_ && Base::equal(other);
203 }
204
205 constexpr reference dereference() const noexcept {
206 return this->graph().adjacent(nid_, this->index());
207 }
208
209 int nid_;
210 };
211
212 template <class GT, bool is_const>
213 class NodeWrapper {
214 public:
215 using DT = typename GT::node_data_type;
216
217 using parent_type = const_if_t<is_const, GT>;
218 using value_type = const_if_t<is_const, DT>;
219
220 using adjacency_iterator =
221 std::conditional_t<is_const, typename GT::const_adjacency_iterator,
222 typename GT::adjacency_iterator>;
223
224 template <bool other_const>
225 using Other = NodeWrapper<GT, other_const>;
226
227 constexpr NodeWrapper(int nid, parent_type &graph) noexcept
228 : nid_(nid), graph_(&graph) { }
229
230 template <bool other_const,
231 std::enable_if_t<is_const && !other_const, int> = 0>
232 constexpr NodeWrapper(const Other<other_const> &other) noexcept
233 : nid_(other.nid_), graph_(other.graph_) { }
234
235 constexpr int id() const noexcept { return nid_; }
236
237 constexpr value_type &data() const noexcept {
238 return graph_->node_data(nid_);
239 }
240
241 constexpr int degree() const noexcept { return graph_->degree(nid_); }
242
243 adjacency_iterator begin() const noexcept {
244 return graph_->adj_begin(nid_);
245 }
246
247 adjacency_iterator end() const noexcept { return graph_->adj_end(nid_); }
248
249 AdjWrapper<GT, is_const> neighbor(int idx) const noexcept {
250 return graph_->adjacent(nid_, idx);
251 }
252
253 AdjWrapper<GT, is_const> operator[](int idx) const noexcept {
254 return neighbor(idx);
255 }
256
257 adjacency_iterator find_adjacent(int aid) const noexcept {
258 return graph_->find_adjacent(nid_, aid);
259 }
260
261 adjacency_iterator
262 find_adjacent(NodeWrapper<GT, true> node) const noexcept {
263 return find_adjacent(node.id());
264 }
265
266 constexpr Other<true> as_const() const noexcept { return *this; }
267
268 private:
269 template <class, bool>
270 friend class NodeWrapper;
271
272 int nid_;
273 parent_type *graph_;
274 };
275
276 template <class GT, bool is_const>
277 class NodeIterator
278 : public DataIteratorBase<NodeIterator<GT, is_const>, GT,
279 NodeWrapper<GT, is_const>, is_const> {
280 using Base = typename NodeIterator::Parent;
281
282 public:
283 using typename Base::difference_type;
284 using typename Base::iterator_category;
285 using typename Base::pointer;
286 using typename Base::reference;
287 using typename Base::value_type;
288
289 using Base::Base;
290
291 template <bool other_const,
292 std::enable_if_t<is_const && !other_const, int> = 0>
293 constexpr NodeIterator(const NodeIterator<GT, other_const> &other) noexcept
294 : Base(other) { }
295
296 template <bool other_const,
297 std::enable_if_t<is_const && !other_const, int> = 0>
298 constexpr NodeIterator &
299 operator=(const NodeIterator<GT, other_const> &other) noexcept {
300 Base::operator=(other);
301 return *this;
302 }
303
304 private:
305 friend Base;
306
307 friend class boost::iterator_core_access;
308
309 template <class, bool other_const>
310 friend class NodeIterator;
311
312 constexpr reference dereference() const noexcept {
313 return this->graph().node(this->index());
314 }
315 };
316
317 template <class GT, bool is_const>
318 class EdgeWrapper {
319 public:
320 using DT = typename GT::edge_data_type;
321
322 using parent_type = const_if_t<is_const, GT>;
323 using value_type = const_if_t<is_const, DT>;
324
325 template <bool other_const>
326 using Other = EdgeWrapper<GT, other_const>;
327
328 constexpr EdgeWrapper(int eid, parent_type &graph) noexcept
329 : eid_(eid), graph_(&graph) { }
330
331 template <bool other_const,
332 std::enable_if_t<is_const && !other_const, int> = 0>
333 constexpr EdgeWrapper(const Other<other_const> &other) noexcept
334 : eid_(other.eid_), graph_(other.graph_) { }
335
336 constexpr int id() const noexcept { return eid_; }
337
338 constexpr NodeWrapper<GT, is_const> src() const noexcept {
339 return graph_->edge_src(eid_);
340 }
341
342 constexpr NodeWrapper<GT, is_const> dst() const noexcept {
343 return graph_->edge_dst(eid_);
344 }
345
346 constexpr value_type &data() const noexcept {
347 return graph_->edge_data(eid_);
348 }
349
350 constexpr Other<true> as_const() const noexcept { return *this; }
351
352 private:
353 template <class, bool>
354 friend class EdgeWrapper;
355
356 int eid_;
357 parent_type *graph_;
358 };
359
360 template <class GT, bool is_const>
361 class EdgeIterator
362 : public DataIteratorBase<EdgeIterator<GT, is_const>, GT,
363 EdgeWrapper<GT, is_const>, is_const> {
364 using Base = typename EdgeIterator::Parent;
365
366 public:
367 using typename Base::difference_type;
368 using typename Base::iterator_category;
369 using typename Base::pointer;
370 using typename Base::reference;
371 using typename Base::value_type;
372
373 using Base::Base;
374
375 template <bool other_const,
376 std::enable_if_t<is_const && !other_const, int> = 0>
377 constexpr EdgeIterator(const EdgeIterator<GT, other_const> &other) noexcept
378 : Base(other) { }
379
380 template <bool other_const,
381 std::enable_if_t<is_const && !other_const, int> = 0>
382 constexpr EdgeIterator &
383 operator=(const EdgeIterator<GT, other_const> &other) noexcept {
384 Base::operator=(other);
385 return *this;
386 }
387
388 private:
389 friend Base;
390
391 friend class boost::iterator_core_access;
392
393 template <class, bool other_const>
394 friend class EdgeIterator;
395
396 constexpr reference dereference() const noexcept {
397 return this->graph().edge(this->index());
398 }
399 };
400
401 template <class GT, bool is_const>
402 class EdgesWrapper {
403 public:
404 template <class GU = GT,
405 std::enable_if_t<std::is_same_v<GU, GT> && !is_const, int> = 0>
406 EdgesWrapper(GT &graph): graph_(&graph) { }
407
408 EdgesWrapper(const GT &graph): graph_(&graph) { }
409
410 template <bool other_const>
411 using Other = EdgesWrapper<GT, other_const>;
412
413 template <bool other_const,
414 std::enable_if_t<is_const && !other_const, int> = 0>
415 EdgesWrapper(const Other<other_const> &other) noexcept
416 : graph_(other.graph_) { }
417
418 template <bool other_const,
419 std::enable_if_t<is_const && !other_const, int> = 0>
420 EdgesWrapper &operator=(const Other<other_const> &other) noexcept {
421 graph_ = other.graph_;
422 return *this;
423 }
424
425 auto operator[](int id) const { return graph_->edge(id); }
426
427 auto begin() const { return graph_->edge_begin(); }
428 auto end() const { return graph_->edge_end(); }
429
430 auto cbegin() const { return graph_->edge_cbegin(); }
431 auto cend() const { return graph_->edge_cend(); }
432
433 EdgesWrapper<GT, true> as_const() const { return *this; }
434
435 int size() const { return graph_->num_edges(); }
436
437 private:
438 const_if_t<is_const, GT> *graph_;
439 };
440
441 template <class, bool>
442 class SubEdgeWrapper;
443
444 template <class T>
445 struct OffsetForwarder {
446 template <class U>
447 T operator()(const U &edge) const {
448 return T { edge.src().id() + offset, edge.dst().id() + offset,
449 edge.data() };
450 }
451
452 int offset;
453 };
454} // namespace internal
455
456using NodesErased = std::pair<std::pair<int, std::vector<int>>,
457 std::pair<int, std::vector<int>>>;
458
459template <class NT, class ET, bool is_const>
460class Subgraph;
461
474template <class NT, class ET>
475class Graph {
476private:
477 struct AdjEntry {
478 int dst;
479 int eid;
480 };
481
482public:
483 struct StoredEdge {
484 int src;
485 int dst;
487 };
488
489 using node_data_type = NT;
490 using edge_data_type = ET;
491
492 using iterator = internal::NodeIterator<Graph, false>;
494 using const_iterator = internal::NodeIterator<Graph, true>;
496 using NodeRef = internal::NodeWrapper<Graph, false>;
497 using ConstNodeRef = internal::NodeWrapper<Graph, true>;
498
499 using edge_iterator = internal::EdgeIterator<Graph, false>;
500 using const_edge_iterator = internal::EdgeIterator<Graph, true>;
501 using EdgeRef = internal::EdgeWrapper<Graph, false>;
502 using ConstEdgeRef = internal::EdgeWrapper<Graph, true>;
503
504 using adjacency_iterator = internal::AdjIterator<Graph, false>;
505 using const_adjacency_iterator = internal::AdjIterator<Graph, true>;
506 using AdjRef = internal::AdjWrapper<Graph, false>;
507 using ConstAdjRef = internal::AdjWrapper<Graph, true>;
508
509 static_assert(std::is_same_v<typename iterator::reference, NodeRef>);
510 static_assert(
511 std::is_same_v<typename const_iterator::reference, ConstNodeRef>);
512 static_assert(std::is_same_v<typename edge_iterator::reference, EdgeRef>);
513 static_assert(
514 std::is_same_v<typename const_edge_iterator::reference, ConstEdgeRef>);
515 static_assert(std::is_same_v<typename adjacency_iterator::reference, AdjRef>);
516 static_assert(
517 std::is_same_v<typename const_adjacency_iterator::reference, ConstAdjRef>);
518
519 Graph() = default;
520 Graph(const Graph &) = default;
521 Graph(Graph &&) noexcept = default;
522 Graph &operator=(const Graph &) = default;
523 Graph &operator=(Graph &&) noexcept = default;
524 ~Graph() noexcept = default;
525
531 Graph(int num_nodes): offsets_(num_nodes + 1), nodes_(num_nodes) { }
532
539 Graph(int num_nodes, const NT &data)
540 : offsets_(num_nodes + 1), nodes_(num_nodes, data) { }
541
542 bool empty() const {
543 // GCOV_EXCL_START
544 ABSL_DCHECK(num_nodes() > 0 || num_edges() == 0)
545 << "The graph is empty (num_nodes() == 0) but num_edges() == "
546 << num_edges();
547 // GCOV_EXCL_STOP
548 return size() == 0;
549 }
550 int size() const { return num_nodes(); }
551 int num_nodes() const { return static_cast<int>(nodes_.size()); }
552
553 bool edge_empty() const {
554 // GCOV_EXCL_START
555 ABSL_DCHECK(num_nodes() > 0 || num_edges() == 0)
556 << "The graph is empty (num_nodes() == 0) but num_edges() == "
557 << num_edges();
558 // GCOV_EXCL_STOP
559 return num_edges() == 0;
560 }
561 int num_edges() const { return static_cast<int>(edges_.size()); }
562
563 int degree(int id) const { return offsets_[id + 1] - offsets_[id]; }
564
565 void reserve(int num_nodes) {
566 nodes_.reserve(num_nodes);
567 offsets_.reserve(num_nodes + 1);
568 }
569
571 edges_.reserve(num_edges);
572 adj_list_.reserve(num_edges * 2);
573 }
574
581 int add_node(const NT &data) {
582 int id = num_nodes();
583 nodes_.push_back(data);
584 offsets_.push_back(offsets_[id]);
585 return id;
586 }
587
594 int add_node(NT &&data) noexcept {
595 int id = num_nodes();
596 nodes_.push_back(std::move(data));
597 offsets_.push_back(offsets_[id]);
598 return id;
599 }
600
611 template <class Iterator,
612 internal::enable_if_compatible_iter_t<Iterator, NT> = 0>
613 int add_nodes(Iterator begin, Iterator end) {
614 const int first = num_nodes();
615 nodes_.insert(nodes_.end(), begin, end);
616 offsets_.resize(num_nodes() + 1, offsets_.back());
617 return first;
618 }
619
634 ABSL_DEPRECATED("Slow, due to CSR recompilation.")
635 int add_edge(int src, int dst, const ET &data) {
636 ABSL_DCHECK_NE(src, dst) << "self-loop is not allowed";
637
638 int eid = num_edges();
639 edges_.push_back({ src, dst, data });
640 publish_edges_from(eid);
641 return eid;
642 }
643
655 ABSL_DEPRECATED("Slow, due to CSR recompilation.")
656 int add_edge(int src, int dst, ET &&data) noexcept {
657 ABSL_DCHECK_NE(src, dst) << "self-loop is not allowed";
658
659 int eid = num_edges();
660 edges_.push_back({ src, dst, std::move(data) });
661 publish_edges_from(eid);
662 return eid;
663 }
664
679 template <class Iterator,
680 internal::enable_if_compatible_iter_t<Iterator, StoredEdge> = 0>
681 int add_edges(Iterator begin, Iterator end) {
682 const int first = num_edges();
683 edges_.insert(edges_.end(), begin, end);
684 publish_edges_from(first);
685 return first;
686 }
687
688 NodeRef operator[](int id) { return node(id); }
689 ConstNodeRef operator[](int id) const { return node(id); }
690
691 NodeRef node(int id) { return { id, *this }; }
692 ConstNodeRef node(int id) const { return { id, *this }; }
693
694 NT &node_data(int id) { return nodes_[id]; }
695 const NT &node_data(int id) const { return nodes_[id]; }
696
697 void clear() {
698 nodes_.clear();
699 edges_.clear();
700 adj_list_.clear();
701 offsets_ = { 0 };
702 }
703
725 return erase_nodes(begin, end, [](auto /* ref */) { return true; });
726 }
727
750 template <class UnaryPred>
752 UnaryPred pred);
753
774 template <class Iterator,
775 class = internal::enable_if_compatible_iter_t<Iterator, int>>
776 NodesErased erase_nodes(Iterator begin, Iterator end);
777
778 iterator begin() { return { *this, 0 }; }
779 iterator end() { return { *this, num_nodes() }; }
780 const_iterator begin() const { return cbegin(); }
781 const_iterator end() const { return cend(); }
782 const_iterator cbegin() const { return { *this, 0 }; }
783 const_iterator cend() const { return { *this, num_nodes() }; }
784
785 EdgeRef edge(int id) { return { id, *this }; }
786 ConstEdgeRef edge(int id) const { return { id, *this }; }
787
788 NodeRef edge_src(int id) { return this->node(edges_[id].src); }
789 ConstNodeRef edge_src(int id) const { return this->node(edges_[id].src); }
790
791 NodeRef edge_dst(int id) { return this->node(edges_[id].dst); }
792 ConstNodeRef edge_dst(int id) const { return this->node(edges_[id].dst); }
793
794 ET &edge_data(int id) { return edges_[id].data; }
795 const ET &edge_data(int id) const { return edges_[id].data; }
796
805 edge_iterator find_edge(int src, int dst) {
806 return find_edge_helper(*this, src, dst);
807 }
808
817 const_edge_iterator find_edge(int src, int dst) const {
818 return find_edge_helper(*this, src, dst);
819 }
820
831 return find_edge(src.id(), dst.id());
832 }
833
844 return find_edge(src.id(), dst.id());
845 }
846
851 void clear_edges() {
852 edges_.clear();
853 adj_list_.clear();
854 absl::c_fill(offsets_, 0);
855 }
856
865 void erase_edge(int id) {
866 const StoredEdge &edge = edges_[id];
867 auto srcit = find_adjacency_entry(edge.src, edge.dst),
868 dstit = find_adjacency_entry(edge.dst, edge.src);
869 erase_adjacency_entry(edge.src, srcit, edge.dst, dstit);
870
871 erase_edge_common(id);
872 }
873
884 bool erase_edge_between(int src, int dst);
885
898 return erase_edge_between(src.id(), dst.id());
899 }
900
919 std::pair<int, std::vector<int>> erase_edges(const_edge_iterator begin,
921 return erase_edges(begin, end, [](auto /* ref */) { return true; });
922 }
923
946 template <class UnaryPred>
947 std::pair<int, std::vector<int>> erase_edges(const_edge_iterator begin,
949 UnaryPred pred);
950
971 template <class Iterator,
972 class = internal::enable_if_compatible_iter_t<Iterator, int>>
973 std::pair<int, std::vector<int>> erase_edges(Iterator begin, Iterator end);
974
975 internal::EdgesWrapper<Graph, false> edges() { return { *this }; }
976 internal::EdgesWrapper<Graph, true> edges() const { return { *this }; }
977 internal::EdgesWrapper<Graph, true> cedges() const { return { *this }; }
978
979 edge_iterator edge_begin() { return { *this, 0 }; }
980 edge_iterator edge_end() { return { *this, num_edges() }; }
983 const_edge_iterator edge_cbegin() const { return { *this, 0 }; }
984 const_edge_iterator edge_cend() const { return { *this, num_edges() }; }
985
996 return find_adj_helper(*this, src, dst);
997 }
998
1008 const_adjacency_iterator find_adjacent(int src, int dst) const {
1009 return find_adj_helper(*this, src, dst);
1010 }
1011
1023 return find_adjacent(src.id(), dst.id());
1024 }
1025
1037 ConstNodeRef dst) const {
1038 return find_adjacent(src.id(), dst.id());
1039 }
1040
1041 adjacency_iterator adj_begin(int nid) { return { *this, 0, nid }; }
1042 adjacency_iterator adj_end(int nid) { return { *this, degree(nid), nid }; }
1043 const_adjacency_iterator adj_begin(int nid) const { return adj_cbegin(nid); }
1044 const_adjacency_iterator adj_end(int nid) const { return adj_cend(nid); }
1046 return { *this, 0, nid };
1047 }
1049 return { *this, degree(nid), nid };
1050 }
1051
1061 template <class GraphLike>
1062 void merge(const GraphLike &other) {
1063 const int offset = size();
1064 reserve(offset + other.size());
1065
1066 for (auto node: other)
1067 add_node(node.data());
1068
1069 auto edges = other.edges();
1070 reserve_edges(num_edges() + edges.size());
1071
1072 internal::OffsetForwarder<StoredEdge> as_stored_edge { offset };
1073 add_edges(internal::make_transform_iterator(edges.begin(), as_stored_edge),
1074 internal::make_transform_iterator(edges.end(), as_stored_edge));
1075 }
1076
1077private:
1078 template <class, bool>
1079 friend class internal::NodeWrapper;
1080
1081 template <class, bool>
1082 friend class internal::EdgeIterator;
1083
1084 template <class, bool>
1085 friend class internal::AdjIterator;
1086
1087 friend class Subgraph<NT, ET, false>;
1088 friend class Subgraph<NT, ET, true>;
1089
1090 template <class GT>
1091 static internal::AdjIterator<Graph, std::is_const_v<GT>>
1092 find_adj_helper(GT &graph, int src, int dst) {
1093 auto ret = graph.adj_begin(src);
1094
1095 for (; !ret.end(); ++ret)
1096 if (ret->dst().id() == dst)
1097 break;
1098
1099 return ret;
1100 }
1101
1102 template <class GT>
1103 static internal::EdgeIterator<Graph, std::is_const_v<GT>>
1104 find_edge_helper(GT &graph, int src, int dst) {
1105 if (graph.degree(src) > graph.degree(dst))
1106 return find_edge_helper(graph, dst, src);
1107
1108 auto ait = find_adj_helper(graph, src, dst);
1109 if (ait.end())
1110 return graph.edge_end();
1111
1112 return { graph, ait->eid_ };
1113 }
1114
1115 std::pair<int, std::vector<int>>
1116 erase_nodes_common(std::vector<int> &node_keep, int first_erased_id,
1117 bool erase_trailing);
1118
1119 void erase_edge_common(int id);
1120
1121 void erase_edges_common(std::vector<int> &edge_keep, int first_erased_id,
1122 bool erase_trailing);
1123
1140 void publish_edges_from(int first_eid);
1141
1142 auto find_adjacency_entry(int src, int dst) {
1143 return std::find_if(adj_list_.begin() + offsets_[src],
1144 adj_list_.begin() + offsets_[src + 1],
1145 [dst](const AdjEntry &adj) { return adj.dst == dst; });
1146 }
1147
1148 void erase_adjacency_entry(int src,
1149 typename std::vector<AdjEntry>::iterator srcit,
1150 int dst,
1151 typename std::vector<AdjEntry>::iterator dstit) {
1152 if (src > dst) {
1153 erase_adjacency_entry(dst, dstit, src, srcit);
1154 return;
1155 }
1156
1157 // Close the two gaps left by the removed entries via a pair of back-to-back
1158 // left shifts on the flat buffer.
1159 std::move(srcit + 1, dstit, srcit);
1160 std::move(dstit + 1, adj_list_.end(), dstit - 1);
1161 adj_list_.resize(adj_list_.size() - 2);
1162
1163 // Slices of nodes in (src, dst] shrink by 1 (one entry gone before them);
1164 // slices of nodes > dst shrink by 2.
1165 for (int v = src + 1; v <= dst; ++v)
1166 --offsets_[v];
1167 for (int v = dst + 1, n = static_cast<int>(offsets_.size()); v < n; ++v)
1168 offsets_[v] -= 2;
1169 }
1170
1171 AdjRef adjacent(int nid, int idx) {
1172 const AdjEntry &adj = adj_list_[offsets_[nid] + idx];
1173 return { *this, nid, adj.dst, adj.eid };
1174 }
1175
1176 ConstAdjRef adjacent(int nid, int idx) const {
1177 const AdjEntry &adj = adj_list_[offsets_[nid] + idx];
1178 return { *this, nid, adj.dst, adj.eid };
1179 }
1180
1181 std::vector<int> offsets_ = { 0 };
1182 std::vector<AdjEntry> adj_list_;
1183
1184 std::vector<NT> nodes_;
1185 std::vector<StoredEdge> edges_;
1186};
1187
1188/* Out-of-line definitions */
1189
1190template <class NT, class ET>
1191template <class Iterator, class>
1193 // Note: the time complexity notations are only for very sparse graphs, i.e.,
1194 // E = O(V).
1195 if (begin == end) {
1196 return {
1197 { num_nodes(), {} },
1198 { num_edges(), {} },
1199 };
1200 }
1201
1202 // Phase I: mark nodes for removal, O(V)
1203 std::vector<int> node_keep(num_nodes(), 1);
1204 int first_erased_id = num_nodes();
1205 for (auto it = begin; it != end; ++it) {
1206 int nid = *it;
1207 node_keep[nid] = 0;
1208 first_erased_id = std::min(first_erased_id, nid);
1209 }
1210
1211 bool erase_trailing = true;
1212 for (int i = num_nodes() - 1; i >= first_erased_id; --i) {
1213 if (node_keep[i] == 1) {
1214 erase_trailing = false;
1215 break;
1216 }
1217 }
1218
1219 auto edge_info =
1220 erase_nodes_common(node_keep, first_erased_id, erase_trailing);
1221 return {
1222 { erase_trailing ? first_erased_id : -1, std::move(node_keep) },
1223 std::move(edge_info),
1224 };
1225}
1226
1227template <class NT, class ET>
1228template <class UnaryPred>
1230 const const_iterator end,
1231 UnaryPred pred) {
1232 // Note: the time complexity notations are only for very sparse graphs, i.e.,
1233 // E = O(V).
1234
1235 // This will also handle size() == 0 case correctly.
1236 if (begin >= end) {
1237 return {
1238 { num_nodes(), {} },
1239 { num_edges(), {} },
1240 };
1241 }
1242
1243 // Phase I: mark nodes for removal, O(V)
1244 std::vector<int> node_keep(num_nodes(), 1);
1245 int first_erased_id = -1;
1246 bool erase_trailing = end == this->end();
1247 for (auto it = begin; it != end; ++it) {
1248 if (pred(*it)) {
1249 int nid = it->id();
1250
1251 // GCOV_EXCL_START
1252 ABSL_ASSUME(nid >= 0);
1253 // GCOV_EXCL_STOP
1254
1255 if (first_erased_id < 0)
1256 first_erased_id = nid;
1257
1258 node_keep[nid] = 0;
1259 } else if (first_erased_id >= 0) {
1260 erase_trailing = false;
1261 }
1262 }
1263
1264 auto edge_info =
1265 erase_nodes_common(node_keep, first_erased_id, erase_trailing);
1266 return {
1267 { erase_trailing ? first_erased_id : -1, std::move(node_keep) },
1268 std::move(edge_info),
1269 };
1270}
1271
1272template <class NT, class ET>
1273std::pair<int, std::vector<int>>
1274Graph<NT, ET>::erase_nodes_common(std::vector<int> &node_keep,
1275 const int first_erased_id,
1276 const bool erase_trailing) {
1277 // Fast path 1: no node is erased
1278 if (first_erased_id < 0 || first_erased_id >= num_nodes())
1279 return { num_edges(), {} };
1280
1281 // Phase II: erase the edges, O(V+E)
1282 auto ret = erase_edges(edge_begin(), edge_end(), [&](ConstEdgeRef edge) {
1283 return node_keep[edge.src().id()] == 0 || node_keep[edge.dst().id()] == 0;
1284 });
1285
1286 // Phase III: erase the nodes & adjacencies
1287 if (erase_trailing) {
1288 // Fast path 2: only trailing nodes with incident edges; the edge erase
1289 // above already compacted adj_list_, so just trim nodes_/offsets_.
1290 ABSL_DLOG(INFO) << "resizing offset table & node list";
1291 // O(1) operations
1292 nodes_.resize(first_erased_id);
1293 offsets_.resize(first_erased_id + 1);
1294 return ret;
1295 }
1296
1297 // Erase unused nodes, O(V)
1298 int i = 0;
1299 erase_if(nodes_,
1300 [&](const NT & /* unused */) { return node_keep[i++] == 0; });
1301
1302 // Phase IV: update the node numbers in adjacencies and edges, O(V+E)
1303 mask_to_map(node_keep);
1304
1305 for (int v = 0; v < static_cast<int>(node_keep.size()); ++v) {
1306 int w = node_keep[v];
1307 if (w < 0)
1308 continue;
1309 offsets_[w + 1] = offsets_[v + 1];
1310 }
1311 offsets_.resize(nodes_.size() + 1);
1312
1313 for (AdjEntry &adj: adj_list_)
1314 adj.dst = node_keep[adj.dst];
1315
1316 for (StoredEdge &edge: edges_) {
1317 edge.src = node_keep[edge.src];
1318 edge.dst = node_keep[edge.dst];
1319 }
1320
1321 return ret;
1322}
1323
1324template <class NT, class ET>
1325void Graph<NT, ET>::publish_edges_from(const int first_eid) {
1326 const int total_edges = num_edges();
1327 if (first_eid == total_edges)
1328 return;
1329
1330 const int n = num_nodes();
1331
1332 // Phase 1: count per-node delta from just the new edges; track v_min.
1333 std::vector<int> delta(n, 0);
1334 for (int eid = first_eid; eid < total_edges; ++eid) {
1335 const StoredEdge &e = edges_[eid];
1336 ++delta[e.src];
1337 ++delta[e.dst];
1338 }
1339 const int v_min = static_cast<int>(
1340 absl::c_find_if(delta, [](int d) { return d > 0; }) - delta.begin());
1341
1342 // Phase 2: grow the flat buffer by the total number of new endpoints.
1343 const int added = 2 * (total_edges - first_eid);
1344 adj_list_.resize(adj_list_.size() + added);
1345
1346 // Phase 3: back-to-front shift of existing slices; update offsets_ in place.
1347 //
1348 // Invariant on entry to iteration `v`: `shift == sum(delta[v..n-1])`, which
1349 // equals `shift_{v+1}` (= the offset-shift that was applied to node v+1's
1350 // slice). Subtracting `delta[v]` yields `shift_v` for the current node.
1351 int shift = added;
1352 for (int i = n - 1; i > v_min; --i) {
1353 const int old_end = offsets_[i + 1], old_start = offsets_[i],
1354 old_deg = old_end - old_start;
1355
1356 offsets_[i + 1] = old_end + shift;
1357 shift -= delta[i];
1358 const int new_start = old_start + shift;
1359 std::move_backward(adj_list_.begin() + old_start,
1360 adj_list_.begin() + old_end,
1361 adj_list_.begin() + new_start + old_deg);
1362 }
1363 offsets_[v_min + 1] += shift;
1364
1365 // GCOV_EXCL_START
1366 ABSL_DCHECK_EQ(shift, delta[v_min])
1367 << "shift mismatch: " << shift << " vs " << delta[v_min];
1368 // GCOV_EXCL_STOP
1369
1370 // Phase 4: place new adjacency entries in eid order.
1371 for (int eid = first_eid; eid < total_edges; ++eid) {
1372 const StoredEdge &e = edges_[eid];
1373 int sd = delta[e.src]--, dd = delta[e.dst]--;
1374 adj_list_[offsets_[e.src + 1] - sd] = { e.dst, eid };
1375 adj_list_[offsets_[e.dst + 1] - dd] = { e.src, eid };
1376 }
1377}
1378
1379template <class NT, class ET>
1381 if (degree(src) > degree(dst))
1382 return erase_edge_between(dst, src);
1383
1384 auto srcit = find_adjacency_entry(src, dst);
1385 if (srcit == adj_list_.begin() + offsets_[src + 1])
1386 return false;
1387
1388 int eid = srcit->eid;
1389 // NOLINTNEXTLINE(readability-suspicious-call-argument)
1390 auto dstit = find_adjacency_entry(dst, src);
1391 erase_adjacency_entry(src, srcit, dst, dstit);
1392
1393 erase_edge_common(eid);
1394
1395 return true;
1396}
1397
1398template <class NT, class ET>
1399void Graph<NT, ET>::erase_edge_common(int id) {
1400 const int orig_edges = num_edges();
1401 edges_.erase(edges_.begin() + id);
1402
1403 if (id == orig_edges - 1)
1404 return;
1405
1406 for (AdjEntry &adj: adj_list_) {
1407 if (adj.eid > id)
1408 --adj.eid;
1409 }
1410}
1411
1412template <class NT, class ET>
1413template <class UnaryPred>
1414std::pair<int, std::vector<int>>
1416 UnaryPred pred) {
1417 // This will also handle num_edges() == 0 case correctly.
1418 if (begin >= end)
1419 return { num_edges(), {} };
1420
1421 // Phase I: mark edges for removal, O(E)
1422 std::vector<int> edge_keep(num_edges(), 1);
1423 int first_erased_id = -1;
1424 bool erase_trailing = end == this->edge_end();
1425 for (auto it = begin; it != end; ++it) {
1426 if (pred(*it)) {
1427 int eid = it->id();
1428 // GCOV_EXCL_START
1429 ABSL_ASSUME(eid >= 0);
1430 // GCOV_EXCL_STOP
1431 if (first_erased_id < 0)
1432 first_erased_id = eid;
1433
1434 edge_keep[eid] = 0;
1435 } else if (first_erased_id >= 0) {
1436 erase_trailing = false;
1437 }
1438 }
1439
1440 erase_edges_common(edge_keep, first_erased_id, erase_trailing);
1441 return { erase_trailing ? first_erased_id : -1, std::move(edge_keep) };
1442}
1443
1444template <class NT, class ET>
1445template <class Iterator, class>
1446std::pair<int, std::vector<int>> Graph<NT, ET>::erase_edges(Iterator begin,
1447 Iterator end) {
1448 if (begin == end)
1449 return { num_edges(), {} };
1450
1451 // Phase I: mark edges for removal, O(E)
1452 std::vector<int> edge_keep(num_edges(), 1);
1453 int first_erased_id = num_edges();
1454 for (auto it = begin; it != end; ++it) {
1455 int eid = *it;
1456 edge_keep[eid] = 0;
1457 first_erased_id = std::min(first_erased_id, eid);
1458 }
1459
1460 bool erase_trailing = true;
1461 for (int i = num_edges() - 1; i >= first_erased_id; --i) {
1462 if (edge_keep[i] == 1) {
1463 erase_trailing = false;
1464 break;
1465 }
1466 }
1467
1468 erase_edges_common(edge_keep, first_erased_id, erase_trailing);
1469 return { erase_trailing ? first_erased_id : -1, std::move(edge_keep) };
1470}
1471
1472template <class NT, class ET>
1473void Graph<NT, ET>::erase_edges_common(std::vector<int> &edge_keep,
1474 int first_erased_id,
1475 bool erase_trailing) {
1476 // Fast path 1: no edge is erased
1477 if (first_erased_id < 0 || first_erased_id >= num_edges())
1478 return;
1479
1480 // Phase II: compact adj_list_ and rewrite offsets_ in place, O(V+E).
1481 const int n = num_nodes();
1482 int p = 0, old_end = 0;
1483 for (int i = 0; i < n; ++i) {
1484 const int old_start = old_end;
1485 old_end = offsets_[i + 1];
1486 for (int q = old_start; q < old_end; ++q) {
1487 if (edge_keep[adj_list_[q].eid] != 0)
1488 adj_list_[p++] = adj_list_[q];
1489 }
1490 offsets_[i + 1] = p;
1491 }
1492 adj_list_.resize(p);
1493
1494 // Fast path 2: if only trailing edges are erased, no edge number needs to
1495 // be updated.
1496 if (erase_trailing) {
1497 ABSL_DLOG(INFO) << "resizing edge list";
1498 // O(1) operation
1499 edges_.resize(first_erased_id);
1500 return;
1501 }
1502
1503 // Phase III: erase the edges, O(E)
1504 int i = 0;
1505 erase_if(edges_, [&](const StoredEdge & /* unused */) {
1506 return edge_keep[i++] == 0;
1507 });
1508
1509 // Phase IV: update the edge numbers in adjacencies, O(E)
1510 mask_to_map(edge_keep);
1511
1512 for (AdjEntry &adj: adj_list_)
1513 adj.eid = edge_keep[adj.eid];
1514}
1515
1516namespace internal {
1517 // NOLINTBEGIN(readability-identifier-naming)
1518 template <class NT, class ET>
1519 struct GraphTraits<Graph<NT, ET>> {
1520 constexpr static bool is_degree_constant_time = true;
1521 };
1522 // NOLINTEND(readability-identifier-naming)
1523
1524 template <class GT, bool is_const>
1525 class EigenNeighborIndexer {
1526 public:
1527 EigenNeighborIndexer(NodeWrapper<GT, is_const> node): node_(node) { }
1528
1529 int size() const { return node_.degree(); }
1530
1531 int operator[](int i) const { return node_[i].dst().id(); }
1532
1533 private:
1534 NodeWrapper<GT, is_const> node_;
1535 };
1536} // namespace internal
1537
1538template <class GT, bool is_const>
1539internal::EigenNeighborIndexer<GT, is_const>
1540as_index(internal::NodeWrapper<GT, is_const> node) {
1541 return { node };
1542}
1543
1544namespace internal {
1545 template <class NT, class ET>
1546 void connected_components_impl(const Graph<NT, ET> &g,
1547 absl::flat_hash_set<int> &visited,
1548 std::queue<int> &q) {
1549 while (!q.empty()) {
1550 int atom = q.front();
1551 q.pop();
1552
1553 auto [_, inserted] = visited.insert(atom);
1554 if (!inserted)
1555 continue;
1556
1557 for (auto ait = g.adj_begin(atom); !ait.end(); ++ait) {
1558 int dst = ait->dst().id();
1559 q.push(dst);
1560 }
1561 }
1562 }
1563} // namespace internal
1564
1577template <class NT, class ET>
1578absl::flat_hash_set<int> connected_components(const Graph<NT, ET> &g,
1579 int begin) {
1580 absl::flat_hash_set<int> visited;
1581 std::queue<int> q { { begin } };
1582
1583 internal::connected_components_impl(g, visited, q);
1584
1585 return visited;
1586}
1587
1605template <class NT, class ET>
1606absl::flat_hash_set<int> connected_components(const Graph<NT, ET> &g, int begin,
1607 int exclude) {
1608 absl::flat_hash_set<int> visited { begin };
1609 std::queue<int> q;
1610
1611 for (auto ait = g.adj_begin(begin); !ait.end(); ++ait) {
1612 int dst = ait->dst().id();
1613 if (dst != exclude)
1614 q.push(dst);
1615 }
1616
1617 internal::connected_components_impl(g, visited, q);
1618
1619 if (visited.contains(exclude))
1620 return {};
1621
1622 return visited;
1623}
1624} // namespace nuri
1625
1626#endif /* NURI_CORE_GRAPH_GRAPH_H_ */
Class for very sparse graphs, especially designed for the molecular graphs.
Definition graph.h:475
const ET & edge_data(int id) const
Definition graph.h:795
internal::EdgesWrapper< Graph, true > cedges() const
Definition graph.h:977
int add_node(const NT &data)
Add a node to the graph.
Definition graph.h:581
int degree(int id) const
Definition graph.h:563
void reserve(int num_nodes)
Definition graph.h:565
void erase_edge(int id)
Erase an edge from the graph.
Definition graph.h:865
ConstNodeRef node(int id) const
Definition graph.h:692
int size() const
Definition graph.h:550
bool empty() const
Definition graph.h:542
int add_node(NT &&data) noexcept
Add a node to the graph.
Definition graph.h:594
const NT & node_data(int id) const
Definition graph.h:695
internal::NodeWrapper< Graph, true > ConstNodeRef
Definition graph.h:497
NT & node_data(int id)
Definition graph.h:694
void clear()
Definition graph.h:697
int add_edges(Iterator begin, Iterator end)
Add multiple edges to the graph.
Definition graph.h:681
const_edge_iterator edge_cend() const
Definition graph.h:984
const_iterator begin() const
Definition graph.h:780
void reserve_edges(int num_edges)
Definition graph.h:570
edge_iterator find_edge(ConstNodeRef src, ConstNodeRef dst)
Find an edge between two nodes.
Definition graph.h:830
adjacency_iterator adj_begin(int nid)
Definition graph.h:1041
NodesErased erase_nodes(const_iterator begin, const_iterator end, UnaryPred pred)
Erase matching nodes and all its associated edge(s) from the graph.
Definition graph.h:1229
std::pair< int, std::vector< int > > erase_edges(Iterator begin, Iterator end)
Erase edges from the graph.
Definition graph.h:1446
NodeRef node(int id)
Definition graph.h:691
adjacency_iterator find_adjacent(int src, int dst)
Find an adjacency entry between two nodes.
Definition graph.h:995
ConstEdgeRef edge(int id) const
Definition graph.h:786
void clear_edges()
Remove all edges from the graph.
Definition graph.h:851
ConstNodeRef edge_dst(int id) const
Definition graph.h:792
NodeRef edge_src(int id)
Definition graph.h:788
const_adjacency_iterator adj_end(int nid) const
Definition graph.h:1044
bool erase_edge_between(int src, int dst)
Erase an edge from the graph between two nodes.
Definition graph.h:1380
const_adjacency_iterator find_adjacent(int src, int dst) const
Find an adjacency entry between two nodes.
Definition graph.h:1008
NodesErased erase_nodes(Iterator begin, Iterator end)
Erase nodes and all its associated edge(s) from the graph.
Definition graph.h:1192
internal::EdgeIterator< Graph, false > edge_iterator
Definition graph.h:499
adjacency_iterator adj_end(int nid)
Definition graph.h:1042
ET & edge_data(int id)
Definition graph.h:794
int num_nodes() const
Definition graph.h:551
std::pair< int, std::vector< int > > erase_edges(const_edge_iterator begin, const_edge_iterator end, UnaryPred pred)
Erase matching edges from the graph.
Definition graph.h:1415
void merge(const GraphLike &other)
Merge another graph-like object into this graph.
Definition graph.h:1062
iterator begin()
Definition graph.h:778
NodeRef operator[](int id)
Definition graph.h:688
const_adjacency_iterator find_adjacent(ConstNodeRef src, ConstNodeRef dst) const
Find an adjacency entry between two nodes.
Definition graph.h:1036
const_iterator end() const
Definition graph.h:781
internal::NodeIterator< Graph, true > const_iterator
Definition graph.h:494
int add_nodes(Iterator begin, Iterator end)
Add multiple nodes to the graph.
Definition graph.h:613
NT node_data_type
Definition graph.h:489
const_edge_iterator find_edge(int src, int dst) const
Find an edge between two nodes.
Definition graph.h:817
internal::AdjWrapper< Graph, true > ConstAdjRef
Definition graph.h:507
internal::EdgeIterator< Graph, true > const_edge_iterator
Definition graph.h:500
internal::NodeWrapper< Graph, false > NodeRef
Definition graph.h:496
edge_iterator edge_end()
Definition graph.h:980
Graph(const Graph &)=default
internal::EdgeWrapper< Graph, false > EdgeRef
Definition graph.h:501
iterator end()
Definition graph.h:779
edge_iterator find_edge(int src, int dst)
Find an edge between two nodes.
Definition graph.h:805
int add_edge(int src, int dst, const BondData &data)
Definition graph.h:635
const_edge_iterator find_edge(ConstNodeRef src, ConstNodeRef dst) const
Find an edge between two nodes.
Definition graph.h:843
bool edge_empty() const
Definition graph.h:553
Graph()=default
const_edge_iterator edge_cbegin() const
Definition graph.h:983
int num_edges() const
Definition graph.h:561
internal::AdjIterator< Graph, false > adjacency_iterator
Definition graph.h:504
ET edge_data_type
Definition graph.h:490
iterator node_iterator
Definition graph.h:493
internal::EdgesWrapper< Graph, false > edges()
Definition graph.h:975
Graph(int num_nodes, const NT &data)
Create a graph with num_nodes nodes, each initialized with data.
Definition graph.h:539
EdgeRef edge(int id)
Definition graph.h:785
std::pair< int, std::vector< int > > erase_edges(const_edge_iterator begin, const_edge_iterator end)
Erase edges from the graph.
Definition graph.h:919
NodeRef edge_dst(int id)
Definition graph.h:791
bool erase_edge_between(ConstNodeRef src, ConstNodeRef dst)
Erase an edge from the graph between two nodes.
Definition graph.h:897
const_adjacency_iterator adj_cend(int nid) const
Definition graph.h:1048
edge_iterator edge_begin()
Definition graph.h:979
const_adjacency_iterator adj_cbegin(int nid) const
Definition graph.h:1045
const_edge_iterator edge_end() const
Definition graph.h:982
const_adjacency_iterator adj_begin(int nid) const
Definition graph.h:1043
const_edge_iterator edge_begin() const
Definition graph.h:981
internal::EdgeWrapper< Graph, true > ConstEdgeRef
Definition graph.h:502
internal::EdgesWrapper< Graph, true > edges() const
Definition graph.h:976
const_iterator cend() const
Definition graph.h:783
Graph(Graph &&) noexcept=default
internal::AdjWrapper< Graph, false > AdjRef
Definition graph.h:506
NodesErased erase_nodes(const_iterator begin, const_iterator end)
Erase nodes and all its associated edge(s) from the graph.
Definition graph.h:724
ConstNodeRef operator[](int id) const
Definition graph.h:689
ConstNodeRef edge_src(int id) const
Definition graph.h:789
internal::AdjIterator< Graph, true > const_adjacency_iterator
Definition graph.h:505
const_iterator const_node_iterator
Definition graph.h:495
internal::NodeIterator< Graph, false > iterator
Definition graph.h:492
adjacency_iterator find_adjacent(ConstNodeRef src, ConstNodeRef dst)
Find an adjacency entry between two nodes.
Definition graph.h:1022
const_iterator cbegin() const
Definition graph.h:782
A subgraph of a graph.
Definition subgraph.h:575
adjacency_iterator adj_begin(int idx)
Definition subgraph.h:1539
Definition crdgen.h:16
std::vector< T, Alloc >::size_type erase_if(std::vector< T, Alloc > &c, Pred &&pred)
Definition container_ext.h:55
auto degree(typename boost::graph_traits< Graph< NT, ET > >::vertex_descriptor v, const Graph< NT, ET > &g)
Definition adaptor.h:242
absl::flat_hash_set< int > connected_components(const Graph< NT, ET > &g, int begin)
Find connected components of a graph starting from a node.
Definition graph.h:1578
internal::EigenNeighborIndexer< GT, is_const > as_index(internal::NodeWrapper< GT, is_const > node)
Definition graph.h:1540
std::pair< std::pair< int, std::vector< int > >, std::pair< int, std::vector< int > > > NodesErased
Definition graph.h:456
auto num_edges(const Graph< NT, ET > &g)
Definition adaptor.h:279
Definition graph.h:483
ET data
Definition graph.h:486
int dst
Definition graph.h:485
int src
Definition graph.h:484