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