LAL: Linear Arrangement Library 23.01.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
conversions.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// C++ includes
43#if defined DEBUG
44#include <cassert>
45#endif
46#include <sstream>
47
48// lal includes
49#include <lal/basic_types.hpp>
50#include <lal/linear_arrangement.hpp>
51#include <lal/graphs/free_tree.hpp>
52#include <lal/graphs/rooted_tree.hpp>
53#include <lal/detail/data_array.hpp>
54#include <lal/detail/identity_arrangement.hpp>
55
56namespace lal {
57namespace detail {
58
59// -----------------------------------------------------------------------------
60// -- EDGE LIST --
61
62template <
63 typename tree_t,
64 bool ensure_root_is_returned,
65 bool free_tree_plus_root =
66 ensure_root_is_returned and
67 std::is_same_v<tree_t, lal::graphs::free_tree>
68>
69std::conditional_t<
70 free_tree_plus_root,
71 std::pair<tree_t, lal::node>,
72 tree_t
73>
74from_edge_list_to_tree(std::stringstream& ss) noexcept
75{
76 // Read an edge list: {1 2} {2 3} {4 5} ...
77 // The edge list is assumed to be complete due to
78 // the requirements of this function.
79
80 uint64_t max_node_idx = 0;
81
82 // parse the edge list to find the
83 // number of vertices of the tree
84 char curly_bracket;
85 lal::node u,v;
86 while (ss >> curly_bracket) {
87 ss >> u >> v >> curly_bracket;
88 max_node_idx = std::max(max_node_idx, u);
89 max_node_idx = std::max(max_node_idx, v);
90 }
91 ss.clear();
92 ss.seekg(0);
93
94 const auto num_nodes = max_node_idx + 1;
95
96 // read the edge list (again...) to construct the tree
97 // without using extra memory
98
99 tree_t t(num_nodes);
100 while (ss >> curly_bracket) {
101 ss >> u >> v >> curly_bracket;
102 t.add_edge_bulk(u,v);
103 }
104 t.finish_bulk_add();
105
106 if constexpr (std::is_same_v<tree_t, lal::graphs::rooted_tree>) {
107#if defined DEBUG
108 // If in the future the call to "finish_bulk_add" above
109 // finds the root of the tree then this assertion should
110 // fail and thus giving us a heads up that the code that
111 // follows is completely useless, hence saving us some
112 // execution time...
113 assert(not t.has_root());
114#endif
115
116 // find and set the root in linear time :( ...
117 for (lal::node w = 0; w < num_nodes; ++w) {
118 if (t.get_in_degree(w) == 0) {
119 t.set_root(w);
120 break;
121 }
122 }
123
124#if defined DEBUG
125 assert(t.is_rooted_tree());
126#endif
127 }
128 else {
129#if defined DEBUG
130 assert(t.is_tree());
131#endif
132 }
133
134 if constexpr (free_tree_plus_root) {
135 static_assert(std::is_same_v<tree_t, lal::graphs::free_tree>);
136 return {std::move(t), num_nodes + 1};
137 }
138 else {
139 return t;
140 }
141}
142
158template <class graph_t>
160(const std::vector<edge>& edge_list, bool normalise = true, bool check = true)
161noexcept
162{
163 uint64_t max_vertex_index = 0;
164 for (const edge& e : edge_list) {
165 max_vertex_index = std::max(max_vertex_index, e.first);
166 max_vertex_index = std::max(max_vertex_index, e.second);
167 }
168 const uint64_t num_nodes = 1 + max_vertex_index;
169 graph_t g(num_nodes);
170 g.set_edges(edge_list, normalise, check);
171 return g;
172}
173
174// -----------------------------------------------------------------------------
175// -- HEAD VECTOR --
176
177
178template <
179 typename tree_t,
180 bool ensure_root_is_returned,
181 bool free_tree_plus_root =
182 ensure_root_is_returned and
183 std::is_same_v<tree_t, lal::graphs::free_tree>
184>
185std::conditional_t<
186 free_tree_plus_root,
187 std::pair<tree_t, lal::node>,
188 tree_t
189>
190from_head_vector_to_tree(std::stringstream& ss) noexcept
191{
192 uint64_t num_nodes = 0;
193
194 // parse the edge list to find the number of vertices of the tree
195 lal::node u;
196 while (ss >> u) { ++num_nodes; }
197 ss.clear();
198 ss.seekg(0);
199
200 // construct the tree
201 tree_t t(num_nodes);
202
203 // root node of the tree
204 lal::node r = num_nodes + 1;
205
206#if defined DEBUG
207 uint64_t num_edges_added = 0;
208#endif
209
210 uint64_t i = 0;
211 while (ss >> u) {
212 if (u == 0) {
213 // root, do nothing
214 r = i;
215 }
216 else {
217 // note:
218 // * i ranges in [0,n-1]
219 // * L[i] ranges in [1,n] (hence the '-1')
220
221 // In the head vector the edge (i, hv[i] - 1) is an edge of an
222 // anti-arborescence. Since for our rooted trees we need the edge
223 // of an arborescence, then we add the edge as (hv[i] - 1, i).
224 // For free trees the order of vertices does not matter.
225 t.add_edge_bulk(u - 1, i);
226
227#if defined DEBUG
228 ++num_edges_added;
229#endif
230 }
231 ++i;
232 }
233
234#if defined DEBUG
235 // root must have been set
236 assert(r < num_nodes);
237 // amount of edges added must be 'n-1'
238 assert(num_edges_added == num_nodes - 1);
239#endif
240
241 t.finish_bulk_add();
242 if constexpr (std::is_same_v<tree_t, lal::graphs::rooted_tree>) {
243 t.set_root(r);
244#if defined DEBUG
245 assert(t.is_rooted_tree());
246#endif
247 }
248 else {
249#if defined DEBUG
250 assert(t.is_tree());
251#endif
252 }
253
254 if constexpr (free_tree_plus_root) {
255 static_assert(std::is_same_v<tree_t, lal::graphs::free_tree>);
256 return {std::move(t), r};
257 }
258 else {
259 return t;
260 }
261}
262
270template <detail::linarr_type arr_type>
272 const graphs::rooted_tree& t,
274)
275noexcept
276{
277#if defined DEBUG
278 assert(t.is_rooted_tree());
279#endif
280
281 const uint64_t n = t.get_num_nodes();
282 head_vector hv(n);
283 for (position_t p = 0ull; p < n; ++p) {
284 const node u = arr[p];
285 if (t.get_root() == u) {
286 hv[*p] = 0;
287 }
288 else {
289 const node_t parent = t.get_in_neighbours(u)[0];
290 hv[*p] = arr[parent] + 1;
291 }
292 }
293 return hv;
294}
295
304template <detail::linarr_type arr_type>
306 const graphs::free_tree& t,
308 node r
309)
310noexcept
311{
312#if defined DEBUG
313 assert(t.is_tree());
314#endif
315
317}
318
328template <
329 class tree_t,
330 bool is_rooted = std::is_base_of_v<graphs::rooted_tree, tree_t>
331>
332std::conditional_t<
333 is_rooted,
335 std::pair<graphs::free_tree,node>
336>
337from_head_vector_to_tree(const head_vector& hv, bool normalise, bool check)
338noexcept
339{
340 if (hv.size() == 0) {
341 if constexpr (is_rooted) {
342 return graphs::rooted_tree(0);
343 }
344 else {
345 return {graphs::free_tree(0), 0};
346 }
347 }
348 if (hv.size() == 1) {
349#if defined DEBUG
350 // the only vertex can only be the root
351 assert(hv[0] == 0);
352#endif
353 if constexpr (is_rooted) {
354 return graphs::rooted_tree(1);
355 }
356 else {
357 return {graphs::free_tree(1), 0};
358 }
359 }
360
361 const uint64_t num_nodes = hv.size();
362
363 // output tree
364 tree_t t(num_nodes);
365
366 // root node of the tree
367 node r = num_nodes + 1;
368
369#if defined DEBUG
370 uint64_t num_edges_added = 0;
371#endif
372
373 for (uint64_t i = 0; i < num_nodes; ++i) {
374 if (hv[i] == 0) {
375 // root, do nothing
376 r = i;
377 }
378 else {
379 // note:
380 // * i ranges in [0,n-1]
381 // * L[i] ranges in [1,n] (hence the '-1')
382
383 // In the head vector the edge (i, hv[i] - 1) is an edge of an
384 // anti-arborescence. Since for our rooted trees we need the edge
385 // of an arborescence, then we add the edge as (hv[i] - 1, i).
386 // For free trees the order of vertices does not matter.
387 t.add_edge_bulk(hv[i] - 1, i);
388
389#if defined DEBUG
390 ++num_edges_added;
391#endif
392 }
393 }
394
395#if defined DEBUG
396 // root must have been set
397 assert(r < num_nodes);
398 // amount of edges added must be 'n-1'
399 assert(num_edges_added == num_nodes - 1);
400#endif
401
402 t.finish_bulk_add(normalise, check);
403
404 if constexpr (is_rooted) {
405 t.set_root(r);
406#if defined DEBUG
407 assert(t.is_rooted_tree());
408#endif
409
410 return t;
411 }
412 else {
413#if defined DEBUG
414 assert(t.is_tree());
415#endif
416
417 return {std::move(t), r};
418 }
419}
420
421// -----------------------------------------------------------------------------
422// -- LEVEL SEQUENCE --
423
448inline
450(const uint64_t * const L, uint64_t n, bool normalise = true, bool check = true)
451noexcept
452{
453#if defined DEBUG
454 // a little sanity check
455 assert(L[0] == 0);
456 assert(L[1] == 1);
457#endif
458
459 // output tree
461
462 // 'stack' of root candidates: node at every level in {1,...,N}.
463 // at position j, lev[j] contains the last node added at level j.
464 data_array<node> lev(n+1, 0);
465 uint64_t stack_it = 0;
466
467 // evidently,
468 lev[0] = 1;
469
470 for (node i = 2; i <= n; ++i) {
471 // find in the stack the node which
472 // has to be connected to node 'i'.
473 if (lev[stack_it] + 2 > L[i]) {
474 stack_it = L[i] - 2 + 1;
475 }
476
477 // the top of the stack is the parent of this node
478 const node r = lev[stack_it];
479
480 // add the edge...
481 const auto [u,v] = (r == 0 ? edge(r, i - 1) : edge(r - 1, i - 1));
482 t.add_edge_bulk(u, v);
483
484 // the last node added at level L[i] is i.
485 ++stack_it;
486#if defined DEBUG
487 assert(stack_it == L[i]);
488#endif
489 lev[stack_it] = i;
490 }
491
492 t.finish_bulk_add(normalise, check);
493 return t;
494}
495
502inline
504(const std::vector<uint64_t>& L, uint64_t n, bool normalise = true, bool check = true)
505noexcept
506{ return level_sequence_to_ftree(&L[0], n, normalise, check); }
507
508// -----------------------------------------------------------------------------
509// -- PRUFER SEQUENCE --
510
521inline
523(const uint64_t * const seq, uint64_t n, bool normalise = true, bool check = true)
524noexcept
525{
526 // initialisation
527 const uint64_t L = n - 2;
528 data_array<uint64_t> degree(n, 1);
529 for (uint64_t i = 0; i < L; ++i) {
530 degree[ seq[i] ] += 1;
531 }
532
533 // the output tree
535
536 // for each number in the sequence seq[i], find the first
537 // lowest-numbered node, j, with degree equal to 1, add
538 // the edge (j, seq[i]) to the tree, and decrement the degrees
539 // of j and seq[i].
540 for (uint64_t v = 0; v < L; ++v) {
541 const auto value = seq[v];
542 bool node_found = false;
543 node w = 0;
544 while (w < n and not node_found) {
545 if (degree[w] == 1) {
546 t.add_edge_bulk(value, w);
547
548 degree[value] -= 1;
549 degree[w] -= 1;
550 node_found = true;
551 }
552 ++w;
553 }
554 }
555
556 // two nodes u,v with degree 1 remain. Find them
557 node u, v;
558 u = v = n;
559 for (node w = 0; w < n; ++w) {
560 if (degree[w] == 1) {
561 if (u == n) {
562 u = w;
563 }
564 else {
565 v = w;
566 }
567 }
568 }
569
570 // add edge (u,v) to the tree
571 t.add_edge_bulk(u, v);
572 t.finish_bulk_add(normalise, check);
573 return t;
574}
575
586inline
588(const std::vector<uint64_t>& seq, uint64_t n, bool normalise = true, bool check = true)
589noexcept
590{ return Prufer_sequence_to_ftree(&seq[0], n, normalise, check); }
591
592
593} // -- namespace detail
594} // -- namespace lal
Free tree graph class.
Definition: free_tree.hpp:60
free_tree & add_edge_bulk(node s, node t) noexcept
Adds an edge to the graph.
void finish_bulk_add(bool norm=true, bool check=true) noexcept
Finishes adding edges in bulk.
Rooted tree graph class.
Definition: rooted_tree.hpp:103
graphs::free_tree Prufer_sequence_to_ftree(const uint64_t *const seq, uint64_t n, bool normalise=true, bool check=true) noexcept
Converts the Prüfer sequence of a labelled tree into a tree structure.
Definition: conversions.hpp:523
graph_t from_edge_list_to_graph(const std::vector< edge > &edge_list, bool normalise=true, bool check=true) noexcept
Converts an edge list into a graph.
Definition: conversions.hpp:160
head_vector from_tree_to_head_vector(const graphs::rooted_tree &t, const detail::linarr_wrapper< arr_type > &arr) noexcept
Constructs the head vector representation of a tree.
Definition: conversions.hpp:271
graphs::free_tree level_sequence_to_ftree(const uint64_t *const L, uint64_t n, bool normalise=true, bool check=true) noexcept
Converts the level sequence of a tree into a graph structure.
Definition: conversions.hpp:450
@ num_nodes
Number of nodes of the tree.
Main namespace of the library.
Definition: basic_types.hpp:50
std::vector< uint64_t > head_vector
See Head vector page for further details.
Definition: basic_types.hpp:64
std::pair< node, node > edge
See Edge page for further details.
Definition: basic_types.hpp:58
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
A wrapper to easily use identity arrangements.
Definition: identity_arrangement.hpp:72
Typesafe node type.
Definition: basic_types.hpp:67
Typesafe position type.
Definition: basic_types.hpp:168