LAL: Linear Arrangement Library 24.10.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
Unconstrained_YS.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#include <vector>
46
47#include <lal/linear_arrangement.hpp>
48#include <lal/detail/graphs/traversal.hpp>
49#include <lal/detail/graphs/size_subtrees.hpp>
50#include <lal/detail/properties/tree_centroid.hpp>
51#include <lal/detail/sorting/counting_sort.hpp>
52#include <lal/detail/array.hpp>
53#include <lal/detail/macros/basic_convert.hpp>
54#include <lal/detail/linarr/D/Dopt_utils.hpp>
55
56namespace lal {
57namespace detail {
58namespace Dmin {
59namespace unconstrained {
60
67namespace Shiloach {
68
71
83template <char anchored>
84[[nodiscard]] uint64_t calculate_p_alpha
85(const uint64_t n, const ordering& ord, uint64_t& s_0, uint64_t& s_1)
86noexcept
87{
88 // anchored is ANCHOR or NO_ANCHOR
89 // left or right anchored is not important for the cost
90 static_assert(
91 anchored == Dopt_utils::NO_ANCHOR or
92 anchored == Dopt_utils::ANCHOR
93 );
94
95 // number of subtrees
96 const uint64_t k = ord.size() - 1;
97
98 uint64_t n_0 = ord[0].size;
99 uint64_t max_p = 0;
100
101 if constexpr (anchored == Dopt_utils::NO_ANCHOR) {
102 // -- not anchored
103
104 // Maximum possible p_alpha
105 max_p = k/2;
106 if (max_p == 0) { return 0; }
107
108 uint64_t sum = 0;
109 for (uint64_t i = 0; i <= 2*max_p; ++i) { sum += ord[i].size; }
110
111 uint64_t n_star = n - sum;
112 uint64_t tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
113
114 // n_0 >= n_1 >= ... >= n_k
115 uint64_t n_p = ord[2*max_p].size;
116 while (max_p > 0 and n_p <= tricky_formula) {
117 sum -= ord[2*max_p].size + ord[2*max_p - 1].size;
118
119 --max_p;
120 n_star = n - sum;
121 tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
122
123 // --
124 //if (max_p > 0) { n_p = ord[2*max_p].first; }
125 n_p = (max_p > 0 ? ord[2*max_p].size : n_p);
126 // --
127 }
128 s_0 = max_p*(n_star + 1 + n_0);
129 s_1 = 0;
130 for (uint64_t i = 1; i < max_p; ++i) {
131 s_0 += i*(ord[2*i + 1].size + ord[2*i + 2].size);
132 }
133 }
134 else {
135 // -- anchored
136
137 // Maximum possible p_alpha
138 max_p = (k + 1)/2;
139 if (max_p == 0) { return 0; }
140
141 uint64_t sum = 0;
142 for (uint64_t i = 0; i <= 2*max_p - 1; ++i) { sum += ord[i].size; }
143 uint64_t n_star = n - sum;
144 uint64_t tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
145 uint64_t n_p = ord[2*max_p - 1].size;
146 while (max_p > 0 and n_p <= tricky_formula) {
147 sum -= ord[2*max_p - 1].size;
148 sum -= ord[2*max_p - 2].size;
149 --max_p;
150 n_star = n - sum;
151 tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
152
153 // --
154 //if (max_p > 0) { n_p = ord[2*max_p - 1].first; }
155 n_p = (max_p > 0 ? ord[2*max_p - 1].size : n_p);
156 // --
157 }
158 s_0 = 0;
159 s_1 = max_p*(n_star + 1 + n_0) - 1;
160 for (uint64_t i = 1; i < max_p; ++i) {
161 s_1 += i*(ord[2*i].size + ord[2*i + 1].size);
162 }
163 }
164 return max_p;
165}
166
183template <char alpha, bool make_arrangement>
185(
187 const node root_or_anchor,
188 position start,
189 position end,
191 uint64_t& cost
192)
193noexcept
194{
195 static_assert(
199 );
200
201 // Size of the tree
202 const uint64_t size_tree = t.get_num_nodes_component(root_or_anchor);
203
204#if defined DEBUG
205 assert(size_tree > 0);
206#endif
207
208 // Base case
209 if (size_tree == 1) {
210 cost = 0;
211 if constexpr (make_arrangement) {
212 mla.assign(root_or_anchor, start);
213 }
214 return;
215 }
216
217 // Recursion for COST A
218 const node v_star =
220 retrieve_centroid(t, root_or_anchor).first :
221 root_or_anchor;
222
223 // Let 'T_v' to be a tree rooted at vertex 'v'.
224 // Order subtrees of 'T_v' by size.
225 ordering ord(t.get_degree(v_star));
226 {
227 // Retrieve size of every subtree. Let 'T_v[u]' be the subtree
228 // of 'T_v' rooted at vertex 'u'. Now,
229 // s[u] := the size of the subtree 'T_v[u]'
230 array<uint64_t> s(t.get_num_nodes());
231 get_size_subtrees(t, v_star, s.begin());
232
233 uint64_t M = 0; // maximum of the sizes (needed for the counting sort algorithm)
234 const neighbourhood& v_star_neighs = t.get_neighbors(v_star);
235 for (std::size_t i = 0; i < v_star_neighs.size(); ++i) {
236 // i-th child of v_star
237 const node ui = v_star_neighs[i];
238 // size of subtree rooted at 'ui'
239 const uint64_t s_ui = s[ui];
240
241 ord[i].size = s_ui;
242 M = std::max(M, s_ui);
243 ord[i].v = ui;
244 }
247 (
248 ord.begin(), ord.end(), M, ord.size(),
249 [](const node_size& p) { return p.size; }
250 );
251 }
252
253 const node v_0 = ord[0].v; // Root of biggest subtree
254 const uint64_t n_0 = ord[0].size; // Size of biggest subtree
255
256 // remove edge connecting v_star and its largest subtree
257 t.remove_edge(v_star, v_0, false, false);
258
259 uint64_t c1, c2;
260 c1 = c2 = 0;
261
262 // t -t0 : t0 if t has a LEFT_ANCHOR
263 if constexpr (alpha == Dopt_utils::LEFT_ANCHOR) {
265 (t, v_star, start, end - n_0, mla, c2);
266
268 (t, v_0, end - n_0 + 1, end, mla, c1);
269 }
270 // t0 : t- t0 if t has NO_ANCHOR or RIGHT_ANCHOR
271 else {
273 (t, v_0, start, start + n_0 - 1, mla, c1);
274
275 constexpr auto new_alpha =
279
281 (t, v_star, start + n_0, end, mla, c2);
282 }
283
284 // Cost for recursion A
285 if constexpr (alpha == Dopt_utils::NO_ANCHOR) { cost = c1 + c2 + 1; }
286 else { cost = c1 + c2 + size_tree - n_0; }
287
288 // reconstruct t
289 t.add_edge(v_star, v_0, false, false);
290
291 // Recursion B
292
293 // Left or right anchored is not important for the cost.
294 // Note that the result returned is either 0 or 1.
295 constexpr auto anchored =
299
300 uint64_t s_0 = 0;
301 uint64_t s_1 = 0;
302 const uint64_t p_alpha =
303 calculate_p_alpha<anchored>(size_tree, ord, s_0, s_1);
304
305 uint64_t cost_B = 0;
306 linear_arrangement mla_B(make_arrangement ? mla : 0);
307
308 if (p_alpha > 0) {
309 std::vector<edge> edges(2*p_alpha - anchored);
310
311 // number of nodes not in the central tree
312 for (uint64_t i = 1; i <= 2*p_alpha - anchored; ++i) {
313 edges[i - 1] = {v_star, ord[i].v};
314 }
315 t.remove_edges(edges, false, false);
316
317 // t1 : t3 : ... : t* : ... : t4 : t2 if t has NO_ANCHOR or RIGHT_ANCHOR
318 // t2 : t4 : ... : t* : ... : t3 : t1 if t has LEFT_ANCHOR
319 for(uint64_t i = 1; i <= 2*p_alpha - anchored; ++i) {
320 uint64_t c_aux = 0;
321
322 const node r = ord[i].v;
323 const uint64_t n_i = ord[i].size;
324 if (
325 (alpha == Dopt_utils::LEFT_ANCHOR and i%2 == 0) or
326 (alpha != Dopt_utils::LEFT_ANCHOR and i%2 == 1)
327 )
328 {
330 (t, r, start, start + n_i - 1, mla_B, c_aux);
331
332 cost_B += c_aux;
333 start += n_i;
334 }
335 else {
337 (t, r, end - n_i + 1, end, mla_B, c_aux);
338
339 cost_B += c_aux;
340 end -= n_i;
341 }
342 }
343
344 // t*
345 uint64_t c_aux = 0;
347 (t, v_star, start, end, mla_B, c_aux);
348
349 cost_B += c_aux;
350
351 // reconstruct t
352 t.add_edges(edges, false, false);
353
354 // We add the anchors part not previously added
355 if constexpr (alpha == Dopt_utils::NO_ANCHOR) {
356 cost_B += s_0;
357 }
358 else {
359 cost_B += s_1;
360 }
361
362 }
363
364 // We choose B-recursion only if it is better
365 if (p_alpha != 0) {
366 if (cost_B < cost) {
367 if constexpr (make_arrangement) {
368 //mla.swap(mla_B);
369 mla = std::move(mla_B);
370 }
371
372 cost = cost_B;
373 }
374 }
375}
376
377} // -- namespace Shiloach
378
388template <bool make_arrangement>
389[[nodiscard]] std::conditional_t<
390 make_arrangement,
391 std::pair<uint64_t, linear_arrangement>,
392 uint64_t
393>
395noexcept
396{
397#if defined DEBUG
398 assert(t.is_tree());
399#endif
400
401 uint64_t Dmin = 0;
402 linear_arrangement arrangement(make_arrangement ? t.get_num_nodes() : 0);
403
404 graphs::free_tree T = t;
405 // Positions 0, 1, ..., t.get_num_nodes() - 1
407 (T, 0, 0, t.get_num_nodes() - 1, arrangement, Dmin);
408
409 if constexpr (make_arrangement) {
410 return {Dmin, std::move(arrangement)};
411 }
412 else {
413 return Dmin;
414 }
415}
416
417} // -- namespace unconstrained
418} // -- namespace Dmin
419} // -- namespace detail
420} // -- namespace lal
Free tree graph class.
Definition free_tree.hpp:60
Linear arrangement of vertices.
Definition linear_arrangement.hpp:103
void calculate_mla(graphs::free_tree &t, const node root_or_anchor, position start, position end, linear_arrangement &mla, uint64_t &cost) noexcept
Calculate a minimum linear arrangment using Shiloach's algorithm.
Definition Unconstrained_YS.hpp:185
uint64_t calculate_p_alpha(const uint64_t n, const ordering &ord, uint64_t &s_0, uint64_t &s_1) noexcept
Calculate .
Definition Unconstrained_YS.hpp:85
array< node_size > ordering
Typedef for a useful type.
Definition Unconstrained_YS.hpp:70
std::conditional_t< make_arrangement, std::pair< uint64_t, linear_arrangement >, uint64_t > YossiShiloach(const graphs::free_tree &t) noexcept
Calculates a minimum linear arrangment using Shiloach's algorithm.
Definition Unconstrained_YS.hpp:394
static constexpr char NO_ANCHOR
The tree is not anchored.
Definition Dopt_utils.hpp:101
static constexpr char ANCHOR
The tree is anchored.
Definition Dopt_utils.hpp:103
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
constexpr uint64_t alpha(const int64_t n, const int64_t d1, const int64_t d2) noexcept
Amount of crossings pairs of edges of given lengths.
Definition predict.hpp:72
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