LAL: Linear Arrangement Library 21.07.01
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 - 2021
7 *
8 * This file is part of Linear Arrangement Library. To see the full code
9 * visit the webpage:
10 * https://github.com/lluisalemanypuig/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/definitions.hpp>
52#include <lal/graphs/rooted_tree.hpp>
53#include <lal/graphs/free_tree.hpp>
54#include <lal/internal/graphs/traversal.hpp>
55
56namespace lal {
57namespace internal {
58
59/*
60 * @brief Calculate the centre of the connected component that has node @e x.
61 *
62 * Here, "centre" should NOT be confused with "centroid". The center is the set
63 * of (at most) two vertices that have minimum eccentricity. The centroid is the
64 * set of (at most) two vertices that have minimum weight, where the weight is
65 * the maximum size of the subtrees rooted at that vertex. See \cite Harary1969a
66 * for further details.
67 *
68 * A graph of type @ref lal::graphs::tree may lack some edges tree so it has
69 * several connected components. Vertex @e x belongs to one of these connected
70 * components.
71 *
72 * This method finds the central nodes of the connected components node
73 * 'x' belongs to.
74 *
75 * @param t Input tree.
76 * @param x Input node.
77 * @returns A tuple of two values: the nodes in the centre. If the
78 * tree has a single central node, only the first node is valid and the second
79 * is assigned an invalid vertex index. It is guaranteed that the first vertex
80 * has smaller index value than the second.
81 */
82template<
83 class T,
84 std::enable_if_t<
85 std::is_base_of_v<graphs::free_tree, T> ||
86 std::is_base_of_v<graphs::rooted_tree, T>,
87 bool> = true
88>
89std::pair<node, node> retrieve_centre(const T& t, node X) {
90
91 const auto n = t.get_num_nodes();
92
93 // First simple case:
94 // in case the component of x has only one node (node x)...
95 if (t.get_num_nodes_component(X) == 1) {
96 return std::make_pair(X, n+1);
97 }
98
99 // Second simple case:
100 // if the connected component has two nodes then
101 if (t.get_num_nodes_component(X) == 2) {
102 // case component_size==1 is actually the first simple case
103 const node v1 = X;
104
105 // only neighbour of X
106 node v2;
107 if constexpr (std::is_base_of_v<graphs::free_tree, T>) {
108 v2 = t.get_neighbours(X)[0];
109 }
110 else {
111 v2 = (t.get_out_degree(X) == 0 ?
112 t.get_in_neighbours(X)[0] : t.get_out_neighbours(X)[0]
113 );
114 }
115 return (v1 < v2 ? std::make_pair(v1, v2) : std::make_pair(v2, v1));
116 }
117
118 BFS<T> bfs(t);
119
120 // leaves of the orginal tree's connected component
121 std::vector<node> tree_leaves;
122 tree_leaves.reserve(t.get_num_nodes_component(X) - 1);
123 // full degree of every node of the connected component
124 std::vector<uint32_t> trimmed_degree(n, 0);
125 // number of nodes in the connected component
126 uint32_t size_trimmed = t.get_num_nodes_component(X);
127
128#if defined DEBUG
129 uint32_t __size_trimmed = 0; // for debugging purposes only
130#endif
131
132 // leaves left to process
133 // l0: leaves in the current tree
134 uint32_t l0 = 0;
135 // l1: leaves produced after having trimmed all the l0 leaves
136 uint32_t l1 = 0;
137
138 // ---------------------------------------------------
139 // Initialise data:
140 // 1. fill in 'trimmed_degree' values
141 // 2. retrieve connected component's leaves ('tree_leaves')
142 // 3. calculate amount of leaves left to process ('l0')
143 bfs.set_process_current(
144 [&](const auto&, node u) -> void {
145#if defined DEBUG
146 ++__size_trimmed;
147#endif
148
149 // 'trimmed_degree' must be the degree of the vertex
150 // in the underlying undirected graph!
151 trimmed_degree[u] = t.get_degree(u);
152
153 if (trimmed_degree[u] == 1) {
154 tree_leaves.push_back(u);
155 ++l0;
156 }
157 }
158 );
159
160 if constexpr (std::is_base_of_v<graphs::free_tree, T>)
161 { bfs.set_use_rev_edges(false); }
162 else
163 { bfs.set_use_rev_edges(true); }
164
165 bfs.start_at(X);
166
167#if defined DEBUG
168 // make sure that the method n_nodes_component returns a correct value
169 assert(__size_trimmed == size_trimmed);
170#endif
171
172 // Third case: the component has three nodes or more...
173
174 // ---------------------------------------------------
175 bfs.reset();
176
177 // ---------------------------------------------------
178 // retrieve the centre of the connected component
179
180 bfs.set_terminate(
181 [&](const auto&, node) -> bool {
182 // Meaning of every condition:
183 // --> l0 == 1 or l0 == 2
184 // The trimmmed tree has 1 or 2 leaves left.
185 // --> l1 == 0
186 // After trimming once, the trimmed tree can't be trimmed any further.
187 // --> size_trimmed <= 2
188 // Note that a (trimmed) linear tree (or path graph) has two leaves.
189 // This means that the conditions so far are true. However, this
190 // does not mean we have calculated the centre because there still
191 // is a big amount of leaves to trim. Therefore, we need a trimmed
192 // tree of at most two nodes to finish.
193 return (l0 == 1 or l0 == 2) and l1 == 0 and size_trimmed <= 2;
194 }
195 );
196
197 // does the connected component have unique centre?
198 bool has_single_center = false;
199 node single_center = n + 1;
200
201 bfs.set_process_visited_neighbours(true);
202 bfs.set_process_neighbour(
203 [&](const auto&, node u, node v, bool) -> void
204 {
205 // ignore the edge if one of its nodes has already been trimmed out.
206 if (trimmed_degree[u] == 0) { return; }
207 if (trimmed_degree[v] == 0) { return; }
208
209 // trim node 's':
210 // 1) its degree is set to null, 2) node 't' loses a neighbour, so
211 // its degree is reduced by 1. 3) the size of the trimmed tree
212 // decreases by 1.
213 trimmed_degree[u] = 0;
214 --trimmed_degree[v];
215 --size_trimmed;
216
217 if (trimmed_degree[v] == 0) {
218 has_single_center = true;
219 single_center = v;
220 }
221
222 // leaves left to process in the current trimmed tree
223 --l0;
224 // leaves left to process in the next trimmed tree
225 if (trimmed_degree[v] == 1) {
226 ++l1;
227 if (l0 == 0) {
228 // l0 <- l1
229 // l1 <- 0
230 std::swap(l0, l1);
231 }
232 }
233 }
234 );
235
236 // add the next node only if its degree
237 // (in the trimmed tree) is exactly one.
238 bfs.set_node_add(
239 [&](const auto&, node, node v) -> bool { return (trimmed_degree[v] == 1); }
240 );
241
242 // do the bfs from the leaves inwards
243 bfs.set_use_rev_edges(t.is_directed());
244 bfs.start_at(tree_leaves);
245
246 if (has_single_center) {
247#if defined DEBUG
248 assert(size_trimmed == 1);
249#endif
250 return std::make_pair(single_center, n+1);
251 }
252
253 // in case the 'has_single_center' boolean is false
254 // the variable 'size_trimmed' must equal 2.
255#if defined DEBUG
256 assert(size_trimmed == 2);
257#endif
258
259 // ---------------------------------------------------
260 // retrieve the two central nodes
261
262 // -- reset the bfs
263 bfs.reset();
264 bfs.set_use_rev_edges(t.is_directed());
265
266 node v1, v2;
267 v1 = v2 = n;
268
269 // Traverse the connected component of 'x' in order to find the central
270 // nodes. NOTE: we could use a "for" loop through the 'n' nodes of the
271 // tree, but this BFS-traversal might be faster (due to the fewer
272 // amount of vertices in the connected component).
273 bfs.set_process_current(
274 [&](const auto&, node u) -> void {
275 if (trimmed_degree[u] == 1) {
276 if (v1 == n) { v1 = u; }
277 else { v2 = u; }
278 }
279 }
280 );
281 bfs.start_at(X);
282
283 // return the nodes in the right order according to index values
284 return (v1 < v2 ? std::make_pair(v1, v2) : std::make_pair(v2, v1));
285}
286
287} // -- namespace internal
288} // -- namespace lal
Main namespace of the library.
Definition definitions.hpp:48
uint32_t node
Node type.
Definition definitions.hpp:51