LAL: Linear Arrangement Library 23.01.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
tree_centroid.hpp
1/*********************************************************************
2 *
3 * Linear Arrangement Library - A library that implements a collection
4 * algorithms for linear arrangments of graphs.
5 *
6 * Copyright (C) 2019 - 2023
7 *
8 * This file is part of Linear Arrangement Library. The full code is available
9 * at:
10 * https://github.com/LAL-project/linear-arrangement-library.git
11 *
12 * Linear Arrangement Library is free software: you can redistribute it
13 * and/or modify it under the terms of the GNU Affero General Public License
14 * as published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * Linear Arrangement Library is distributed in the hope that it will be
18 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with Linear Arrangement Library. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * Contact:
26 *
27 * LluĂ­s Alemany Puig (lalemany@cs.upc.edu)
28 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
29 * CQL (Complexity and Quantitative Linguistics Lab)
30 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
31 * Webpage: https://cqllab.upc.edu/people/lalemany/
32 *
33 * Ramon Ferrer i Cancho (rferrericancho@cs.upc.edu)
34 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Office S124, Omega building
37 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
38 * Webpage: https://cqllab.upc.edu/people/rferrericancho/
39 *
40 ********************************************************************/
41
42#pragma once
43
44// C++ includes
45#if defined DEBUG
46#include <cassert>
47#endif
48#include <vector>
49
50// lal includes
51#include <lal/graphs/output.hpp>
52#include <lal/basic_types.hpp>
53#include <lal/graphs/rooted_tree.hpp>
54#include <lal/detail/graphs/size_subtrees.hpp>
55#include <lal/detail/sorting/counting_sort.hpp>
56#include <lal/detail/pairs_utils.hpp>
57#include <lal/detail/linear_queue.hpp>
58#include <lal/detail/graphs/traversal.hpp>
59#include <lal/detail/type_traits/conditional_list.hpp>
60
61namespace lal {
62namespace detail {
63
65enum class centroid_results {
66 // 1
69 // 2
72 // 3
75 // 4
78};
79
80#define m1(mode) (mode == centroid_results::only_one_centroidal)
81#define m2(mode) (mode == centroid_results::full_centroid)
82#define m3(mode) (mode == centroid_results::full_centroid_plus_subtree_sizes)
83#define m4(mode) (mode == centroid_results::full_centroid_plus_edge_sizes)
84#define node_pair std::pair<lal::node,lal::node>
85#define P std::pair
86
113template <
114 centroid_results mode,
115 class tree_t,
116 std::enable_if_t<std::is_base_of_v<graphs::tree, tree_t>, bool> = true
117>
119 bool_sequence<
120 m1(mode),
121 m2(mode),
122 m3(mode),
123 m4(mode)
124 >,
125 type_sequence<
126 node,
127 std::pair<lal::node,lal::node>,
128 std::pair<std::pair<lal::node,lal::node>, data_array<uint64_t>>,
129 std::pair<std::pair<lal::node,lal::node>, data_array<edge_size>>
130 >
131>
132find_centroidal_vertex(const tree_t& t, node x) noexcept
133{
134 const auto N = t.get_num_nodes();
135 const auto n = t.get_num_nodes_component(x);
136
137 if (n == 1) {
138 if constexpr (m1(mode)) {
139 return x;
140 }
141 else if constexpr (m2(mode)) {
142 return {x,2};
143 }
144 else if constexpr (m3(mode)) {
145 data_array<uint64_t> s(N, 0);
146 s[x] = 1;
147 return {{x,2}, std::move(s)};
148 }
149 else if constexpr (m4(mode)) {
150 return {{x,2}, data_array<edge_size>{}};
151 }
152 }
153 if (n == 2) {
154 auto only_neigh = [&]() {
155 if constexpr (std::is_base_of_v<graphs::free_tree, tree_t>) {
156 return t.get_neighbours(x)[0];
157 }
158 else {
159 if (t.get_out_degree(x) == 0) { return t.get_in_neighbours(x)[0]; }
160 else { return t.get_out_neighbours(x)[0]; }
161 }
162 }();
163
164 if (x > only_neigh) { std::swap(x, only_neigh); }
165 if constexpr (m1(mode)) {
166 return x;
167 }
168 else if constexpr (m2(mode)) {
169 return {x,only_neigh};
170 }
171 else if constexpr (m3(mode)) {
172 data_array<uint64_t> s(N, 0);
173 s[x] = 2;
174 s[only_neigh] = 1;
175 return {{x,only_neigh}, std::move(s)};
176 }
177 else if constexpr (m4(mode)) {
178 return {
179 {x,only_neigh},
180 data_array<edge_size>{ {{x, only_neigh}, 1} }
181 };
182 }
183 }
184
185 const auto ndiv2 = n/2 + n%2;
186
187 // the centroidal vertices, initialized to invalid values
188 node c1 = N + 1;
189 node c2 = N + 1;
190
191 // weight of every node: needed to detect the centroid.
192 data_array<uint64_t> weight(N, 1);
193 // degree of every vertex: needed to find leaves
194 data_array<uint64_t> degree(N, 0);
195 // array of pairs of edge and directional size
196 data_array<edge_size> edge_sizes;
197 std::size_t idx_edge_sizes = 0;
198 if constexpr (m4(mode)) {
199 edge_sizes.resize(n - 1);
200 }
201
202 // queue of the traversal
203 linear_queue<node> queue;
204 queue.init(n);
205
206 // push leaves of the connected component into the queue.
207 {
208 BFS<tree_t> bfs(t);
209 bfs.set_use_rev_edges(std::is_base_of_v<graphs::rooted_tree, tree_t>);
211 [&](const auto&, node u) {
212 degree[u] = t.get_degree(u);
213 // fill queue
214 if (t.get_degree(u) == 1) {
215 queue.push(u);
216 }
217 }
218 );
219 bfs.start_at(x);
220 }
221
222 // find centroid.
223 while (queue.size() > 0) {
224 // current node
225 const node u = queue.pop();
226
227 if (weight[u] >= ndiv2) {
228 if (c1 >= n) {
229 // if the user requested just one centroidal vertex,
230 // stop now, there is no need to go on.
231 if constexpr (m1(mode)) { return u; }
232
233 c1 = u;
234 }
235 else {
236 c2 = u;
237 }
238 continue;
239 }
240
241 // "delete" vertex u
242 --degree[u];
243#if defined DEBUG
244 assert(degree[u] == 0);
245#endif
246
247 // append a new leaf to the queue
248 if constexpr (std::is_base_of_v<graphs::free_tree, tree_t>) {
249 for (node v : t.get_neighbours(u)) {
250 if (degree[v] == 0) { continue; }
251
252 --degree[v];
253 weight[v] += weight[u];
254 if (degree[v] == 1) {
255 queue.push(v);
256 }
257
258 if constexpr (m4(mode)) {
259 edge_sizes[idx_edge_sizes++] = {{v,u}, weight[u]};
260 }
261 }
262 }
263 else {
264 for (node v : t.get_in_neighbours(u)) {
265 if (degree[v] == 0) { continue; }
266
267 --degree[v];
268 weight[v] += weight[u];
269 if (degree[v] == 1) {
270 queue.push(v);
271 }
272
273 if constexpr (m4(mode)) {
274 edge_sizes[idx_edge_sizes++] = {{v,u}, weight[u]};
275 }
276 }
277 for (node v : t.get_out_neighbours(u)) {
278 if (degree[v] == 0) { continue; }
279
280 --degree[v];
281 weight[v] += weight[u];
282 if (degree[v] == 1) {
283 queue.push(v);
284 }
285
286 if constexpr (m4(mode)) {
287 edge_sizes[idx_edge_sizes++] = {{v,u}, weight[u]};
288 }
289 }
290 }
291 }
292
293 if (c2 < N) {
294 if (c1 > c2) {
295 std::swap(c1, c2);
296 }
297 weight[c1] += weight[c2];
298
299 if constexpr (m4(mode)) {
300 edge_sizes[idx_edge_sizes++] = {{c1,c2}, weight[c2]};
301 }
302 }
303
304#if defined DEBUG
305 if constexpr (m4(mode)) {
306 assert(idx_edge_sizes == edge_sizes.size());
307 }
308#endif
309
310 if constexpr (m2(mode)) {
311 return {c1, c2};
312 }
313 else if constexpr (m3(mode)) {
314 return {{c1, c2}, std::move(weight)};
315 }
316 else if constexpr (m4(mode)) {
317 return {{c1, c2}, std::move(edge_sizes)};
318 }
319}
320
321#undef P
322#undef node_pair
323#undef m4
324#undef m3
325#undef m2
326#undef m1
327
339template <
340 class tree_t,
341 std::enable_if_t<std::is_base_of_v<graphs::tree, tree_t>, bool> = true
342>
344 const tree_t& t, node x,
345 std::vector< std::vector<node_size> >& L
346)
347noexcept
348{
349 // retrieve centroid and set of edges and directional size
350 std::pair< std::pair<node,node>, data_array<edge_size> >
351 centroid_subtree_sizes =
352 find_centroidal_vertex<centroid_results::full_centroid_plus_edge_sizes>(t, x);
353
354 auto sizes_edge = std::move(centroid_subtree_sizes.second);
355
356 const uint64_t n = t.get_num_nodes();
357
358 // sort the edges by directional size
361 (
362 sizes_edge.begin(), sizes_edge.end(), n, sizes_edge.size(),
363 [](const edge_size& edge_pair) -> std::size_t
364 { return edge_pair.size; }
365 );
366
367 // fill (already-rooted) adjacency matrix
368 L.resize(n);
369 for (const auto& [uv, suv] : sizes_edge) {
370 const auto [u, v] = uv;
371 L[u].push_back({v, suv});
372 }
373
374 return centroid_subtree_sizes.first;
375}
376
377// -----------------------------------------------------------------------------
378
392template <
393 class tree_t,
394 std::enable_if_t< std::is_base_of_v<graphs::tree, tree_t>, bool > = true
395>
396std::pair<node, node> retrieve_centroid(const tree_t& t, const node x)
397noexcept
398{
399 return find_centroidal_vertex<centroid_results::full_centroid>(t, x);
400}
401
415template <
416 class tree_t,
417 std::enable_if_t< std::is_base_of_v<graphs::tree, tree_t>, bool > = true
418>
419std::pair<node, node> retrieve_centroid(const tree_t& t)
420noexcept
421{
422 return find_centroidal_vertex<centroid_results::full_centroid>(t, 0);
423}
424
425} // -- namespace detail
426} // -- namespace lal
Abstract graph Breadth-First Search traversal.
Definition: traversal.hpp:89
void set_process_current(const BFS_process_one &f) noexcept
Set the function that controls the processing of the current node.
Definition: traversal.hpp:186
void start_at(node source) noexcept
Start traversal at a given node.
Definition: traversal.hpp:152
void set_use_rev_edges(bool use) noexcept
Set whether the traversal can use reversed edges.
Definition: traversal.hpp:173
Simple array-like fixed-size queue.
Definition: linear_queue.hpp:69
T && pop() noexcept
Pops the first element of the queue.
Definition: linear_queue.hpp:98
std::size_t size() const noexcept
Returns the size of the queue.
Definition: linear_queue.hpp:121
void push(const T &v) noexcept
Insert a new element to the queue.
Definition: linear_queue.hpp:79
void init(std::size_t n) noexcept
Initializes the queue to hold n elements.
Definition: linear_queue.hpp:72
void counting_sort(const value_iterator_t begin, const value_iterator_t end, const std::size_t largest_key_plus_1, const std::function< std::size_t(const value_t &)> &key, countingsort::memory< value_t > &mem) noexcept
Counting sort algorithm with reusable memory.
Definition: counting_sort.hpp:155
std::pair< node, node > retrieve_centroid(const tree_t &t, const node x) noexcept
Calculate the centroid of the connected component that has node x.
Definition: tree_centroid.hpp:396
centroid_results
The different types of results.
Definition: tree_centroid.hpp:65
@ full_centroid_plus_edge_sizes
Returns the full centroid of the tree. Also returns the edge_size array.
@ full_centroid
Returns the full centroid of the tree. No weights.
@ only_one_centroidal
Returns only one centroidal vertex. No weights.
@ full_centroid_plus_subtree_sizes
Returns the full centroid of the tree. Also returns the weights.
conditional_list_t< bool_sequence< m1(mode), m2(mode), m3(mode), m4(mode) >, type_sequence< node, std::pair< lal::node, lal::node >, std::pair< std::pair< lal::node, lal::node >, data_array< uint64_t > >, std::pair< std::pair< lal::node, lal::node >, data_array< edge_size > > > > find_centroidal_vertex(const tree_t &t, node x) noexcept
Calculates the centroid of a tree.
Definition: tree_centroid.hpp:132
std::pair< node, node > centroidal_vertex_plus_adjacency_list(const tree_t &t, node x, std::vector< std::vector< node_size > > &L) noexcept
Calculates the centroid and the corresponding rooted adjacency list.
Definition: tree_centroid.hpp:343
typename conditional_list< bool_seq, type_seq >::type conditional_list_t
Shorthand for conditional_list.
Definition: conditional_list.hpp:78
Main namespace of the library.
Definition: basic_types.hpp:50
std::pair< edge, edge > edge_pair
Edge pair type.
Definition: basic_types.hpp:60
uint64_t node
Node type. See Node / Vertex page for further details.
Definition: basic_types.hpp:53
Wrapper of a C array for autmatic deallocation of memory.
Definition: data_array.hpp:59
std::size_t size() const noexcept
Size of the array.
Definition: data_array.hpp:204
void resize(std::size_t new_size) noexcept
Resize the array.
Definition: data_array.hpp:175
Struct used in many algorithms to sort edges according to some integer value.
Definition: pairs_utils.hpp:65
Non-increasing sort.
Definition: sorting_types.hpp:51