LAL: Linear Arrangement Library 24.10.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
Unconstrained_FC.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 * Juan Luis Esteban (esteban@cs.upc.edu)
28 * LOGPROG: Logics and Programming Research Group
29 * Office 110, Omega building
30 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
31 * Webpage: https://www.cs.upc.edu/~esteban/
32 *
33 * LluĂ­s Alemany Puig (lluis.alemany.puig@upc.edu)
34 * LQMC (Quantitative, Mathematical, and Computational Linguisitcs)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
37 * Webpage: https://cqllab.upc.edu/people/lalemany/
38 *
39 ********************************************************************/
40
41// C++ includes
42#if defined DEBUG
43#include <cassert>
44#endif
45
46#include <optional>
47#include <limits>
48#include <vector>
49
50#include <lal/linear_arrangement.hpp>
51#include <lal/detail/pairs_utils.hpp>
52#include <lal/detail/graphs/traversal.hpp>
53#include <lal/detail/properties/tree_centroid.hpp>
54#include <lal/detail/graphs/size_subtrees.hpp>
55#include <lal/detail/sorting/counting_sort.hpp>
56#include <lal/detail/array.hpp>
57#include <lal/detail/macros/basic_convert.hpp>
58#include <lal/detail/linarr/D/Dopt_utils.hpp>
59
60namespace lal {
61namespace detail {
62namespace Dmin {
63namespace unconstrained {
64
71namespace Chung {
72
75
76/*
77int calculate_q(uint64_t n, const ordering& ord) {
78 const uint64_t k = to_uint64(ord.size()) - 1;
79 const uint64_t t_0 = ord[0].size;
80
81 // Maximum possible p_alpha
82 int64_t q = to_int64(k)/2;
83 uint64_t sum = 0;
84 for (uint64_t i = 0; i <= 2*to_uint64(q); ++i) {
85 sum += ord[i].size;
86 }
87
88 uint64_t z = n - sum;
89 uint64_t tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
90 // t_0 >= t_1 >= ... >= t_k
91 uint64_t t_2q = ord[2*q].size;
92 while (q >= 0 and t_2q <= tricky_formula) {
93 z += ord[2*q].size;
94 if (q > 0) {
95 z += ord[2*q - 1].size;
96 }
97 --q;
98 tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
99 if (q >= 0) {
100 t_2q = ord[2*q].size;
101 }
102 }
103 return q;
104}
105*/
106
115[[nodiscard]] std::optional<uint64_t> calculate_q
116(const uint64_t n, const ordering& ord)
117noexcept
118{
119#if defined DEBUG
120 assert(ord.size() > 0);
121#endif
122
123 const uint64_t k = ord.size() - 1;
124 const uint64_t t_0 = ord[0].size;
125
126 // Maximum possible p_alpha
127 uint64_t q = k/2;
128 uint64_t sum = 0;
129 for (uint64_t i = 0; i <= 2*q; ++i) {
130 sum += ord[i].size;
131 }
132
133 uint64_t z = n - sum;
134 uint64_t tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
135 // t_0 >= t_1 >= ... >= t_k
136 uint64_t t_2q = ord[2*q].size;
137
138 while (t_2q <= tricky_formula) {
139 z += ord[2*q].size;
140
141 if (q > 0) { z += ord[2*q - 1].size; }
142 tricky_formula = (t_0 + 2)/2 + (z + 2)/2;
143
144 if (q == 0) { return {}; }
145 --q;
146 t_2q = ord[2*q].size;
147 }
148 return q;
149}
150
151/*
152int calculate_p(const uint64_t n, const ordering& ord) {
153 if (ord.size() < 2) {
154 return -1;
155 }
156
157 // number of subtrees (T_0, T_1, ..., T_k)
158 const uint64_t k = to_uint64(ord.size() - 1);
159 const uint64_t t_0 = ord[0].size;
160
161 int p = (k - 1)/2;
162
163 uint64_t sum = 0;
164 for (uint64_t i = 0; i <= 2*to_uint64(p) + 1; ++i) {
165 sum += ord[i].size;
166 }
167
168 uint64_t y = n - sum;
169 uint64_t tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
170 uint64_t t_2p_plus_1 = ord[2*p + 1].size;
171
172 while (p >= 0 and t_2p_plus_1 <= tricky_formula) {
173 y = y + ord[2*p + 1].size + ord[2*p].size;
174 --p;
175 tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
176 if (p >= 0) {
177 t_2p_plus_1 = ord[2*p + 1].size;
178 }
179 }
180 return p;
181}
182*/
183
192[[nodiscard]] std::optional<uint64_t> calculate_p
193(const uint64_t n, const ordering& ord)
194noexcept
195{
196 if (ord.size() < 2) { return {}; }
197
198 // number of subtrees (T_0, T_1, ..., T_k)
199 const uint64_t k = ord.size() - 1;
200 const uint64_t t_0 = ord[0].size;
201
202 uint64_t p = (k - 1)/2;
203
204 uint64_t sum = 0;
205 for (uint64_t i = 0; i <= 2*p + 1; ++i) {
206 sum += ord[i].size;
207 }
208
209 uint64_t y = n - sum;
210 uint64_t tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
211 uint64_t t_2p_plus_1 = ord[2*p + 1].size;
212
213 while (t_2p_plus_1 <= tricky_formula) {
214 y = y + ord[2*p + 1].size + ord[2*p].size;
215 tricky_formula = (t_0 + 2)/2 + (y + 2)/2;
216
217 if (p == 0) { return {}; }
218 --p;
219 t_2p_plus_1 = ord[2*p + 1].size;
220 }
221 return p;
222}
223
233(const uint64_t p, uint64_t i)
234noexcept
235{
236 array<uint64_t> v(2*p + 1 + 1);
237 uint64_t pos = v.size() - 1;
238 uint64_t right_pos = pos;
239 uint64_t left_pos = 1;
240
241 uint64_t j = 0;
242 while (j <= 2*p + 1) {
243 if (j == i) {
244 ++j;
245 }
246 else {
247 v[pos] = j;
248 if (pos > left_pos) {
249 --right_pos;
250 pos = left_pos;
251 }
252 else {
253 ++left_pos;
254 pos = right_pos;
255 }
256 ++j;
257 }
258 }
259
260 return v;
261}
262
271[[nodiscard]] array<uint64_t> get_Q(const uint64_t q, const uint64_t i) noexcept {
272 array<uint64_t> v(2*q + 1);
273 uint64_t pos = v.size() - 1;
274 uint64_t right_pos = pos;
275 uint64_t left_pos = 1;
276
277 uint64_t j = 0;
278 while (j <= 2*q) {
279 if (j == i) {
280 ++j;
281 }
282 else {
283 v[pos] = j;
284 if (pos > left_pos) {
285 --right_pos;
286 pos = left_pos;
287 }
288 else {
289 ++left_pos;
290 pos = right_pos;
291 }
292 ++j;
293 }
294 }
295 return v;
296}
297
306[[nodiscard]] ordering get_ordering(const graphs::free_tree& t, const node u)
307noexcept
308{
309 // Let 'T_u' to be a tree rooted at vertex 'u'.
310 // Order subtrees of 'T_u' by size.
311 ordering ord(t.get_degree(u));
312
313 // Retrieve size of every subtree. Let 'T_u[v]' be the subtree
314 // of 'T_u' rooted at vertex 'v'. Now,
315 // s[v] := the size of the subtree 'T_u[v]'
316 array<uint64_t> s(t.get_num_nodes());
317 get_size_subtrees(t, u, s.begin());
318
319 uint64_t M = 0; // maximum of the sizes (needed for the counting sort algorithm)
320 const neighbourhood& u_neighs = t.get_neighbors(u);
321 for (std::size_t i = 0; i < u_neighs.size(); ++i) {
322 // i-th child of v_star
323 const node ui = u_neighs[i];
324 // size of subtree rooted at 'ui'
325 const uint64_t s_ui = s[ui];
326
327 ord[i].size = s_ui;
328 ord[i].v = ui;
329
330 M = std::max(M, s_ui);
331 }
334 (
335 ord.begin(), ord.end(), M, ord.size(),
336 [](const node_size& p) { return p.size; }
337 );
338
339 return ord;
340}
341
354template <char root, bool make_arrangement>
356(
358 const node one_node,
359 const position start,
361 uint64_t& cost
362)
363noexcept
364{
365 array<node> reachable(t.get_num_nodes_component(one_node));
366 {
367 auto it = reachable.begin();
368 BFS bfs(t);
369 bfs.set_process_current([&](const auto&, node u) { *it++ = u; });
370 bfs.start_at(one_node);
371 }
372 const uint64_t size_tree = t.get_num_nodes_component(one_node);
373
374 static_assert(
375 root == Dopt_utils::NO_ANCHOR or
376 root == Dopt_utils::RIGHT_ANCHOR or
378 );
379
380#if defined DEBUG
381 assert(size_tree > 0);
382#endif
383
384 // Base case
385 if (size_tree == 1) {
386#if defined DEBUG
387 assert(one_node == reachable[0]);
388 assert(start <= t.get_num_nodes());
389#endif
390 if constexpr (make_arrangement) {
391 mla.assign(one_node, start);
392 }
393 cost = 0;
394 return;
395 }
396
397 if constexpr (root == Dopt_utils::NO_ANCHOR) {
398 const node u = retrieve_centroid(t, one_node).first;
399
400 const ordering ord = get_ordering(t, u);
401
402 const auto q = calculate_q(size_tree, ord);
403 if (not q) {
404 const uint64_t n_0 = ord[0].size;
405 const node t_0 = ord[0].v;
406
407 t.remove_edge(u, t_0, false, false);
408
409 uint64_t c1 = 0;
411 (t, t_0, start, mla, c1);
412
413 uint64_t c2 = 0;
415 (t, u, start + n_0, mla, c2);
416
417 cost = c1 + c2 + 1;
418
419 t.add_edge(u, t_0, false, false);
420 }
421 else {
422 // uq: unsigned 'q'
423 const uint64_t uq = *q;
424 cost = std::numeric_limits<uint64_t>::max();
425
426 std::vector<edge> edges(2*uq + 1);
427 for (uint64_t i = 0; i <= 2*uq; ++i) {
428 edges[i] = {u, ord[i].v};
429 }
430
431 // Transform g into Y
432 t.remove_edges(edges, false, false);
433
434 // Central tree size
435 uint64_t size_rest_of_trees = 0;
436 for (uint64_t i = 2*uq + 1; i < ord.size(); ++i) {
437 size_rest_of_trees += ord[i].size;
438 }
439
440 for (uint64_t i = 0; i <= 2*uq; ++i) {
441 const auto Q_i = get_Q(uq, i);
442
443 t.add_edge(u, ord[i].v, false, false);
444
445 uint64_t c_i = 0;
446 linear_arrangement arr_aux = mla;
447 uint64_t start_aux = start;
448
449 // Left part of the arrangement
450 for (uint64_t j = 1; j <= uq; ++j) {
451 const position pos_in_ord = Q_i[j];
452
453 uint64_t c_i_j = 0;
455 (
456 t,
457 ord[pos_in_ord].v, start_aux,
458 arr_aux, c_i_j
459 );
460 start_aux += ord[pos_in_ord].size;
461 c_i += c_i_j;
462 }
463
464 // Central part of the arrangement
465 uint64_t c_i_j = 0;
466 calculate_mla<Dopt_utils::NO_ANCHOR, make_arrangement>(t, u, start_aux, arr_aux, c_i_j);
467 c_i += c_i_j;
468
469 // Right part of the arrangement
470 start_aux += ord[i].size + 1 + size_rest_of_trees;
471 for (uint64_t j = uq + 1; j <= 2*uq; ++j) {
472 const position pos_in_ord = Q_i[j];
473
474 uint64_t c_i_j_in = 0;
476 (
477 t,
478 ord[pos_in_ord].v, start_aux,
479 arr_aux, c_i_j_in
480 );
481 start_aux += ord[pos_in_ord].size;
482 c_i += c_i_j_in;
483 }
484
485 // Adding parts of the anchors over trees nearer to the central tree
486 c_i += size_tree*uq;
487
488 uint64_t subs = 0;
489 for (uint64_t j = 1; j <= uq; ++j) {
490 subs += (uq - j + 1)*(ord[Q_i[j]].size + ord[Q_i[2*uq - j + 1]].size);
491 }
492 c_i -= subs;
493 c_i += uq; // NOT IN CHUNG'S PAPER
494
495 if (c_i < cost) {
496 cost = c_i;
497 if constexpr (make_arrangement) {
498 mla = std::move(arr_aux);
499 }
500 }
501#if defined DEBUG
502 assert(u != ord[i].v);
503#endif
504 t.remove_edge(u, ord[i].v, false, false);
505 }
506
507 // Transform g into its previous form
508 t.add_edges(edges, false, false);
509 }
510 }
511 else { // root == ANCHOR
512 const ordering ord = get_ordering(t, one_node);
513
514 const auto p = calculate_p(size_tree, ord);
515 if (not p) {
516 const uint64_t n_0 = ord[0].size;
517 const node t_0 = ord[0].v;
518#if defined DEBUG
519 assert(one_node != t_0);
520#endif
521
522 t.remove_edge(one_node, t_0, false, false);
523
524 uint64_t c1 = 0;
525 uint64_t c2 = 0;
526
527 if constexpr (root == Dopt_utils::LEFT_ANCHOR) {
529 (t, one_node, start, mla, c1);
530
532 (t, t_0, start + size_tree - ord[0].size, mla, c2);
533 }
534 else {
536 (t, t_0, start, mla, c1);
537
539 (t, one_node, start + n_0, mla, c2);
540 }
541
542 cost = c1 + c2 + size_tree - ord[0].size;
543 t.add_edge(one_node, t_0, false, false);
544
545 /*
546 if constexpr (root == LEFT_ANCHOR) {
547 //if (2*mla[one_node - 1] - 2*start > size_tree - 1) {
548
549 // Left anchor and the root is too much to the right
550 for (uint64_t i = 0; i < size_tree; ++i) {
551 const uint64_t aux = start + size_tree - 1 - mla[reachable[i]] + start;
552 mla[reachable[i]] = aux;
553 }
554 //}
555 }
556 */
557 }
558 else {
559 // up: unsigned 'p'
560 const uint64_t up = *p;
561 cost = std::numeric_limits<uint64_t>::max();
562
563 std::vector<edge> edges(2*up + 2);
564 for (uint64_t i = 0; i <= 2*up + 1; ++i) {
565 edges[i] = {one_node, ord[i].v};
566 }
567
568 // Transform g into Y
569 t.remove_edges(edges, false, false);
570
571 // Central tree size
572 uint64_t size_rest_of_trees= 0;
573 for (uint64_t i = 2*up + 2; i < ord.size() ;++i) {
574 size_rest_of_trees += ord[i].size;
575 }
576
577 for (uint64_t i = 0; i <= 2*up + 1; ++i) {
578 const auto P_i = get_P(up, i);
579 t.add_edge(one_node, ord[i].v, false, false);
580
581 uint64_t c_i = 0;
582 linear_arrangement arr_aux = mla;
583 uint64_t start_aux = start;
584
585 if constexpr (root == Dopt_utils::LEFT_ANCHOR) {
586 // Left part of the arrangement
587 for (uint64_t j = 1; j <= up; ++j) {
588 const position pos_in_ord = P_i[j];
589
590 uint64_t c_i_j_in = 0;
592 (
593 t,
594 ord[pos_in_ord].v, start_aux,
595 arr_aux, c_i_j_in
596 );
597 start_aux += ord[pos_in_ord].size;
598 c_i += c_i_j_in;
599 }
600
601 // Central part of the arrangement
602 uint64_t c_i_j = 0;
604 (t, one_node, start_aux, arr_aux, c_i_j);
605
606 start_aux += ord[i].size + 1 + size_rest_of_trees;
607 c_i += c_i_j;
608
609 // Right part of the arrangement
610 for (uint64_t j = up + 1; j <= 2*up + 1; ++j) {
611 const position pos_in_ord = P_i[j];
612
613 uint64_t c_i_j_in = 0;
615 (
616 t,
617 ord[pos_in_ord].v, start_aux,
618 arr_aux, c_i_j_in
619 );
620 start_aux += ord[pos_in_ord].size;
621 c_i += c_i_j_in;
622 }
623 }
624 else { // root == RIGHT_ANCHOR
625
626 // Right part of the arrangement
627 for (uint64_t j = 2*up + 1; j >= up + 1; --j) {
628 const position pos_in_ord = P_i[j];
629
630 uint64_t c_i_j_in = 0;
632 (
633 t,
634 ord[pos_in_ord].v, start_aux,
635 arr_aux, c_i_j_in
636 );
637 start_aux += ord[pos_in_ord].size;
638 c_i += c_i_j_in;
639 }
640
641 // Central part of the arrangement
642 uint64_t c_i_j = 0;
644 (t, one_node, start_aux, arr_aux, c_i_j);
645
646 start_aux += ord[i].size + 1 + size_rest_of_trees;
647 c_i += c_i_j;
648
649 // Left part of the arrangement
650 for (uint64_t j = up; j >= 1; --j) {
651 const position pos_in_ord = P_i[j];
652
653 uint64_t c_i_j_in = 0;
655 (
656 t,
657 ord[pos_in_ord].v, start_aux,
658 arr_aux, c_i_j_in
659 );
660 start_aux += ord[pos_in_ord].size;
661 c_i += c_i_j_in;
662 }
663 }
664
665 // Adding parts of the anchors over trees nearer to the central tree
666 c_i += size_tree*(up + 1);
667 c_i -= (up + 1)*ord[P_i[P_i.size() - 1]].size;
668
669 uint64_t subs = 0;
670 for (uint64_t j = 1; j <= up; ++j) {
671 subs += (up - j + 1)*(ord[P_i[j]].size + ord[P_i[2*up - j + 1]].size);
672 }
673 c_i -= subs;
674 c_i += up; // NOT IN CHUNG'S PAPER
675
676 if (c_i < cost) {
677 cost = c_i;
678 mla = arr_aux;
679 }
680#if defined DEBUG
681 assert(one_node != ord[i].v);
682#endif
683 t.remove_edge(one_node, ord[i].v, false, false);
684 }
685
686 // Transform g into its previous form
687 t.add_edges(edges, false, false);
688
689 }
690 }
691}
692
693} // -- namespace dmin_Chung
694
704template <bool make_arrangement>
705[[nodiscard]] std::conditional_t<
706 make_arrangement,
707 std::pair<uint64_t, linear_arrangement>,
708 uint64_t
709>
711{
712#if defined DEBUG
713 assert(t.is_tree());
714#endif
715
716 graphs::free_tree T = t;
717
718 uint64_t Dmin = 0;
719 linear_arrangement arr(make_arrangement ? t.get_num_nodes() : 0);
721
722 if constexpr (make_arrangement) {
723 return {Dmin, std::move(arr)};
724 }
725 else {
726 return Dmin;
727 }
728}
729
730} // -- namespace unconstrained
731} // -- namespace Dmin
732} // -- namespace detail
733} // -- namespace lal
Abstract graph Breadth-First Search traversal.
Definition traversal.hpp:89
Free tree graph class.
Definition free_tree.hpp:60
Linear arrangement of vertices.
Definition linear_arrangement.hpp:103
std::optional< uint64_t > calculate_p(const uint64_t n, const ordering &ord) noexcept
Calculate .
Definition Unconstrained_FC.hpp:193
array< node_size > ordering
Typedef for a useful type.
Definition Unconstrained_FC.hpp:74
array< uint64_t > get_Q(const uint64_t q, const uint64_t i) noexcept
Calculate .
Definition Unconstrained_FC.hpp:271
std::optional< uint64_t > calculate_q(const uint64_t n, const ordering &ord) noexcept
Calculate .
Definition Unconstrained_FC.hpp:116
ordering get_ordering(const graphs::free_tree &t, const node u) noexcept
Sort the children of u in the rooted tree .
Definition Unconstrained_FC.hpp:306
array< uint64_t > get_P(const uint64_t p, uint64_t i) noexcept
Calculate .
Definition Unconstrained_FC.hpp:233
void calculate_mla(graphs::free_tree &t, const node one_node, const position start, linear_arrangement &mla, uint64_t &cost) noexcept
Calculate a minimum linear arrangment using Fan Chung's algorithm.
Definition Unconstrained_FC.hpp:356
std::conditional_t< make_arrangement, std::pair< uint64_t, linear_arrangement >, uint64_t > FanChung_2(const graphs::free_tree &t) noexcept
Calculates a minimum linear arrangment using Fan Chung's algorithm.
Definition Unconstrained_FC.hpp:710
static constexpr char NO_ANCHOR
The tree is not anchored.
Definition Dopt_utils.hpp:101
static constexpr char RIGHT_ANCHOR
The tree is right-anchored.
Definition Dopt_utils.hpp:99
static constexpr char LEFT_ANCHOR
The tree is left-anchored.
Definition Dopt_utils.hpp:97
@ non_increasing
Non-increasing sort.
void counting_sort(const value_iterator_t begin, const value_iterator_t end, const std::size_t largest_key_plus_1, const Callable &key, countingsort::memory< value_t > &mem) noexcept
Counting sort algorithm with reusable memory.
Definition counting_sort.hpp:152
void get_size_subtrees(const tree_t &t, const node u, const node v, uint64_t *const sizes) noexcept
Calculate the size of every subtree of the tree t.
Definition size_subtrees.hpp:74
std::pair< node, node > retrieve_centroid(const tree_t &t, const node x=0) noexcept
Calculate the centroid of the connected component that has node x.
Definition tree_centroid.hpp:416
Main namespace of the library.
Definition basic_types.hpp:48
uint64_t position
Node's position type.
Definition basic_types.hpp:53
uint64_t node
Node type. See Node / Vertex page for further details.
Definition basic_types.hpp:51
std::vector< node > neighbourhood
List of nodes.
Definition basic_types.hpp:64
Wrapper of a C array for automatic deallocation of memory.
Definition array.hpp:59
T * begin() noexcept
Non-constant raw pointer to first element.
Definition array.hpp:300
T * end() noexcept
Non-constant raw pointer to last+1 element.
Definition array.hpp:302
std::size_t size() const noexcept
Size of the array.
Definition array.hpp:215
Struct used in many algorithms to sort vertices according to some integer value.
Definition pairs_utils.hpp:54