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