LAL: Linear Arrangement Library 23.01.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
tree_centre.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/basic_types.hpp>
52#include <lal/graphs/rooted_tree.hpp>
53#include <lal/graphs/free_tree.hpp>
54#include <lal/detail/graphs/traversal.hpp>
55
56namespace lal {
57namespace detail {
58
81template <
82 class tree_t,
83 std::enable_if_t< std::is_base_of_v<graphs::tree, tree_t>, bool > = true
84>
85std::pair<node, node> retrieve_centre(const tree_t& t, node X) noexcept
86{
87 // number of nodes of the whole tree
88 const auto n = t.get_num_nodes();
89
90 // First simple case:
91 // in case the component of x has only one node (node x)...
92 if (t.get_num_nodes_component(X) == 1) {
93 return {X, n+1};
94 }
95
96 // Second simple case:
97 // if the connected component has two nodes then
98 if (t.get_num_nodes_component(X) == 2) {
99 // case component_size==1 is actually the first simple case
100 const node v1 = X;
101
102 // only neighbour of X
103 node v2;
104 if constexpr (std::is_base_of_v<graphs::free_tree, tree_t>) {
105 v2 = t.get_neighbours(X)[0];
106 }
107 else {
108 v2 = (t.get_out_degree(X) == 0 ?
109 t.get_in_neighbours(X)[0] : t.get_out_neighbours(X)[0]
110 );
111 }
112 return (v1 < v2 ? std::make_pair(v1, v2) : std::make_pair(v2, v1));
113 }
114
115 BFS<tree_t> bfs(t);
116
117 // leaves of the orginal tree's connected component
118 std::vector<node> tree_leaves;
119 tree_leaves.reserve(t.get_num_nodes_component(X) - 1);
120 // full degree of every node of the connected component
121 data_array<uint64_t> trimmed_degree(n, 0);
122 // number of nodes in the connected component
123 uint64_t size_trimmed = t.get_num_nodes_component(X);
124
125#if defined DEBUG
126 uint64_t __size_trimmed = 0; // for debugging purposes only
127#endif
128
129 // leaves left to process
130 // l0: leaves in the current tree
131 uint64_t l0 = 0;
132 // l1: leaves produced after having trimmed all the l0 leaves
133 uint64_t l1 = 0;
134
135 // ---------------------------------------------------
136 // Initialise data:
137 // 1. fill in 'trimmed_degree' values
138 // 2. retrieve connected component's leaves ('tree_leaves')
139 // 3. calculate amount of leaves left to process ('l0')
141 [&](const auto&, node u) -> void {
142#if defined DEBUG
143 ++__size_trimmed;
144#endif
145
146 // 'trimmed_degree' must be the degree of the vertex
147 // in the underlying undirected graph!
148 trimmed_degree[u] = t.get_degree(u);
149
150 if (trimmed_degree[u] == 1) {
151 tree_leaves.push_back(u);
152 ++l0;
153 }
154 }
155 );
156
157 if constexpr (std::is_base_of_v<graphs::free_tree, tree_t>)
158 { bfs.set_use_rev_edges(false); }
159 else
160 { bfs.set_use_rev_edges(true); }
161
162 bfs.start_at(X);
163
164#if defined DEBUG
165 // make sure that the method n_nodes_component returns a correct value
166 assert(__size_trimmed == size_trimmed);
167#endif
168
169 // Third case: the component has three nodes or more...
170
171 // ---------------------------------------------------
172 bfs.reset();
173
174 // ---------------------------------------------------
175 // retrieve the centre of the connected component
176
177 bfs.set_terminate(
178 [&](const auto&, node) -> bool {
179 // Meaning of every condition:
180 // --> l0 == 1 or l0 == 2
181 // The trimmmed tree has 1 or 2 leaves left.
182 // --> l1 == 0
183 // After trimming once, the trimmed tree can't be trimmed any further.
184 // --> size_trimmed <= 2
185 // Note that a (trimmed) linear tree (or path graph) has two leaves.
186 // This means that the conditions so far are true. However, this
187 // does not mean we have calculated the centre because there still
188 // is a big amount of leaves to trim. Therefore, we need a trimmed
189 // tree of at most two nodes to finish.
190 return (l0 == 1 or l0 == 2) and l1 == 0 and size_trimmed <= 2;
191 }
192 );
193
194 // does the connected component have unique centre?
195 bool has_single_center = false;
196 node single_center = n + 1;
197
200 [&](const auto&, node u, node v, bool) -> void
201 {
202 // ignore the edge if one of its nodes has already been trimmed out.
203 if (trimmed_degree[u] == 0) { return; }
204 if (trimmed_degree[v] == 0) { return; }
205
206 // trim node 's':
207 // 1) its degree is set to null, 2) node 't' loses a neighbour, so
208 // its degree is reduced by 1. 3) the size of the trimmed tree
209 // decreases by 1.
210 trimmed_degree[u] = 0;
211 --trimmed_degree[v];
212 --size_trimmed;
213
214 if (trimmed_degree[v] == 0) {
215 has_single_center = true;
216 single_center = v;
217 }
218
219 // leaves left to process in the current trimmed tree
220 --l0;
221 // leaves left to process in the next trimmed tree
222 if (trimmed_degree[v] == 1) {
223 ++l1;
224 if (l0 == 0) {
225 // l0 <- l1
226 // l1 <- 0
227 std::swap(l0, l1);
228 }
229 }
230 }
231 );
232
233 // add the next node only if its degree
234 // (in the trimmed tree) is exactly one.
235 bfs.set_node_add(
236 [&](const auto&, node, node v) -> bool { return (trimmed_degree[v] == 1); }
237 );
238
239 // do the bfs from the leaves inwards
240 bfs.set_use_rev_edges(t.is_directed());
241 bfs.start_at(tree_leaves);
242
243 if (has_single_center) {
244#if defined DEBUG
245 assert(size_trimmed == 1);
246#endif
247 return {single_center, n+1};
248 }
249
250 // in case the 'has_single_center' boolean is false
251 // the variable 'size_trimmed' must equal 2.
252#if defined DEBUG
253 assert(size_trimmed == 2);
254#endif
255
256 // ---------------------------------------------------
257 // retrieve the two central nodes
258
259 // -- reset the bfs
260 bfs.reset();
261 bfs.set_use_rev_edges(t.is_directed());
262
263 node v1, v2;
264 v1 = v2 = n;
265
266 // Traverse the connected component of 'x' in order to find the central
267 // nodes. NOTE: we could use a "for" loop through the 'n' nodes of the
268 // tree, but this BFS-traversal might be faster (due to the fewer
269 // amount of vertices in the connected component).
271 [&](const auto&, node u) -> void {
272 if (trimmed_degree[u] == 1) {
273 if (v1 == n) { v1 = u; }
274 else { v2 = u; }
275 }
276 }
277 );
278 bfs.start_at(X);
279
280 // return the nodes in the right order according to index values
281 return (v1 < v2 ? std::make_pair(v1, v2) : std::make_pair(v2, v1));
282}
283
284} // -- namespace detail
285} // -- namespace lal
Abstract graph Breadth-First Search traversal.
Definition: traversal.hpp:89
void reset() noexcept
Set the graph traversal to its default state.
Definition: traversal.hpp:135
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 set_terminate(const BFS_bool_one &f) noexcept
Set the function that controls the termination of the loop.
Definition: traversal.hpp:179
void start_at(node source) noexcept
Start traversal at a given node.
Definition: traversal.hpp:152
void set_node_add(const BFS_bool_two &f) noexcept
Set the function that controls when a node is to be added to the queue.
Definition: traversal.hpp:200
void set_process_neighbour(const BFS_process_two &f) noexcept
Set the function that controls the processing of the current neighbour.
Definition: traversal.hpp:193
void set_process_visited_neighbours(bool v) noexcept
Should the algorithm call the neighbour processing function for already visited neighbours?
Definition: traversal.hpp:208
void set_use_rev_edges(bool use) noexcept
Set whether the traversal can use reversed edges.
Definition: traversal.hpp:173
std::pair< node, node > retrieve_centre(const tree_t &t, node X) noexcept
Calculate the centre of the connected component that has node x.
Definition: tree_centre.hpp:85
Main namespace of the library.
Definition: basic_types.hpp:50
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