Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Tree_Decomposition.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
111# ifndef TREE_DECOMPOSITION_H
112# define TREE_DECOMPOSITION_H
113
114# include <ah-graph-concepts.H>
115
116# include <algorithm>
117# include <concepts>
118# include <cstddef>
119# include <limits>
120# include <type_traits>
121# include <utility>
122
123# include <ah-errors.H>
124# include <ahFunction.H>
125# include <tpl_array.H>
126# include <tpl_dynListStack.H>
127# include <tpl_dynMapOhash.H>
128# include <tpl_dynMapTree.H>
129# include <tpl_graph.H>
130# include <tpl_segment_tree.H>
131
132namespace Aleph
133{
134 namespace tree_decomposition_detail
135 {
139 struct Id_Range
140 {
141 size_t left = 0;
142 size_t right = 0;
143 };
144
153 template <AlephGraph GT, ArcFilter<GT> SA>
155 {
156 public:
157 using Node = typename GT::Node;
158 using Arc = typename GT::Arc;
159
160 static constexpr size_t NONE = std::numeric_limits<size_t>::max();
161
162 private:
163 using Pair_Key = std::pair<size_t, size_t>;
164
165 const GT *graph_ = nullptr;
166 SA sa_;
167 Node *root_ = nullptr;
168 size_t root_id_ = NONE;
169
170 size_t n_ = 0;
171
175
176 static Pair_Key normalize_pair(size_t u, size_t v) noexcept
177 {
178 if (u > v)
179 std::swap(u, v);
180 return std::make_pair(u, v);
181 }
182
183 void check_id(const size_t id, const char *where) const
184 {
186 << where << ": id=" << id << " is out of range [0, " << n_ << ")";
187 }
188
190 {
192
193 if (n_ == 0)
194 {
195 ah_domain_error_if(root_ != nullptr)
196 << "Rooted_Tree_Topology: root node provided but graph is empty";
197 return;
198 }
199
202 node_to_id_.swap(tmp);
203 }
204
205 size_t next_id = 0;
206 for (Node_Iterator<GT> it(*graph_); it.has_curr(); it.next_ne())
207 {
208 Node *p = it.get_curr();
209 id_to_node_(next_id) = p;
211 ++next_id;
212 }
213
215 << "Rooted_Tree_Topology: failed to index all graph nodes";
216
217 if (root_ == nullptr)
218 root_ = id_to_node_(0);
219
221 << "Rooted_Tree_Topology: root node does not belong to graph";
222
224 }
225
227 {
228 if (n_ == 0)
229 return;
230
231 adjacency_.empty();
232 adjacency_.reserve(n_);
233 for (size_t i = 0; i < n_; ++i)
234 adjacency_.append(Array<size_t>());
235
237 size_t edge_count = 0;
238
239 for (Arc_Iterator<GT, SA> it(*graph_, sa_); it.has_curr(); it.next_ne())
240 {
241 Arc *a = it.get_curr_ne();
242 Node *src = graph_->get_src_node(a);
243 Node *tgt = graph_->get_tgt_node(a);
244
245 const auto *src_item = node_to_id_.search(src);
246 const auto *tgt_item = node_to_id_.search(tgt);
247
248 ah_runtime_error_unless(src_item != nullptr and tgt_item != nullptr)
249 << "Rooted_Tree_Topology: arc endpoint is not indexed";
250
251 const size_t u = src_item->second;
252 const size_t v = tgt_item->second;
253
254 ah_domain_error_if(u == v)
255 << "Rooted_Tree_Topology: self-loop detected in filtered graph";
256
257 const Pair_Key key = normalize_pair(u, v);
258 ah_domain_error_if(unique_edges.search(key) != nullptr)
259 << "Rooted_Tree_Topology: parallel/duplicate edge detected in filtered graph";
260
261 unique_edges.insert(key, 1);
262 ++edge_count;
263 }
264
266 << "Rooted_Tree_Topology: filtered graph is not a tree (expected "
267 << (n_ - 1) << " edges, got " << edge_count << ")";
268
270 it.has_curr(); it.next_ne())
271 {
272 const auto & [fst, snd] = it.get_curr();
273 (void) snd;
274
275 const size_t u = fst.first;
276 const size_t v = fst.second;
277 adjacency_(u).append(v);
278 adjacency_(v).append(u);
279 }
280 }
281
283 {
284 if (n_ == 0)
285 return;
286
288 for (size_t i = 0; i < n_; ++i)
289 visited(i) = 0;
290
291 struct Frame
292 {
293 size_t node;
294 size_t parent;
295 size_t next_child;
296 };
297
299 visited(root_id_) = 1;
300 size_t visited_count = 1;
301 stack.push({root_id_, NONE, 0});
302
303 while (not stack.is_empty())
304 {
305 auto & fr = stack.top();
306
307 if (fr.next_child == adjacency_(fr.node).size())
308 {
309 (void) stack.pop();
310 continue;
311 }
312
313 const size_t nxt = adjacency_(fr.node)(fr.next_child++);
314 if (nxt == fr.parent)
315 continue;
316
317 ah_domain_error_if(visited(nxt))
318 << "Rooted_Tree_Topology: filtered graph is not acyclic";
319
320 visited(nxt) = 1;
322 stack.push({nxt, fr.node, 0});
323 }
324
326 << "Rooted_Tree_Topology: filtered graph is not connected";
327 }
328
329 public:
330 Rooted_Tree_Topology(const GT & g, Node *root, SA sa = SA())
331 : graph_(&g), sa_(std::move(sa)), root_(root)
332 {
333 index_nodes();
336 }
337
338 [[nodiscard]] size_t size() const noexcept { return n_; }
339 [[nodiscard]] bool is_empty() const noexcept { return n_ == 0; }
340 [[nodiscard]] Node * root() const noexcept { return root_; }
341 [[nodiscard]] size_t root_id() const noexcept { return root_id_; }
342
344 {
345 return id_to_node_;
346 }
347
349 {
350 return adjacency_;
351 }
352
353 [[nodiscard]] size_t id_of(const Node *node) const
354 {
355 ah_invalid_argument_if(node == nullptr)
356 << "Rooted_Tree_Topology::id_of: null node";
357
358 const auto *item = node_to_id_.search(const_cast<Node *>(node));
359 ah_domain_error_if(item == nullptr)
360 << "Rooted_Tree_Topology::id_of: node does not belong to graph";
361
362 return item->second;
363 }
364
365 [[nodiscard]] Node * node_of(const size_t id) const
366 {
367 check_id(id, "Rooted_Tree_Topology::node_of");
368 return id_to_node_(id);
369 }
370
371 void validate_id(const size_t id, const char *where) const
372 {
373 check_id(id, where);
374 }
375 };
376 } // namespace tree_decomposition_detail
377
378
393 template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
395 {
396 public:
397 using Node = typename GT::Node;
399
400 private:
402 static constexpr size_t NONE = Topology::NONE;
403
405
413
414 void ensure_not_empty(const char *where) const
415 {
416 ah_domain_error_if(is_empty()) << where << ": tree is empty";
417 }
418
420 {
421 const size_t n = size();
422 if (n == 0)
423 return;
424
432
433 for (size_t i = 0; i < n; ++i)
434 {
435 parent_(i) = NONE;
436 depth_(i) = 0;
437 subtree_size_(i) = 0;
438 heavy_(i) = NONE;
439 head_(i) = NONE;
440 pos_(i) = NONE;
441 inv_pos_(i) = NONE;
442 }
443
444 Array<char> visited = Array<char>::create(n);
445 for (size_t i = 0; i < n; ++i)
446 visited(i) = 0;
447
448 Array<size_t> order;
449 order.reserve(n);
450
451 struct Frame
452 {
453 size_t node;
454 size_t parent;
455 size_t next_child;
456 };
457
458 const size_t root_id = topology_.root_id();
460 depth_(root_id) = 0;
461 visited(root_id) = 1;
462
463 size_t visited_count = 1;
464 order.append(root_id);
465
467 stack.push({root_id, NONE, 0});
468
469 while (not stack.is_empty())
470 {
471 auto & fr = stack.top();
472
473 if (fr.next_child == topology_.adjacency()(fr.node).size())
474 {
475 (void) stack.pop();
476 continue;
477 }
478
479 const size_t nxt = topology_.adjacency()(fr.node)(fr.next_child++);
480 if (nxt == fr.parent)
481 continue;
482
483 ah_domain_error_if(visited(nxt))
484 << "Gen_Heavy_Light_Decomposition: filtered graph is not acyclic";
485
486 visited(nxt) = 1;
488
489 parent_(nxt) = fr.node;
490 depth_(nxt) = depth_(fr.node) + 1;
491 order.append(nxt);
492
493 stack.push({nxt, fr.node, 0});
494 }
495
497 << "Gen_Heavy_Light_Decomposition: filtered graph is not connected";
498
499 for (size_t i = order.size(); i-- > 0;)
500 {
501 const size_t u = order(i);
502 subtree_size_(u) = 1;
503 heavy_(u) = NONE;
504
505 for (size_t j = 0; j < topology_.adjacency()(u).size(); ++j)
506 {
507 const size_t v = topology_.adjacency()(u)(j);
508 if (parent_(v) != u)
509 continue;
510
512 if (heavy_(u) == NONE or subtree_size_(v) > subtree_size_(heavy_(u)))
513 heavy_(u) = v;
514 }
515 }
516
517 size_t next_pos = 0;
520
521 while (not chain_stack.is_empty())
522 {
523 const auto [chain_head, chain_start] = chain_stack.pop();
524
525 size_t u = chain_start;
526 while (u != NONE)
527 {
528 head_(u) = chain_head;
529 pos_(u) = next_pos;
530 inv_pos_(next_pos) = u;
531 ++next_pos;
532
533 for (size_t j = 0; j < topology_.adjacency()(u).size(); ++j)
534 if (const size_t v = topology_.adjacency()(u)(j); parent_(v) == u and v != heavy_(u))
535 chain_stack.push({v, v});
536
537 u = heavy_(u);
538 }
539 }
540
542 << "Gen_Heavy_Light_Decomposition: invalid decomposition size";
543 }
544
545 public:
552 Gen_Heavy_Light_Decomposition(const GT & g, Node *root, SA sa = SA())
553 : topology_(g, root, std::move(sa))
554 {
555 build_decomposition();
556 }
557
563 Gen_Heavy_Light_Decomposition(const GT & g, SA sa = SA())
564 : topology_(g, nullptr, std::move(sa))
565 {
566 build_decomposition();
567 }
568
569 [[nodiscard]] size_t size() const noexcept { return topology_.size(); }
570 [[nodiscard]] bool is_empty() const noexcept { return topology_.is_empty(); }
571
572 [[nodiscard]] Node * root() const noexcept { return topology_.root(); }
573 [[nodiscard]] size_t root_id() const noexcept { return topology_.root_id(); }
574
575 [[nodiscard]] Node * node_of(const size_t id) const
576 {
577 return topology_.node_of(id);
578 }
579
580 [[nodiscard]] size_t id_of(const Node *node) const
581 {
582 return topology_.id_of(node);
583 }
584
585 [[nodiscard]] size_t parent_id(const size_t id) const
586 {
587 topology_.validate_id(id, "Gen_Heavy_Light_Decomposition::parent_id");
588 return parent_(id);
589 }
590
591 [[nodiscard]] Node * parent_of(const Node *node) const
592 {
593 const size_t pid = parent_id(id_of(node));
594 return pid == NONE ? nullptr : node_of(pid);
595 }
596
597 [[nodiscard]] size_t depth_of_id(const size_t id) const
598 {
599 topology_.validate_id(id, "Gen_Heavy_Light_Decomposition::depth_of_id");
600 return depth_(id);
601 }
602
603 [[nodiscard]] size_t depth_of(const Node *node) const
604 {
605 return depth_of_id(id_of(node));
606 }
607
608 [[nodiscard]] size_t subtree_size_of_id(const size_t id) const
609 {
610 topology_.validate_id(id, "Gen_Heavy_Light_Decomposition::subtree_size_of_id");
611 return subtree_size_(id);
612 }
613
614 [[nodiscard]] size_t subtree_size_of(const Node *node) const
615 {
616 return subtree_size_of_id(id_of(node));
617 }
618
619 [[nodiscard]] size_t heavy_child_id(const size_t id) const
620 {
621 topology_.validate_id(id, "Gen_Heavy_Light_Decomposition::heavy_child_id");
622 return heavy_(id);
623 }
624
625 [[nodiscard]] Node * heavy_child(const Node *node) const
626 {
627 const size_t cid = heavy_child_id(id_of(node));
628 return cid == NONE ? nullptr : node_of(cid);
629 }
630
631 [[nodiscard]] size_t head_id(const size_t id) const
632 {
633 topology_.validate_id(id, "Gen_Heavy_Light_Decomposition::head_id");
634 return head_(id);
635 }
636
637 [[nodiscard]] Node * head_of(const Node *node) const
638 {
639 return node_of(head_id(id_of(node)));
640 }
641
642 [[nodiscard]] size_t position_of_id(const size_t id) const
643 {
644 topology_.validate_id(id, "Gen_Heavy_Light_Decomposition::position_of_id");
645 return pos_(id);
646 }
647
648 [[nodiscard]] size_t position_of(const Node *node) const
649 {
650 return position_of_id(id_of(node));
651 }
652
654 [[nodiscard]] Range subtree_range_id(const size_t id) const
655 {
656 topology_.validate_id(id, "Gen_Heavy_Light_Decomposition::subtree_range_id");
657 const size_t l = pos_(id);
658 const size_t r = l + subtree_size_(id) - 1;
659 return Range{l, r};
660 }
661
663 [[nodiscard]] Range subtree_range(const Node *node) const
664 {
665 return subtree_range_id(id_of(node));
666 }
667
669 [[nodiscard]] bool is_ancestor_id(const size_t u, const size_t v) const
670 {
671 const Range r = subtree_range_id(u);
672 const size_t pv = position_of_id(v);
673 return r.left <= pv and pv <= r.right;
674 }
675
677 [[nodiscard]] bool is_ancestor(const Node *u, const Node *v) const
678 {
679 return is_ancestor_id(id_of(u), id_of(v));
680 }
681
683 [[nodiscard]] size_t lca_id(size_t u, size_t v) const
684 {
685 ensure_not_empty("Gen_Heavy_Light_Decomposition::lca_id");
686 topology_.validate_id(u, "Gen_Heavy_Light_Decomposition::lca_id");
687 topology_.validate_id(v, "Gen_Heavy_Light_Decomposition::lca_id");
688
689 while (head_(u) != head_(v))
690 if (depth_(head_(u)) >= depth_(head_(v)))
691 u = parent_(head_(u));
692 else
693 v = parent_(head_(v));
694
695 return (depth_(u) <= depth_(v)) ? u : v;
696 }
697
699 [[nodiscard]] Node * lca(const Node *u, const Node *v) const
700 {
701 return node_of(lca_id(id_of(u), id_of(v)));
702 }
703
705 [[nodiscard]] size_t distance_id(const size_t u, const size_t v) const
706 {
707 const size_t a = lca_id(u, v);
708 return depth_(u) + depth_(v) - 2 * depth_(a);
709 }
710
712 [[nodiscard]] size_t distance(const Node *u, const Node *v) const
713 {
714 return distance_id(id_of(u), id_of(v));
715 }
716
727 template <class F>
728 void for_each_path_segment_id(size_t u, size_t v, F && visit) const
729 {
730 ensure_not_empty("Gen_Heavy_Light_Decomposition::for_each_path_segment_id");
731 topology_.validate_id(u, "Gen_Heavy_Light_Decomposition::for_each_path_segment_id");
732 topology_.validate_id(v, "Gen_Heavy_Light_Decomposition::for_each_path_segment_id");
733
735
736 while (head_(u) != head_(v))
737 if (depth_(head_(u)) >= depth_(head_(v)))
738 {
739 visit(pos_(head_(u)), pos_(u), true);
740 u = parent_(head_(u));
741 }
742 else
743 {
744 down_segments.push(Range{pos_(head_(v)), pos_(v)});
745 v = parent_(head_(v));
746 }
747
748 if (depth_(u) >= depth_(v))
749 visit(pos_(v), pos_(u), true);
750 else
751 down_segments.push(Range{pos_(u), pos_(v)});
752
753 while (not down_segments.is_empty())
754 {
755 const auto [left, right] = down_segments.pop();
756 visit(left, right, false);
757 }
758 }
759
761 template <class F>
762 void for_each_path_segment(const Node *u, const Node *v, F && visit) const
763 {
764 for_each_path_segment_id(id_of(u), id_of(v), std::forward<F>(visit));
765 }
766
771 template <class F>
773 const size_t v,
774 F && visit) const
775 {
776 for_each_path_segment_id(u, v,
777 [&](const size_t l, const size_t r, const bool)
778 {
779 visit(l, r);
780 });
781 }
782 };
783
784
809 template <AlephGraph GT,
810 typename T = typename GT::Node::Node_Type,
811 class Op = Aleph::plus<T>,
814 {
815 public:
816 using Node = typename GT::Node;
817
818 private:
822 Op op_;
823
824 void ensure_not_empty(const char *where) const
825 {
826 ah_domain_error_if(is_empty()) << where << ": tree is empty";
827 }
828
829 template <class Getter>
830 requires std::invocable<Getter, Node *> and
831 std::convertible_to<std::invoke_result_t<Getter, Node *>, T>
834 {
835 const size_t n = hld.size();
836 if (n == 0)
837 return Array<T>();
838
839 auto base = Array<T>::create(n);
840 for (size_t id = 0; id < n; ++id)
841 {
842 const size_t p = hld.position_of_id(id);
843 base(p) = static_cast<T>(getter(hld.node_of(id)));
844 }
845
846 return base;
847 }
848
850 {
851 return build_base_array(hld,
852 [](Node *p) -> T
853 {
854 return static_cast<T>(p->get_info());
855 });
856 }
857
858 public:
861 Node *root,
862 const T & identity,
863 Op op = Op(),
864 SA sa = SA())
865 : hld_(g, root, std::move(sa)),
867 identity_(identity),
868 op_(op)
869 {
870 // Empty
871 }
872
875 const T & identity,
876 Op op = Op(),
877 SA sa = SA())
878 : hld_(g, std::move(sa)),
880 identity_(identity),
881 op_(op)
882 {
883 // Empty
884 }
885
887 template <class Getter>
888 requires std::invocable<Getter, Node *> and
889 std::convertible_to<std::invoke_result_t<Getter, Node *>, T>
891 Node *root,
893 const T & identity,
894 Op op = Op(),
895 SA sa = SA())
896 : hld_(g, root, std::move(sa)),
897 segment_tree_(build_base_array(hld_, getter), identity, op),
898 identity_(identity),
899 op_(op)
900 {
901 // Empty
902 }
903
905 template <class Getter>
906 requires std::invocable<Getter, Node *> and
907 std::convertible_to<std::invoke_result_t<Getter, Node *>, T>
910 const T & identity,
911 Op op = Op(),
912 SA sa = SA())
913 : hld_(g, std::move(sa)),
914 segment_tree_(build_base_array(hld_, getter), identity, op),
915 identity_(identity),
916 op_(op)
917 {
918 // Empty
919 }
920
925
926 [[nodiscard]] size_t size() const noexcept { return hld_.size(); }
927 [[nodiscard]] bool is_empty() const noexcept { return hld_.is_empty(); }
928
930 [[nodiscard]] T query_path_id(const size_t u, const size_t v) const
931 {
932 ensure_not_empty("Gen_HLD_Path_Query::query_path_id");
933
934 T ret = identity_;
935 hld_.for_each_path_segment_id(u, v,
936 [&](const size_t l,
937 const size_t r,
938 const bool)
939 {
940 ret = op_(ret, segment_tree_.query(l, r));
941 });
942 return ret;
943 }
944
946 [[nodiscard]] T query_path(const Node *u, const Node *v) const
947 {
948 return query_path_id(hld_.id_of(u), hld_.id_of(v));
949 }
950
952 [[nodiscard]] T query_subtree_id(const size_t id) const
953 {
954 ensure_not_empty("Gen_HLD_Path_Query::query_subtree_id");
955 const auto range = hld_.subtree_range_id(id);
956 return segment_tree_.query(range.left, range.right);
957 }
958
960 [[nodiscard]] T query_subtree(const Node *node) const
961 {
962 return query_subtree_id(hld_.id_of(node));
963 }
964
966 void update_node_id(const size_t id, const T & delta)
967 {
968 ensure_not_empty("Gen_HLD_Path_Query::update_node_id");
969 segment_tree_.update(hld_.position_of_id(id), delta);
970 }
971
973 void update_node(const Node *node, const T & delta)
974 {
975 update_node_id(hld_.id_of(node), delta);
976 }
977
979 void set_node_id(const size_t id, const T & value)
980 {
981 ensure_not_empty("Gen_HLD_Path_Query::set_node_id");
982 segment_tree_.set(hld_.position_of_id(id), value);
983 }
984
986 void set_node(const Node *node, const T & value)
987 {
988 set_node_id(hld_.id_of(node), value);
989 }
990
992 [[nodiscard]] T get_node_id(const size_t id) const
993 {
994 ensure_not_empty("Gen_HLD_Path_Query::get_node_id");
995 return segment_tree_.get(hld_.position_of_id(id));
996 }
997
999 [[nodiscard]] T get_node(const Node *node) const
1000 {
1001 return get_node_id(hld_.id_of(node));
1002 }
1003 };
1004
1005
1024 template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
1026 {
1027 public:
1028 using Node = typename GT::Node;
1029
1030 private:
1032 static constexpr size_t NONE = Topology::NONE;
1033
1035
1040
1043
1046
1047 void ensure_not_empty(const char *where) const
1048 {
1049 ah_domain_error_if(is_empty()) << where << ": tree is empty";
1050 }
1051
1053 {
1054 if (is_empty())
1055 return;
1056
1057 const size_t n = size();
1065
1066 for (size_t i = 0; i < n; ++i)
1067 {
1068 removed_(i) = 0;
1070 centroid_level_(i) = 0;
1071 local_parent_(i) = NONE;
1072 local_subtree_(i) = 0;
1075 }
1076
1078 }
1079
1081 {
1084
1085 local_parent_(start) = NONE;
1086 stack.push(start);
1087
1088 while (not stack.is_empty())
1089 {
1090 const size_t u = stack.pop();
1091 nodes.append(u);
1092
1093 for (size_t i = 0; i < topology_.adjacency()(u).size(); ++i)
1094 {
1095 const size_t v = topology_.adjacency()(u)(i);
1096 if (removed_(v))
1097 continue;
1098
1099 if (v == local_parent_(u))
1100 continue;
1101
1102 local_parent_(v) = u;
1103 stack.push(v);
1104 }
1105 }
1106
1107 return nodes;
1108 }
1109
1111 {
1112 const size_t total = nodes.size();
1114 << "Gen_Centroid_Decomposition: empty component while choosing centroid";
1115
1116 for (size_t i = total; i-- > 0;)
1117 {
1118 const size_t u = nodes(i);
1119 local_subtree_(u) = 1;
1120
1121 for (size_t j = 0; j < topology_.adjacency()(u).size(); ++j)
1122 {
1123 const size_t v = topology_.adjacency()(u)(j);
1124 if (removed_(v))
1125 continue;
1126
1127 if (local_parent_(v) == u)
1129 }
1130 }
1131
1132 size_t centroid = nodes(0);
1133 size_t best_balance = total + 1;
1134
1135 for (size_t i = 0; i < total; ++i)
1136 {
1137 const size_t u = nodes(i);
1138
1139 size_t max_part = total - local_subtree_(u);
1140 for (size_t j = 0; j < topology_.adjacency()(u).size(); ++j)
1141 {
1142 const size_t v = topology_.adjacency()(u)(j);
1143 if (removed_(v))
1144 continue;
1145
1146 if (local_parent_(v) == u)
1147 max_part = std::max(max_part, local_subtree_(v));
1148 }
1149
1150 if (max_part < best_balance)
1151 {
1153 centroid = u;
1154 }
1155 }
1156
1157 return centroid;
1158 }
1159
1160 void append_centroid_annotations(const size_t centroid)
1161 {
1162 struct Frame
1163 {
1164 size_t node;
1165 size_t parent;
1166 size_t dist;
1167 };
1168
1169 DynListStack<Frame> stack;
1170 stack.push({centroid, NONE, 0});
1171
1172 while (not stack.is_empty())
1173 {
1174 const Frame fr = stack.pop();
1175 centroid_ancestors_(fr.node).append(centroid);
1176 centroid_distances_(fr.node).append(fr.dist);
1177
1178 for (size_t i = 0; i < topology_.adjacency()(fr.node).size(); ++i)
1179 {
1180 const size_t v = topology_.adjacency()(fr.node)(i);
1181 if (removed_(v) or v == fr.parent)
1182 continue;
1183
1184 stack.push({v, fr.node, fr.dist + 1});
1185 }
1186 }
1187 }
1188
1190 {
1191 const size_t n = size();
1192 if (n == 0)
1193 return;
1194
1195 init_storage();
1196
1197 struct Task
1198 {
1199 size_t start;
1200 size_t parent_centroid;
1201 size_t level;
1202 };
1203
1204 DynListStack<Task> tasks;
1205 tasks.push({topology_.root_id(), NONE, 0});
1206
1207 size_t centroid_count = 0;
1208
1209 while (not tasks.is_empty())
1210 {
1211 const Task task = tasks.pop();
1212 if (removed_(task.start))
1213 continue;
1214
1216 const size_t centroid = choose_centroid(nodes);
1217
1219
1220 centroid_parent_(centroid) = task.parent_centroid;
1221 centroid_level_(centroid) = task.level;
1222 if (task.parent_centroid == NONE)
1223 centroid_root_ = centroid;
1224
1225 removed_(centroid) = 1;
1227
1228 for (size_t i = 0; i < topology_.adjacency()(centroid).size(); ++i)
1229 if (const size_t v = topology_.adjacency()(centroid)(i); not removed_(v))
1230 tasks.push({v, centroid, task.level + 1});
1231 }
1232
1234 << "Gen_Centroid_Decomposition: invalid centroid tree size";
1235 }
1236
1237 public:
1239 Gen_Centroid_Decomposition(const GT & g, Node *root, SA sa = SA())
1240 : topology_(g, root, std::move(sa))
1241 {
1242 build_centroid_tree();
1243 }
1244
1246 Gen_Centroid_Decomposition(const GT & g, SA sa = SA())
1247 : topology_(g, nullptr, std::move(sa))
1248 {
1249 build_centroid_tree();
1250 }
1251
1252 [[nodiscard]] size_t size() const noexcept { return topology_.size(); }
1253 [[nodiscard]] bool is_empty() const noexcept { return topology_.is_empty(); }
1254
1255 [[nodiscard]] Node * root() const noexcept { return topology_.root(); }
1256 [[nodiscard]] size_t root_id() const noexcept { return topology_.root_id(); }
1257
1258 [[nodiscard]] Node * node_of(const size_t id) const
1259 {
1260 return topology_.node_of(id);
1261 }
1262
1263 [[nodiscard]] size_t id_of(const Node *node) const
1264 {
1265 return topology_.id_of(node);
1266 }
1267
1270 {
1271 return centroid_root_;
1272 }
1273
1276 {
1277 return centroid_root_ == NONE ? nullptr : node_of(centroid_root_);
1278 }
1279
1281 [[nodiscard]] size_t centroid_parent_id(const size_t id) const
1282 {
1283 topology_.validate_id(id, "Gen_Centroid_Decomposition::centroid_parent_id");
1284 return centroid_parent_(id);
1285 }
1286
1288 [[nodiscard]] Node * centroid_parent(const Node *node) const
1289 {
1290 const size_t pid = centroid_parent_id(id_of(node));
1291 return pid == NONE ? nullptr : node_of(pid);
1292 }
1293
1295 [[nodiscard]] size_t centroid_level_of_id(const size_t id) const
1296 {
1297 topology_.validate_id(id, "Gen_Centroid_Decomposition::centroid_level_of_id");
1298 return centroid_level_(id);
1299 }
1300
1302 [[nodiscard]] size_t centroid_level_of(const Node *node) const
1303 {
1304 return centroid_level_of_id(id_of(node));
1305 }
1306
1309 {
1310 size_t ret = 0;
1311 for (size_t i = 0; i < size(); ++i)
1312 ret = std::max(ret, centroid_level_(i));
1313 return ret;
1314 }
1315
1317 [[nodiscard]] size_t centroid_path_length_of_id(const size_t id) const
1318 {
1319 topology_.validate_id(id, "Gen_Centroid_Decomposition::centroid_path_length_of_id");
1320 return centroid_ancestors_(id).size();
1321 }
1322
1324 [[nodiscard]] size_t centroid_path_length_of(const Node *node) const
1325 {
1326 return centroid_path_length_of_id(id_of(node));
1327 }
1328
1330 [[nodiscard]] const Array<size_t> &centroid_ancestors_of_id(const size_t id) const
1331 {
1332 topology_.validate_id(id, "Gen_Centroid_Decomposition::centroid_ancestors_of_id");
1333 return centroid_ancestors_(id);
1334 }
1335
1337 [[nodiscard]] const Array<size_t> &centroid_distances_of_id(const size_t id) const
1338 {
1339 topology_.validate_id(id, "Gen_Centroid_Decomposition::centroid_distances_of_id");
1340 return centroid_distances_(id);
1341 }
1342
1345 {
1346 return centroid_ancestors_of_id(id_of(node));
1347 }
1348
1351 {
1352 return centroid_distances_of_id(id_of(node));
1353 }
1354
1357 const size_t node) const
1358 {
1359 topology_.validate_id(ancestor, "Gen_Centroid_Decomposition::is_centroid_ancestor_id");
1360 topology_.validate_id(node, "Gen_Centroid_Decomposition::is_centroid_ancestor_id");
1361
1362 const auto & chain = centroid_ancestors_(node);
1363 for (size_t i = 0; i < chain.size(); ++i)
1364 if (chain(i) == ancestor)
1365 return true;
1366
1367 return false;
1368 }
1369
1374 [[nodiscard]] size_t distance_to_centroid_id(const size_t node,
1375 const size_t centroid) const
1376 {
1377 topology_.validate_id(node, "Gen_Centroid_Decomposition::distance_to_centroid_id");
1378 topology_.validate_id(centroid, "Gen_Centroid_Decomposition::distance_to_centroid_id");
1379
1380 const auto & chain = centroid_ancestors_(node);
1381 const auto & dists = centroid_distances_(node);
1382
1383 for (size_t i = 0; i < chain.size(); ++i)
1384 if (chain(i) == centroid)
1385 return dists(i);
1386
1387 return NONE;
1388 }
1389
1394 template <class F>
1395 void for_each_centroid_ancestor_id(const size_t node, F && f) const
1396 {
1397 ensure_not_empty("Gen_Centroid_Decomposition::for_each_centroid_ancestor_id");
1398 topology_.validate_id(node, "Gen_Centroid_Decomposition::for_each_centroid_ancestor_id");
1399
1400 const auto & chain = centroid_ancestors_(node);
1401 const auto & dists = centroid_distances_(node);
1402
1403 ah_runtime_error_unless(chain.size() == dists.size())
1404 << "Gen_Centroid_Decomposition: invalid centroid annotation state";
1405
1406 for (size_t i = 0; i < chain.size(); ++i)
1407 f(chain(i), dists(i), i);
1408 }
1409
1414 template <class F>
1415 void for_each_centroid_ancestor(const Node *node, F && f) const
1416 {
1417 for_each_centroid_ancestor_id(id_of(node), std::forward<F>(f));
1418 }
1419 };
1420
1421
1423 template <class GT, class SA = Dft_Show_Arc<GT>>
1425
1427 template <class GT,
1428 typename T = typename GT::Node::Node_Type,
1429 class Op = Aleph::plus<T>,
1430 class SA = Dft_Show_Arc<GT>>
1432
1434 template <class GT, class SA = Dft_Show_Arc<GT>>
1436} // namespace Aleph
1437
1438# endif // TREE_DECOMPOSITION_H
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
Definition ah-errors.H:255
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:644
C++20 concepts for the protocol shared by graph algorithms.
Standard functor implementations and comparison objects.
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
size_t size_t int32_t value
Definition ca-c-api.h:116
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:196
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Dynamic stack of elements of generic type T based on a singly linked list.
T & top()
Return a modifiable reference to the top item of the stack.
bool is_empty() const noexcept
Check if the stack is empty.
T pop()
Remove and return the top item of the stack.
T & push(const T &data)
Push an item by copy onto the top of the stack.
Generic key-value map implemented on top of a binary search tree.
typename Base::Iterator Iterator
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Centroid decomposition over a rooted tree represented as an Aleph graph.
Node * centroid_parent(const Node *node) const
Parent centroid of node (or nullptr if centroid root).
size_t centroid_path_length_of(const Node *node) const
Number of centroid ancestors stored for node.
Array< Array< size_t > > centroid_distances_
size_t centroid_level_of_id(const size_t id) const
Level of centroid id in centroid tree (root level = 0).
const Array< size_t > & centroid_ancestors_of(const Node *node) const
Centroid ancestors of node from centroid root down to local centroid.
Node * centroid_root() const noexcept
Root centroid node of the centroid tree (or nullptr if empty).
bool is_centroid_ancestor_id(const size_t ancestor, const size_t node) const
True iff ancestor is in the centroid-ancestor chain of node.
void ensure_not_empty(const char *where) const
size_t centroid_level_of(const Node *node) const
Level of centroid node in centroid tree (root level = 0).
size_t centroid_root_id() const noexcept
Root centroid ID of the centroid tree (or NONE if empty).
Gen_Centroid_Decomposition(const GT &g, Node *root, SA sa=SA())
Construct using an explicit root node.
const Array< size_t > & centroid_ancestors_of_id(const size_t id) const
Centroid ancestors of node ID from centroid root down to local centroid.
Node * node_of(const size_t id) const
size_t centroid_parent_id(const size_t id) const
Parent centroid ID of id (or NONE if centroid root).
size_t centroid_path_length_of_id(const size_t id) const
Number of centroid ancestors stored for node ID.
const Array< size_t > & centroid_distances_of(const Node *node) const
Distances to centroid ancestors (aligned with centroid_ancestors_of).
size_t max_centroid_level() const noexcept
Maximum centroid-tree level.
size_t distance_to_centroid_id(const size_t node, const size_t centroid) const
Distance from node ID to a centroid ancestor ID.
void append_centroid_annotations(const size_t centroid)
void for_each_centroid_ancestor_id(const size_t node, F &&f) const
Visit each centroid ancestor of node ID.
size_t id_of(const Node *node) const
Array< size_t > collect_component_nodes(const size_t start)
void for_each_centroid_ancestor(const Node *node, F &&f) const
Visit each centroid ancestor of node.
const Array< size_t > & centroid_distances_of_id(const size_t id) const
Distances to centroid ancestors (aligned with centroid_ancestors_of_id).
Gen_Centroid_Decomposition(const GT &g, SA sa=SA())
Construct using the first graph node as root.
size_t choose_centroid(const Array< size_t > &nodes)
Array< Array< size_t > > centroid_ancestors_
Segment-tree-powered path/subtree queries over an HLD layout.
Gen_Heavy_Light_Decomposition< GT, SA > hld_
T query_subtree(const Node *node) const
Subtree query for node in O(log n).
static Array< T > build_base_from_node_info(const Gen_Heavy_Light_Decomposition< GT, SA > &hld)
void set_node(const Node *node, const T &value)
Point assignment: a[node] = value.
T query_path(const Node *u, const Node *v) const
Path query between two nodes in O(log^2 n).
void update_node(const Node *node, const T &delta)
Point update: a[node] = Op(a[node], delta).
Gen_HLD_Path_Query(const GT &g, Node *root, const T &identity, Op op=Op(), SA sa=SA())
Construct from graph/root using node->get_info() as initial values.
void ensure_not_empty(const char *where) const
const Gen_Heavy_Light_Decomposition< GT, SA > & decomposition() const noexcept
T query_path_id(const size_t u, const size_t v) const
Path query between two node IDs in O(log^2 n).
Gen_HLD_Path_Query(const GT &g, const T &identity, Op op=Op(), SA sa=SA())
Construct from graph (first node as root) using node->get_info().
T get_node_id(const size_t id) const
Return current value at node ID.
size_t size() const noexcept
T query_subtree_id(const size_t id) const
Subtree query for node ID in O(log n).
void update_node_id(const size_t id, const T &delta)
Point update: a[id] = Op(a[id], delta).
bool is_empty() const noexcept
void set_node_id(const size_t id, const T &value)
Point assignment: a[id] = value.
Gen_Segment_Tree< T, Op > segment_tree_
and static std::convertible_to< std::invoke_result_t< Getter, Node * >, T > Array< T > build_base_array(const Gen_Heavy_Light_Decomposition< GT, SA > &hld, Getter getter)
and std::convertible_to< std::invoke_result_t< Getter, Node * >, T > Gen_HLD_Path_Query(const GT &g, Node *root, Getter getter, const T &identity, Op op=Op(), SA sa=SA())
Construct with custom node-to-value getter.
and std::convertible_to< std::invoke_result_t< Getter, Node * >, T > Gen_HLD_Path_Query(const GT &g, Getter getter, const T &identity, Op op=Op(), SA sa=SA())
Construct with custom getter and implicit root (first node).
T get_node(const Node *node) const
Return current value at node.
Heavy-Light Decomposition over a rooted tree represented as an Aleph graph.
Gen_Heavy_Light_Decomposition(const GT &g, SA sa=SA())
Construct using the first graph node as root.
size_t position_of(const Node *node) const
void for_each_path_segment_id(size_t u, size_t v, F &&visit) const
Enumerate path segments from u to v in path order.
size_t parent_id(const size_t id) const
size_t head_id(const size_t id) const
size_t distance_id(const size_t u, const size_t v) const
Distance (number of edges) between two node IDs.
size_t lca_id(size_t u, size_t v) const
Lowest common ancestor in O(log n).
size_t position_of_id(const size_t id) const
Node * head_of(const Node *node) const
size_t distance(const Node *u, const Node *v) const
Distance (number of edges) between two nodes.
size_t subtree_size_of_id(const size_t id) const
size_t depth_of_id(const size_t id) const
size_t heavy_child_id(const size_t id) const
void ensure_not_empty(const char *where) const
Node * lca(const Node *u, const Node *v) const
Lowest common ancestor in O(log n).
Node * heavy_child(const Node *node) const
bool is_ancestor(const Node *u, const Node *v) const
True iff u is ancestor of v in the rooted tree.
Range subtree_range(const Node *node) const
Base-array range of a full subtree (inclusive endpoints).
size_t id_of(const Node *node) const
size_t depth_of(const Node *node) const
Gen_Heavy_Light_Decomposition(const GT &g, Node *root, SA sa=SA())
Construct using an explicit root node.
Node * node_of(const size_t id) const
size_t subtree_size_of(const Node *node) const
Range subtree_range_id(const size_t id) const
Base-array range of a full subtree (inclusive endpoints).
Node * parent_of(const Node *node) const
bool is_ancestor_id(const size_t u, const size_t v) const
True iff u is ancestor of v in the rooted tree.
void for_each_path_segment(const Node *u, const Node *v, F &&visit) const
Enumerate path segments from u to v in path order.
void for_each_path_segment_undirected_id(const size_t u, const size_t v, F &&visit) const
Enumerate path segments ignoring direction.
Segment tree over an arbitrary associative binary operation.
typename Node::Node_Type Node_Type
The arc class type.
Definition tpl_graph.H:437
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1207
void validate_id(const size_t id, const char *where) const
const Array< Node * > & id_to_node() const noexcept
Rooted_Tree_Topology(const GT &g, Node *root, SA sa=SA())
const Array< Array< size_t > > & adjacency() const noexcept
void check_id(const size_t id, const char *where) const
static Pair_Key normalize_pair(size_t u, size_t v) noexcept
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4071
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
size_t size(Node *root) noexcept
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
Container< T > range(const T start, const T end, const T step=1)
Generate a range of values [start, end] with a given step.
STL namespace.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1165
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1001
Open addressing hash map using linear probing.
Data & find(const Key &key)
Find and return the value for a key.
Pair * insert(const Key &key, const Data &data)
Insert a key-value pair (copy semantics).
Pair * search(const Key &key) const noexcept
Search for a key in the map.
Task with priority for job scheduling.
gsl_rng * r
Dynamic array container with automatic resizing.
Dynamic stack implementation based on linked lists.
Dynamic map with open hashing.
Dynamic key-value map based on balanced binary search trees.
Generic graph and digraph implementations.
Segment trees for dynamic range queries with lazy propagation.
DynList< int > l