LAL: Linear Arrangement Library 24.10.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
stack_based.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 <map>
47
48// lal includes
49#include <lal/graphs/graph.hpp>
50#include <lal/detail/arrangement_wrapper.hpp>
51#include <lal/detail/avl.hpp>
52#include <lal/detail/sorting/counting_sort.hpp>
53#include <lal/detail/array.hpp>
54#include <lal/detail/macros/basic_convert.hpp>
55
56#define edge_sorted_by_vertex_index(u,v) (u < v ? edge(u,v) : edge(v,u) )
57#define DECIDED_C_GT (upper_bound + 1)
58
59namespace lal {
60namespace detail {
61namespace crossings {
62
68namespace stack_based {
69
71typedef std::pair<uint64_t, edge> indexed_edge;
72
73// =============================================================================
74// ACTUAL ALGORITHM
75// =============================================================================
76
91template <class graph_t, class arrangement_t>
93(
94 const graph_t& g,
95 const arrangement_t& arr,
96 std::vector<neighbourhood>& adjP,
97 std::vector<std::vector<indexed_edge>>& adjN,
98 uint64_t * const size_adjN_u
99)
100noexcept
101{
102 const uint64_t n = g.get_num_nodes();
103
104 // Retrieve all edges of the graph to sort
105 std::vector<edge> edges = g.get_edges();
106
107 // sort edges of the graph by non-decreasing edge length
108 // l(e_1) <= l(e_2) <= ... <= l(e_m)
111 (
112 edges.begin(), edges.end(),
113 n - 1, // length of the longest edge
114 edges.size(),
115 [&](const edge& e) -> std::size_t {
116
117 const node uu = e.first;
118 const node vv = e.second;
119 const auto [u, v] =
120 (arr[node_t{uu}] < arr[node_t{vv}] ? edge(uu,vv) : edge(vv,uu));
121
122 ++size_adjN_u[u];
123 return abs_diff(arr[node_t{u}], arr[node_t{v}]);
124 }
125 );
126
127 // initialize adjN
128 for (node u = 0; u < n; ++u) {
129 // divide by two because the 'key' function in the call to
130 // the sorting function is called twice for every edge
131#if defined DEBUG
132 assert((size_adjN_u[u]%2) == 0);
133 // the assertion
134 // assert(size_adjN_u[u] != 0);
135 // is wrong.
136#endif
137
138 size_adjN_u[u] /= 2;
139 adjN[u].resize(size_adjN_u[u]);
140 }
141
142 // fill adjP and adjN at the same time
143 for (const auto& [uu, vv] : edges) {
144 // arr[u] < arr[v]
145 const auto [u, v] =
146 (arr[node_t{uu}] < arr[node_t{vv}] ? edge(uu,vv) : edge(vv,uu));
147
148 // oriented edge (u,v) "enters" node v
149 adjP[v].push_back(u);
150
151 // Oriented edge (u,v) "leaves" node u
152 --size_adjN_u[u];
153 adjN[u][size_adjN_u[u]] = indexed_edge(0, edge_sorted_by_vertex_index(u,v));
154 }
155
156#if defined DEBUG
157 for (node u = 0; u < n; ++u) {
158 assert(size_adjN_u[u] == 0);
159 }
160#endif
161}
162
179template <bool decide_upper_bound, class graph_t, class arrangement_t>
180[[nodiscard]] uint64_t compute_C_stack_based
181(
182 const graph_t& g,
183 const arrangement_t& arr,
184 uint64_t * const size_adjN_u,
185 uint64_t upper_bound
186)
187noexcept
188{
189 const uint64_t n = g.get_num_nodes();
190
191 // Adjacency lists, sorted by edge length:
192 // - adjP is sorted by increasing edge length
193 std::vector<neighbourhood> adjP(n);
194 // - adjN is sorted by decreasing edge length
195 std::vector<std::vector<indexed_edge>> adjN(n);
196
197 fill_adjP_adjN(g, arr, adjP, adjN, size_adjN_u);
198
199 // relate each edge to an index
200 std::map<edge, uint64_t> edge_to_idx;
201
202 uint64_t idx = 0;
203 for (position_t pu = 0ull; pu < n; ++pu) {
204 const node u = arr[pu];
205 for (auto& v : adjN[u]) {
206 v.first = idx;
207
208 edge_to_idx.insert( make_pair(v.second, idx) );
209 ++idx;
210 }
211 }
212
213 // stack of the algorithm
215
216 // calculate the number of crossings
217 uint64_t C = 0;
218 for (position_t pu = 0ull; pu < n; ++pu) {
219 const node u = arr[pu];
220 for (node v : adjP[u]) {
221 const edge uv = edge_sorted_by_vertex_index(u,v);
222
223 // the elements inserted into the tree are unique by construction,
224 // so we can remove elements without using their counter
225 // (remove<false>) and use the number of larger nodes
226 // (on_top.num_nodes_larger) to calculate the number of crossings.
227 const auto on_top = S.remove<false>(indexed_edge(edge_to_idx[uv], uv));
228 C += on_top.num_nodes_larger;
229
230 if constexpr (decide_upper_bound) {
231 if (C > upper_bound) { return DECIDED_C_GT; }
232 }
233 }
234 S.join_sorted_all_greater(std::move(adjN[u]));
235 }
236
237 // none of the conditions above were true, so we must have
238 // C <= upper_bound
239 return C;
240}
241
242} // -- namespace stack_based
243
244// =============================================================================
245// CALLS TO ALGORITHM
246// =============================================================================
247
248// ------------------
249// single arrangement
250
259template <class graph_t, class arrangement_t>
260[[nodiscard]] uint64_t n_C_stack_based
261(const graph_t& g, const arrangement_t& arr)
262noexcept
263{
264 const uint64_t n = g.get_num_nodes();
265
266#if defined DEBUG
267 assert(arr.size() == 0 or arr.size() == n);
268#endif
269
270 if (n < 4) { return 0; }
271
272 // size_adjN_u[u] := size of adjN[u]
273 // (adjN declared and defined inside the algorithm)
274 array<uint64_t> size_adjN_u(n, 0);
275
277 (g, arr, size_adjN_u.begin(), 0);
278}
279
280// --------------------
281// list of arrangements
282
290template <class graph_t>
291[[nodiscard]] std::vector<uint64_t> n_C_stack_based
292(const graph_t& g, const std::vector<linear_arrangement>& arrs)
293noexcept
294{
295 const uint64_t n = g.get_num_nodes();
296
297 std::vector<uint64_t> cs(arrs.size(), 0);
298 if (n < 4) { return cs; }
299
300 // size_adjN_u[u] := size of adjN[u]
301 // (adjN declared and defined inside the algorithm)
302 array<uint64_t> size_adjN_u(n, 0);
303
304 /* compute C for every linear arrangement */
305 for (std::size_t i = 0; i < arrs.size(); ++i) {
306#if defined DEBUG
307 // ensure that no linear arrangement is empty
308 assert(arrs[i].size() == n);
309#endif
310
311 // compute C
313 (g, nonidentity_arr(arrs[i]), size_adjN_u.begin(), 0);
314 }
315
316 return cs;
317}
318
319// -----------------------------------------------------------------------------
320// DECISION
321
322// ------------------
323// single arrangement
324
335template <class graph_t, class arrangement_t>
336[[nodiscard]] uint64_t is_n_C_stack_based_lesseq_than
337(
338 const graph_t& g,
339 const arrangement_t& arr,
340 const uint64_t upper_bound
341)
342noexcept
343{
344 const uint64_t n = g.get_num_nodes();
345
346#if defined DEBUG
347 assert(arr.size() == 0 or arr.size() == n);
348#endif
349
350 if (n < 4) { return 0; }
351
352 // size_adjN_u[u] := size of adjN[u]
353 // (adjN declared and defined inside the algorithm)
354 array<uint64_t> size_adjN_u(n, 0);
355
357 (g, arr, size_adjN_u.begin(), upper_bound);
358}
359
360// --------------------
361// list of arrangements
362
372template <class graph_t>
373[[nodiscard]] std::vector<uint64_t> is_n_C_stack_based_lesseq_than
374(
375 const graph_t& g,
376 const std::vector<linear_arrangement>& arrs,
377 const uint64_t upper_bound
378)
379noexcept
380{
381 const uint64_t n = g.get_num_nodes();
382
383 std::vector<uint64_t> cs(arrs.size(), 0);
384 if (n < 4) { return cs; }
385
386 // size_adjN_u[u] := size of adjN[u]
387 // (adjN declared and defined inside the algorithm)
388 array<uint64_t> size_adjN_u(n, 0);
389
390 /* compute C for every linear arrangement */
391 for (std::size_t i = 0; i < arrs.size(); ++i) {
392#if defined DEBUG
393 // ensure that no linear arrangement is empty
394 assert(arrs[i].size() == n);
395#endif
396
397 // compute C
399 (g, nonidentity_arr(arrs[i]), size_adjN_u.begin(), upper_bound);
400 }
401
402 return cs;
403}
404
415template <class graph_t>
416[[nodiscard]] std::vector<uint64_t> is_n_C_stack_based_lesseq_than
417(
418 const graph_t& g,
419 const std::vector<linear_arrangement>& arrs,
420 const std::vector<uint64_t>& upper_bounds
421)
422noexcept
423{
424#if defined DEBUG
425 // ensure that there are as many linear arrangements as upper bounds
426 assert(arrs.size() == upper_bounds.size());
427#endif
428
429 const uint64_t n = g.get_num_nodes();
430
431 std::vector<uint64_t> cs(arrs.size(), 0);
432 if (n < 4) { return cs; }
433
434 // size_adjN_u[u] := size of adjN[u]
435 // (adjN declared and defined inside the algorithm)
436 array<uint64_t> size_adjN_u(n, 0);
437
438 /* compute C for every linear arrangement */
439 for (std::size_t i = 0; i < arrs.size(); ++i) {
440#if defined DEBUG
441 // ensure that no linear arrangement is empty
442 assert(arrs[i].size() == n);
443#endif
444
445 // compute C
447 (g, nonidentity_arr(arrs[i]), size_adjN_u.begin(), upper_bounds[i]);
448 }
449
450 return cs;
451}
452
453} // -- namespace crossings
454} // -- namespace detail
455} // -- namespace lal
Simple class that implements an AVL tree.
Definition avl.hpp:73
frequencies remove(const T &v) noexcept
Remove an element from the tree.
Definition avl.hpp:236
void join_sorted_all_greater(U &&v) noexcept
Add to the tree the elements in the vector v.
Definition avl.hpp:253
std::pair< uint64_t, edge > indexed_edge
Useful typedef.
Definition stack_based.hpp:71
uint64_t compute_C_stack_based(const graph_t &g, const arrangement_t &arr, uint64_t *const size_adjN_u, uint64_t upper_bound) noexcept
Stack based computation of for undirected graphs.
Definition stack_based.hpp:181
void fill_adjP_adjN(const graph_t &g, const arrangement_t &arr, std::vector< neighbourhood > &adjP, std::vector< std::vector< indexed_edge > > &adjN, uint64_t *const size_adjN_u) noexcept
Auxiliary function to sort edges as a function of the arrangement.
Definition stack_based.hpp:93
uint64_t is_n_C_stack_based_lesseq_than(const graph_t &g, const arrangement_t &arr, const uint64_t upper_bound) noexcept
Stack based computation of with early termination.
Definition stack_based.hpp:337
uint64_t n_C_stack_based(const graph_t &g, const arrangement_t &arr) noexcept
Stack based computation of .
Definition stack_based.hpp:261
@ non_decreasing
Non-decreasing sort type.
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
arrangement_wrapper< arrangement_type::nonidentity > nonidentity_arr(const linear_arrangement &arr) noexcept
Shorthand for a nonidentity arrangement.
Definition arrangement_wrapper.hpp:133
constexpr T abs_diff(const T &t1, const T &t2) noexcept
Absolute difference of two values.
Definition basic_convert.hpp:77
Main namespace of the library.
Definition basic_types.hpp:48
std::pair< node, node > edge
See Edge page for further details.
Definition basic_types.hpp:56
uint64_t node
Node type. See Node / Vertex page for further details.
Definition basic_types.hpp:51
std::size_t num_nodes_larger
Number of nodes with a key larger than v in the tree.
Definition avl.hpp:82
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
Typesafe node type.
Definition basic_types.hpp:70
Typesafe position type.
Definition basic_types.hpp:244