LAL: Linear Arrangement Library 23.01.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
Dmin_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 - 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 * 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 (lalemany@cs.upc.edu)
34 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
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/data_array.hpp>
53#include <lal/detail/macros/basic_convert.hpp>
54#include <lal/detail/linarr/Dopt_utils.hpp>
55
56typedef std::pair<uint64_t,lal::node> size_node;
58
59namespace lal {
60namespace detail {
61namespace Dmin {
62namespace unconstrained {
63
70namespace Shiloach {
71using namespace Dopt_utils;
72
84template <char anchored>
86(const uint64_t n, const ordering& ord, uint64_t& s_0, uint64_t& s_1)
87noexcept
88{
89 // anchored is ANCHOR or NO_ANCHOR
90 // left or right anchored is not important for the cost
91 static_assert(anchored == NO_ANCHOR or anchored == ANCHOR);
92
93 // number of subtrees
94 const uint64_t k = ord.size() - 1;
95
96 uint64_t n_0 = ord[0].first;
97 uint64_t max_p = 0;
98
99 if constexpr (anchored == NO_ANCHOR) {
100 // -- not anchored
101
102 // Maximum possible p_alpha
103 max_p = k/2;
104 if (max_p == 0) { return 0; }
105
106 uint64_t sum = 0;
107 for (uint64_t i = 0; i <= 2*max_p; ++i) { sum += ord[i].first; }
108
109 uint64_t n_star = n - sum;
110 uint64_t tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
111
112 // n_0 >= n_1 >= ... >= n_k
113 uint64_t n_p = ord[2*max_p].first;
114 while (max_p > 0 and n_p <= tricky_formula) {
115 sum -= ord[2*max_p].first + ord[2*max_p - 1].first;
116
117 --max_p;
118 n_star = n - sum;
119 tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
120
121 // --
122 //if (max_p > 0) { n_p = ord[2*max_p].first; }
123 n_p = (max_p > 0 ? ord[2*max_p].first : n_p);
124 // --
125 }
126 s_0 = max_p*(n_star + 1 + n_0);
127 s_1 = 0;
128 for (uint64_t i = 1; i < max_p; ++i) {
129 s_0 += i*(ord[2*i + 1].first + ord[2*i + 2].first);
130 }
131 }
132 else {
133 // -- anchored
134
135 // Maximum possible p_alpha
136 max_p = (k + 1)/2;
137 if (max_p == 0) { return 0; }
138
139 uint64_t sum = 0;
140 for (uint64_t i = 0; i <= 2*max_p - 1; ++i) { sum += ord[i].first; }
141 uint64_t n_star = n - sum;
142 uint64_t tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
143 uint64_t n_p = ord[2*max_p - 1].first;
144 while (max_p > 0 and n_p <= tricky_formula) {
145 sum -= ord[2*max_p - 1].first;
146 sum -= ord[2*max_p - 2].first;
147 --max_p;
148 n_star = n - sum;
149 tricky_formula = (n_0 + 2)/2 + (n_star + 2)/2;
150
151 // --
152 //if (max_p > 0) { n_p = ord[2*max_p - 1].first; }
153 n_p = (max_p > 0 ? ord[2*max_p - 1].first : n_p);
154 // --
155 }
156 s_0 = 0;
157 s_1 = max_p*(n_star + 1 + n_0) - 1;
158 for (uint64_t i = 1; i < max_p; ++i) {
159 s_1 += i*(ord[2*i].first + ord[2*i + 1].first);
160 }
161 }
162 return max_p;
163}
164
181template <char alpha, bool make_arrangement>
184 node root_or_anchor, position start, position end,
185 linear_arrangement& mla, uint64_t& cost
186)
187noexcept
188{
189 static_assert
191
192 // Size of the tree
193 const uint64_t size_tree = t.get_num_nodes_component(root_or_anchor);
194
195#if defined DEBUG
196 assert(size_tree > 0);
197#endif
198
199 // Base case
200 if (size_tree == 1) {
201 cost = 0;
202 if constexpr (make_arrangement) {
203 mla.assign(root_or_anchor, start);
204 }
205 return;
206 }
207
208 // Recursion for COST A
209 const node v_star = (
210 alpha == NO_ANCHOR ?
211 detail::retrieve_centroid(t, root_or_anchor).first : root_or_anchor
212 );
213
214 // Let 'T_v' to be a tree rooted at vertex 'v'.
215 // Order subtrees of 'T_v' by size.
216 ordering ord(t.get_degree(v_star));
217 {
218 // Retrieve size of every subtree. Let 'T_v[u]' be the subtree
219 // of 'T_v' rooted at vertex 'u'. Now,
220 // s[u] := the size of the subtree 'T_v[u]'
221 data_array<uint64_t> s(t.get_num_nodes());
222 detail::get_size_subtrees(t, v_star, s.begin());
223
224 uint64_t M = 0; // maximum of the sizes (needed for the counting sort algorithm)
225 const neighbourhood& v_star_neighs = t.get_neighbours(v_star);
226 for (std::size_t i = 0; i < v_star_neighs.size(); ++i) {
227 // i-th child of v_star
228 const node ui = v_star_neighs[i];
229 // size of subtree rooted at 'ui'
230 const uint64_t s_ui = s[ui];
231
232 ord[i].first = s_ui;
233 M = std::max(M, s_ui);
234 ord[i].second = ui;
235 }
237 <size_node, sorting::non_increasing_t>
238 (
239 ord.begin(), ord.end(), M, ord.size(),
240 [](const size_node& p) { return p.first; }
241 );
242 }
243
244 const node v_0 = ord[0].second; // Root of biggest subtree
245 const uint64_t n_0 = ord[0].first; // Size of biggest subtree
246
247 // remove edge connecting v_star and its largest subtree
248 t.remove_edge(v_star, v_0, false, false);
249
250 uint64_t c1, c2;
251 c1 = c2 = 0;
252
253 // t -t0 : t0 if t has a LEFT_ANCHOR
254 if constexpr (alpha == LEFT_ANCHOR) {
255 calculate_mla<NO_ANCHOR, make_arrangement>
256 (t, v_star, start, end - n_0, mla, c2);
257
258 calculate_mla<LEFT_ANCHOR, make_arrangement>
259 (t, v_0, end - n_0 + 1, end, mla, c1);
260 }
261 // t0 : t- t0 if t has NO_ANCHOR or RIGHT_ANCHOR
262 else {
263 calculate_mla<RIGHT_ANCHOR, make_arrangement>
264 (t, v_0, start, start + n_0 - 1, mla, c1);
265
266 constexpr auto new_alpha =
268
269 calculate_mla<new_alpha, make_arrangement>
270 (t, v_star, start + n_0, end, mla, c2);
271 }
272
273 // Cost for recursion A
274 if constexpr (alpha == NO_ANCHOR) { cost = c1 + c2 + 1; }
275 else { cost = c1 + c2 + size_tree - n_0; }
276
277 // reconstruct t
278 t.add_edge(v_star, v_0, false, false);
279
280 // Recursion B
281
282 // Left or right anchored is not important for the cost.
283 // Note that the result returned is either 0 or 1.
284 constexpr auto anchored =
286
287 uint64_t s_0 = 0;
288 uint64_t s_1 = 0;
289 const uint64_t p_alpha =
290 calculate_p_alpha<anchored>(size_tree, ord, s_0, s_1);
291
292 uint64_t cost_B = 0;
293 linear_arrangement mla_B(make_arrangement ? mla : 0);
294
295 if (p_alpha > 0) {
296 std::vector<edge> edges(2*p_alpha - anchored);
297
298 // number of nodes not in the central tree
299 for (uint64_t i = 1; i <= 2*p_alpha - anchored; ++i) {
300 const node r = ord[i].second;
301 edges[i - 1].first = v_star;
302 edges[i - 1].second = r;
303 }
304 t.remove_edges(edges, false, false);
305
306 // t1 : t3 : ... : t* : ... : t4 : t2 if t has NO_ANCHOR or RIGHT_ANCHOR
307 // t2 : t4 : ... : t* : ... : t3 : t1 ig t has LEFT_ANCHOR
308 for(uint64_t i = 1; i <= 2*p_alpha - anchored; ++i) {
309 uint64_t c_aux = 0;
310
311 const node r = ord[i].second;
312 const uint64_t n_i = ord[i].first;
313 if ((alpha == LEFT_ANCHOR and i%2 == 0) or (alpha != LEFT_ANCHOR and i%2 == 1)) {
314 calculate_mla<RIGHT_ANCHOR, make_arrangement>
315 (t, r, start, start + n_i - 1, mla_B, c_aux);
316
317 cost_B += c_aux;
318 start += n_i;
319 }
320 else {
321 calculate_mla<LEFT_ANCHOR, make_arrangement>
322 (t, r, end - n_i + 1, end, mla_B, c_aux);
323
324 cost_B += c_aux;
325 end -= n_i;
326 }
327 }
328
329 // t*
330 uint64_t c_aux = 0;
331 calculate_mla<NO_ANCHOR, make_arrangement>
332 (t, v_star, start, end, mla_B, c_aux);
333
334 cost_B += c_aux;
335
336 // reconstruct t
337 t.add_edges(edges, false, false);
338
339 // We add the anchors part not previously added
340 if constexpr (alpha == NO_ANCHOR) {
341 cost_B += s_0;
342 }
343 else {
344 cost_B += s_1;
345 }
346
347 }
348
349 // We choose B-recursion only if it is better
350 if (p_alpha != 0) {
351 if (cost_B < cost) {
352 if constexpr (make_arrangement) {
353 //mla.swap(mla_B);
354 mla = std::move(mla_B);
355 }
356
357 cost = cost_B;
358 }
359 }
360}
361
362} // -- namespace dmin_shiloach
363
373template <bool make_arrangement>
374std::conditional_t<
375 make_arrangement,
376 std::pair<uint64_t, linear_arrangement>,
377 uint64_t
378>
380noexcept
381{
382#if defined DEBUG
383 assert(t.is_tree());
384#endif
385
386 uint64_t Dmin = 0;
387 linear_arrangement arrangement(make_arrangement ? t.get_num_nodes() : 0);
388
389 graphs::free_tree T = t;
390 // Positions 0, 1, ..., t.get_num_nodes() - 1
391 Shiloach::calculate_mla<Shiloach::NO_ANCHOR, make_arrangement>
392 (T, 0, 0, t.get_num_nodes() - 1, arrangement, Dmin);
393
394 if constexpr (make_arrangement) {
395 return {Dmin, std::move(arrangement)};
396 }
397 else {
398 return Dmin;
399 }
400}
401
402} // -- namespace unconstrained
403} // -- namespace Dmin
404} // -- namespace detail
405} // -- namespace lal
Free tree graph class.
Definition: free_tree.hpp:60
uint64_t get_num_nodes() const noexcept
Returns the number of ndoes.
Definition: graph.hpp:198
Linear arrangement of vertices.
Definition: linear_arrangement.hpp:103
void calculate_mla(graphs::free_tree &t, 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: Dmin_Unconstrained_YS.hpp:182
uint64_t calculate_p_alpha(const uint64_t n, const ordering &ord, uint64_t &s_0, uint64_t &s_1) noexcept
Calculate .
Definition: Dmin_Unconstrained_YS.hpp:86
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: Dmin_Unconstrained_YS.hpp:379
constexpr char NO_ANCHOR
The tree is not anchored.
Definition: Dopt_utils.hpp:92
constexpr char ANCHOR
The tree is anchored.
Definition: Dopt_utils.hpp:94
constexpr char RIGHT_ANCHOR
The tree is right-anchored.
Definition: Dopt_utils.hpp:90
constexpr char LEFT_ANCHOR
The tree is left-anchored.
Definition: Dopt_utils.hpp:88
void counting_sort(const value_iterator_t begin, const value_iterator_t end, const std::size_t largest_key_plus_1, const std::function< std::size_t(const value_t &)> &key, countingsort::memory< value_t > &mem) noexcept
Counting sort algorithm with reusable memory.
Definition: counting_sort.hpp:155
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_C.hpp:71
std::pair< node, node > retrieve_centroid(const tree_t &t, const node x) noexcept
Calculate the centroid of the connected component that has node x.
Definition: tree_centroid.hpp:396
Main namespace of the library.
Definition: basic_types.hpp:50
uint64_t position
Node's position type.
Definition: basic_types.hpp:55
uint64_t node
Node type. See Node / Vertex page for further details.
Definition: basic_types.hpp:53
std::vector< node > neighbourhood
List of nodes.
Definition: basic_types.hpp:62
Wrapper of a C array for autmatic deallocation of memory.
Definition: data_array.hpp:59
T & first() noexcept
Non-constant reference to the first element in the array.
Definition: data_array.hpp:234
std::size_t size() const noexcept
Size of the array.
Definition: data_array.hpp:204
T * end() noexcept
Non-constant raw pointer to last+1 element.
Definition: data_array.hpp:293
T * begin() noexcept
Non-constant raw pointer to first element.
Definition: data_array.hpp:291
Non-increasing sort.
Definition: sorting_types.hpp:51