LAL: Linear Arrangement Library 24.10.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 - 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// 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/array.hpp>
54
55namespace lal {
56namespace detail {
57
58// -----------------------------------------------------------------------------
59// -- EDGE LIST --
60
61template <
62 typename tree_t,
63 bool ensure_root_is_returned,
64 bool free_tree_plus_root =
65 ensure_root_is_returned and
66 std::is_same_v<tree_t, graphs::free_tree>
67>
68[[nodiscard]]
69std::conditional_t<
70 free_tree_plus_root,
71 std::pair<tree_t, node>,
72 tree_t
73>
74from_edge_list_to_tree(std::stringstream& ss) noexcept
75{
76 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
77
78 // Read an edge list: {1 2} {2 3} {4 5} ...
79 // The edge list is assumed to be complete due to
80 // the requirements of this function.
81
82 uint64_t max_node_idx = 0;
83
84 // parse the edge list to find the
85 // number of vertices of the tree
86 char curly_bracket;
87 node u,v;
88 while (ss >> curly_bracket) {
89 ss >> u >> v >> curly_bracket;
90 max_node_idx = std::max(max_node_idx, u);
91 max_node_idx = std::max(max_node_idx, v);
92 }
93 ss.clear();
94 ss.seekg(0);
95
96 const auto num_nodes = max_node_idx + 1;
97
98 // read the edge list (again...) to construct the tree
99 // without using extra memory
100
101 tree_t t(num_nodes);
102 while (ss >> curly_bracket) {
103 ss >> u >> v >> curly_bracket;
104 t.add_edge_bulk(u,v);
105 }
106 t.finish_bulk_add_complete();
107
108 if constexpr (std::is_same_v<tree_t, graphs::rooted_tree>) {
109#if defined DEBUG
110 /*
111 * If in the future the call to "finish_bulk_add_complete" above
112 * finds the root of the tree then this assertion should
113 * fail and thus giving us a heads up that the code that
114 * follows is completely useless, hence saving us some
115 * execution time...
116 */
117 assert(not t.has_root());
118#endif
119
120 // find and set the root in linear time :( ...
121 for (node w = 0; w < num_nodes; ++w) {
122 if (t.get_in_degree(w) == 0) {
123 t.set_root(w);
124 break;
125 }
126 }
127
128#if defined DEBUG
129 assert(t.is_rooted_tree());
130#endif
131 }
132 else {
133#if defined DEBUG
134 assert(t.is_tree());
135#endif
136 }
137
138 if constexpr (free_tree_plus_root) {
139 static_assert(std::is_same_v<tree_t, graphs::free_tree>);
140 return {std::move(t), num_nodes + 1};
141 }
142 else {
143 return t;
144 }
145}
146
162template <class graph_t>
163[[nodiscard]] graph_t from_edge_list_to_graph
164(const edge_list& edge_list, const bool normalize, const bool check)
165noexcept
166{
167 uint64_t max_vertex_index = 0;
168 for (const edge& e : edge_list) {
169 max_vertex_index = std::max(max_vertex_index, e.first);
170 max_vertex_index = std::max(max_vertex_index, e.second);
171 }
172 const uint64_t num_nodes = 1 + max_vertex_index;
173 graph_t g(num_nodes);
174 g.set_edges(edge_list, normalize, check);
175 return g;
176}
177
178// -----------------------------------------------------------------------------
179// -- HEAD VECTOR --
180
189template <class graph_t>
190[[nodiscard]] graph_t from_head_vector_to_graph
191(const head_vector& hv, const bool normalize, const bool check)
192noexcept
193{
194 const uint64_t n = hv.size();
195 graph_t g(n);
196 for (uint64_t i = 0; i < n; ++i) {
197 if (hv[i] != 0) {
198 // add the edge:
199 // * i ranges in [0,n-1]
200 // * hv[i] ranges in [1,n]
201 g.add_edge_bulk(hv[i] - 1, i);
202 }
203 }
204 if constexpr (std::is_base_of_v<graphs::tree, graph_t>) {
205 g.finish_bulk_add_complete(normalize, check);
206 }
207 else {
208 g.finish_bulk_add(normalize, check);
209 }
210 return g;
211}
212
221template <
222 typename tree_t,
223 bool ensure_root_is_returned,
224 bool free_tree_plus_root =
225 ensure_root_is_returned and
226 std::is_same_v<tree_t, graphs::free_tree>
227>
228[[nodiscard]]
229std::conditional_t<
230 free_tree_plus_root,
231 std::pair<tree_t, node>,
232 tree_t
233>
234from_head_vector_to_tree(std::stringstream& ss) noexcept
235{
236 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
237
238 uint64_t num_nodes = 0;
239
240 // parse the edge list to find the number of vertices of the tree
241 node u;
242 while (ss >> u) { ++num_nodes; }
243 ss.clear();
244 ss.seekg(0);
245
246 // construct the tree
247 tree_t t(num_nodes);
248
249 // root node of the tree
250 node r = num_nodes + 1;
251
252#if defined DEBUG
253 uint64_t num_edges_added = 0;
254#endif
255
256 uint64_t i = 0;
257 while (ss >> u) {
258 if (u == 0) {
259 // root, do nothing
260 r = i;
261 }
262 else {
263 // note:
264 // * i ranges in [0,n-1]
265 // * L[i] ranges in [1,n] (hence the '-1')
266
267 // In the head vector the edge (i, hv[i] - 1) is an edge of an
268 // anti-arborescence. Since for our rooted trees we need the edge
269 // of an arborescence, then we add the edge as (hv[i] - 1, i).
270 // For free trees the order of vertices does not matter.
271 t.add_edge_bulk(u - 1, i);
272
273#if defined DEBUG
274 ++num_edges_added;
275#endif
276 }
277 ++i;
278 }
279
280#if defined DEBUG
281 // root must have been set
282 assert(r < num_nodes);
283 // amount of edges added must be 'n-1'
284 assert(num_edges_added == num_nodes - 1);
285#endif
286
287 t.finish_bulk_add_complete();
288 if constexpr (std::is_same_v<tree_t, graphs::rooted_tree>) {
289 t.set_root(r);
290#if defined DEBUG
291 assert(t.is_rooted_tree());
292#endif
293 }
294 else {
295#if defined DEBUG
296 assert(t.is_tree());
297#endif
298 }
299
300 if constexpr (free_tree_plus_root) {
301 static_assert(std::is_same_v<tree_t, graphs::free_tree>);
302 return {std::move(t), r};
303 }
304 else {
305 return t;
306 }
307}
308
318template <
319 class tree_t,
320 bool is_rooted = std::is_base_of_v<graphs::rooted_tree, tree_t>
321>
322[[nodiscard]]
323std::conditional_t<
324 is_rooted,
326 std::pair<graphs::free_tree, node>
327>
329(const head_vector& hv, const bool normalize, const bool check)
330noexcept
331{
332 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
333
334 if (hv.size() == 0) {
335 if constexpr (is_rooted) {
336 return graphs::rooted_tree(0);
337 }
338 else {
339 return {graphs::free_tree(0), 0};
340 }
341 }
342 if (hv.size() == 1) {
343#if defined DEBUG
344 // the only vertex can only be the root
345 assert(hv[0] == 0);
346#endif
347 if constexpr (is_rooted) {
348 return graphs::rooted_tree(1);
349 }
350 else {
351 return {graphs::free_tree(1), 0};
352 }
353 }
354
355 const uint64_t num_nodes = hv.size();
356
357 // output tree
358 tree_t t(num_nodes);
359
360 // root node of the tree
361 node r = num_nodes + 1;
362
363#if defined DEBUG
364 uint64_t num_edges_added = 0;
365#endif
366
367 for (uint64_t i = 0; i < num_nodes; ++i) {
368 if (hv[i] == 0) {
369 // root, do nothing
370 r = i;
371 }
372 else {
373 // note:
374 // * i ranges in [0,n-1]
375 // * L[i] ranges in [1,n] (hence the '-1')
376
377 // In the head vector the edge (i, hv[i] - 1) is an edge of an
378 // anti-arborescence. Since for our rooted trees we need the edge
379 // of an arborescence, then we add the edge as (hv[i] - 1, i).
380 // For free trees the order of vertices does not matter.
381 t.add_edge_bulk(hv[i] - 1, i);
382
383#if defined DEBUG
384 ++num_edges_added;
385#endif
386 }
387 }
388
389#if defined DEBUG
390 // root must have been set
391 assert(r < num_nodes);
392 // amount of edges added must be 'n-1'
393 assert(num_edges_added == num_nodes - 1);
394#endif
395
396 t.finish_bulk_add_complete(normalize, check);
397
398 if constexpr (is_rooted) {
399 t.set_root(r);
400#if defined DEBUG
401 assert(t.is_rooted_tree());
402#endif
403
404 return t;
405 }
406 else {
407#if defined DEBUG
408 assert(t.is_tree());
409#endif
410
411 return {std::move(t), r};
412 }
413}
414
422template <class arrangement_t>
424(const graphs::rooted_tree& t, const arrangement_t& arr)
425noexcept
426{
427#if defined DEBUG
428 assert(t.is_rooted_tree());
429#endif
430
431 const uint64_t n = t.get_num_nodes();
432 head_vector hv(n);
433 for (position_t p = 0ull; p < n; ++p) {
434 const node u = arr[p];
435 if (t.get_root() == u) {
436 hv[*p] = 0;
437 }
438 else {
439 const node_t parent = t.get_in_neighbors(u)[0];
440 hv[*p] = arr[parent] + 1;
441 }
442 }
443 return hv;
444}
445
454template <class arrangement_t>
456(const graphs::free_tree& t, const arrangement_t& arr, const node r)
457noexcept
458{
459#if defined DEBUG
460 assert(t.is_tree());
461#endif
462
463 return from_tree_to_head_vector(graphs::rooted_tree(t,r,false,false), arr);
464}
465
466// -----------------------------------------------------------------------------
467// -- LEVEL SEQUENCE --
468
494template <class tree_t>
496(
497 const uint64_t * const L,
498 const uint64_t n,
499 const bool normalize,
500 const bool check
501)
502noexcept
503{
504 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
505
506#if defined DEBUG
507 // a little sanity check
508 assert(L[0] == 0);
509 assert(L[1] == 1);
510#endif
511
512 tree_t t(n);
513
514 // 'stack' of root candidates: node at every level in {1,...,N}.
515 // at position j, lev[j] contains the last node added at level j.
516 array<node> root_candidates(n + 1, 0);
517 std::size_t stack_it = 0;
518
519 // evidently,
520 root_candidates[0] = 1;
521 for (node i = 2; i <= n; ++i) {
522 // find in the stack the node which has to be connected to node 'i'.
523 if (root_candidates[stack_it] + 2 > L[i]) {
524 stack_it = L[i] - 2 + 1;
525 }
526
527 // the top of the stack is the parent of this node
528 const node r = root_candidates[stack_it];
529
530 // add edge...
531 const auto [u,v] = (r == 0 ? edge(r, i - 1) : edge(r - 1, i - 1));
532 t.add_edge_bulk(u, v);
533
534 // the last node added at level L[i] is i.
535 ++stack_it;
536#if defined DEBUG
537 assert(stack_it == L[i]);
538#endif
539 root_candidates[stack_it] = i;
540 }
541
542 if constexpr (std::is_base_of_v<graphs::rooted_tree, tree_t>) {
543 t.set_root(0);
544 }
545
546 t.finish_bulk_add_complete(normalize, check);
547
548#if defined DEBUG
549 assert(t.is_tree());
550#endif
551
552 return t;
553}
554
580template <class tree_t>
582(
583 const uint64_t * const L,
584 const uint64_t n,
585 const bool normalize,
586 const bool check
587)
588noexcept
589{
590 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
591
592#if defined DEBUG
593 // a little sanity check
594 assert(L[0] == 0);
595 assert(L[1] == 1);
596#endif
597
598 // 'stack' of root candidates: node at every level in {1,...,N}.
599 // at position j, lev[j] contains the last node added at level j.
600 array<node> root_candidates(n + 1, 0);
601 std::size_t stack_it = 0;
602
603 array<edge> edge_list(n - 1);
604 std::size_t edge_it = 0;
605 array<uint64_t> vertex_degrees(n, 0);
606
607 // evidently,
608 root_candidates[0] = 1;
609 for (node i = 2; i <= n; ++i) {
610 // find in the stack the node which has to be connected to node 'i'.
611 if (root_candidates[stack_it] + 2 > L[i]) {
612 stack_it = L[i] - 2 + 1;
613 }
614
615 // the top of the stack is the parent of this node
616 const node r = root_candidates[stack_it];
617
618 // retrieve and store the edge, calculate vertex degrees
619 const edge e = (r == 0 ? edge(r, i - 1) : edge(r - 1, i - 1));
620 edge_list[edge_it++] = e;
621 ++vertex_degrees[e.first];
622 ++vertex_degrees[e.second];
623
624 // the last node added at level L[i] is i.
625 ++stack_it;
626#if defined DEBUG
627 assert(stack_it == L[i]);
628#endif
629 root_candidates[stack_it] = i;
630 }
631
632#if defined DEBUG
633 assert(edge_it == n - 1);
634#endif
635
636 tree_t t(n);
637 for (node u = 0; u < n; ++u) {
638 if constexpr (std::is_base_of_v<graphs::free_tree, tree_t>) {
639 t.reserve_degree(u, vertex_degrees[u]);
640 }
641 else {
642 t.reserve_out_degree(u, vertex_degrees[u] - (u != 0));
643 t.reserve_in_degree(u, u != 0);
644 }
645 }
646 for (const edge& e : edge_list) {
647 t.add_edge_bulk(e.first, e.second);
648 }
649
650 if constexpr (std::is_base_of_v<graphs::rooted_tree, tree_t>) {
651 t.set_root(0);
652 }
653
654#if defined DEBUG
655 assert(t.is_tree());
656 if constexpr (std::is_base_of_v<graphs::rooted_tree, tree_t>) {
657 assert(t.has_root());
658 }
659#endif
660
661 t.finish_bulk_add_complete(normalize, check);
662 return t;
663}
664
690template <class tree_t>
691[[nodiscard]] tree_t from_level_sequence_to_tree
692(
693 const uint64_t * const L,
694 const uint64_t n,
695 const bool normalize,
696 const bool check
697)
698noexcept
699{
700 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
701 return n <= 7 ?
702 from_level_sequence_to_tree_small<tree_t>(L, n, normalize, check) :
703 from_level_sequence_to_tree_large<tree_t>(L, n, normalize, check);
704}
705
711template <class tree_t>
713(
714 const std::vector<uint64_t>& L,
715 const uint64_t n,
716 const bool normalize,
717 const bool check
718)
719noexcept
720{
721 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
722 return from_level_sequence_to_tree_small<tree_t>(&L[0], n, normalize, check);
723}
724
730template <class tree_t>
732(
733 const std::vector<uint64_t>& L,
734 const uint64_t n,
735 const bool normalize,
736 const bool check
737)
738noexcept
739{
740 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
741 return from_level_sequence_to_tree_large<tree_t>(&L[0], n, normalize, check);
742}
743
749template <class tree_t>
750[[nodiscard]] tree_t from_level_sequence_to_tree(
751 const std::vector<uint64_t>& L,
752 const uint64_t n,
753 const bool normalize,
754 const bool check
755)
756noexcept
757{
758 static_assert(std::is_base_of_v<graphs::tree, tree_t>);
759 return n <= 7 ?
760 from_level_sequence_to_tree_small<tree_t>(&L[0], n, normalize, check) :
761 from_level_sequence_to_tree_large<tree_t>(&L[0], n, normalize, check);
762}
763
764// -----------------------------------------------------------------------------
765// -- PRUFER SEQUENCE --
766
778 const uint64_t * const seq,
779 const uint64_t n,
780 const bool normalize,
781 const bool check
782)
783noexcept
784{
785 // initialisation
786 const uint64_t L = n - 2;
787 array<uint64_t> degree(n, 1);
788 for (uint64_t i = 0; i < L; ++i) {
789 degree[ seq[i] ] += 1;
790 }
791
792 // the output tree
794
795 // for each number in the sequence seq[i], find the first
796 // lowest-numbered node, j, with degree equal to 1, add
797 // the edge (j, seq[i]) to the tree, and decrement the degrees
798 // of j and seq[i].
799 for (uint64_t v = 0; v < L; ++v) {
800 const auto value = seq[v];
801 bool node_found = false;
802 node w = 0;
803 while (w < n and not node_found) {
804 if (degree[w] == 1) {
805 t.add_edge_bulk(value, w);
806
807 degree[value] -= 1;
808 degree[w] -= 1;
809 node_found = true;
810 }
811 ++w;
812 }
813 }
814
815 // two nodes u,v with degree 1 remain. Find them
816 node u, v;
817 u = v = n;
818 for (node w = 0; w < n; ++w) {
819 if (degree[w] == 1) {
820 if (u == n) {
821 u = w;
822 }
823 else {
824 v = w;
825 }
826 }
827 }
828
829 // add edge (u,v) to the tree
830 t.add_edge_bulk(u, v);
831 t.finish_bulk_add_complete(normalize, check);
832 return t;
833}
834
846(
847 const std::vector<uint64_t>& seq,
848 const uint64_t n,
849 const bool normalize,
850 const bool check
851)
852noexcept
853{
854 return from_Prufer_sequence_to_ftree(&seq[0], n, normalize, check);
855}
856
857
858} // -- namespace detail
859} // -- namespace lal
Free tree graph class.
Definition free_tree.hpp:60
free_tree & add_edge_bulk(const node s, const node t) noexcept
Adds an edge to the tree.
void finish_bulk_add_complete(const bool norm=true, const bool check=true) noexcept
Completes the inner structure of the tree after adding edges in bulk.
Rooted tree graph class.
Definition rooted_tree.hpp:109
graph_t from_edge_list_to_graph(const edge_list &edge_list, const bool normalize, const bool check) noexcept
Converts an edge list into a graph.
Definition conversions.hpp:164
head_vector from_tree_to_head_vector(const graphs::rooted_tree &t, const arrangement_t &arr) noexcept
Constructs the head vector representation of a tree.
Definition conversions.hpp:424
tree_t from_level_sequence_to_tree_large(const uint64_t *const L, const uint64_t n, const bool normalize, const bool check) noexcept
Converts the level sequence of a tree into a graph structure.
Definition conversions.hpp:582
tree_t from_level_sequence_to_tree_small(const uint64_t *const L, const uint64_t n, const bool normalize, const bool check) noexcept
Converts the level sequence of a tree into a graph structure.
Definition conversions.hpp:496
graph_t from_head_vector_to_graph(const head_vector &hv, const bool normalize, const bool check) noexcept
Transforms a head vector in a directed graph.
Definition conversions.hpp:191
graphs::free_tree from_Prufer_sequence_to_ftree(const uint64_t *const seq, const uint64_t n, const bool normalize, const bool check) noexcept
Converts the Prüfer sequence of a labelled tree into a tree structure.
Definition conversions.hpp:777
std::conditional_t< free_tree_plus_root, std::pair< tree_t, node >, tree_t > from_head_vector_to_tree(std::stringstream &ss) noexcept
Transforms a head vector in a tree.
Definition conversions.hpp:234
tree_t from_level_sequence_to_tree(const uint64_t *const L, const uint64_t n, const bool normalize, const bool check) noexcept
Converts the level sequence of a tree into a graph structure.
Definition conversions.hpp:692
@ num_nodes
Number of nodes of the tree.
Main namespace of the library.
Definition basic_types.hpp:48
std::vector< uint64_t > head_vector
See Head vector page for further details.
Definition basic_types.hpp:58
std::pair< node, node > edge
See Edge page for further details.
Definition basic_types.hpp:56
std::vector< edge > edge_list
See Edge list page for further details.
Definition basic_types.hpp:60
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
Typesafe node type.
Definition basic_types.hpp:70
Typesafe position type.
Definition basic_types.hpp:244