1 // Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11
12 #include "gecko.hpp"
13
14 // This file collects the sources of the Gecko library as a single module.
15 // The original library can be found at https://github.com/LLNL/gecko
16 // Used here with permission.
17
18 // ------------------------------------------------------------------------------
19
20 // BSD 3-Clause License
21 //
22 // Copyright (c) 2019-2021, Lawrence Livermore National Security, LLC
23 // All rights reserved.
24 //
25 // Redistribution and use in source and binary forms, with or without
26 // modification, are permitted provided that the following conditions are met:
27 //
28 // * Redistributions of source code must retain the above copyright notice, this
29 // list of conditions and the following disclaimer.
30 //
31 // * Redistributions in binary form must reproduce the above copyright notice,
32 // this list of conditions and the following disclaimer in the documentation
33 // and/or other materials provided with the distribution.
34 //
35 // * Neither the name of the copyright holder nor the names of its
36 // contributors may be used to endorse or promote products derived from
37 // this software without specific prior written permission.
38 //
39 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
40 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
43 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
45 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
46 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
47 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
48 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49
50 // ------------------------------------------------------------------------------
51
52 // Copyright (c) 2019-2021, Lawrence Livermore National Security, LLC and other
53 // gecko project contributors. See the above license for details.
54 // SPDX-License-Identifier: BSD-3-Clause
55 // LLNL-CODE-800597
56
57 #include <algorithm>
58 #include <functional>
59 #include <cstdlib>
60 #include <cstddef>
61 #include <iomanip>
62 #include <iostream>
63 #include <sstream>
64 #include <stdexcept>
65 #include <map>
66
67 // ----- options.h --------------------------------------------------------------
68
69 // ratio of max to min weight for aggregation
70 #ifndef GECKO_PART_FRAC
71 #define GECKO_PART_FRAC 4
72 #endif
73
74 // number of compatible relaxation sweeps
75 #ifndef GECKO_CR_SWEEPS
76 #define GECKO_CR_SWEEPS 1
77 #endif
78
79 // number of Gauss-Seidel relaxation sweeps
80 #ifndef GECKO_GS_SWEEPS
81 #define GECKO_GS_SWEEPS 1
82 #endif
83
84 // max number of nodes in subgraph
85 #ifndef GECKO_WINDOW_MAX
86 #define GECKO_WINDOW_MAX 16
87 #endif
88
89 // use adjacency list (1) or adjacency matrix (0)
90 #ifndef GECKO_WITH_ADJLIST
91 #define GECKO_WITH_ADJLIST 0
92 #endif
93
94 // use nonrecursive permutation algorithm
95 #ifndef GECKO_WITH_NONRECURSIVE
96 #define GECKO_WITH_NONRECURSIVE 0
97 #endif
98
99 // use double-precision computations
100 #ifndef GECKO_WITH_DOUBLE_PRECISION
101 #define GECKO_WITH_DOUBLE_PRECISION 0
102 #endif
103
104 // ----- heap.h -----------------------------------------------------------------
105
106 template <
107 typename T, // data type
108 typename P, // priority type
109 class C = std::less<P>, // comparator for priorities
110 class M = std::map<T, unsigned int> // maps type T to unsigned integer
111 >
112 class DynamicHeap
113 {
114 public:
115 DynamicHeap(size_t count = 0);
~DynamicHeap()116 ~DynamicHeap() {}
117 void insert(T data, P priority);
118 void update(T data, P priority);
119 bool top(T& data);
120 bool top(T& data, P& priority);
121 bool pop();
122 bool extract(T& data);
123 bool extract(T& data, P& priority);
124 bool erase(T data);
125 bool find(T data) const;
126 bool find(T data, P& priority) const;
empty() const127 bool empty() const { return heap.empty(); }
size() const128 size_t size() const { return heap.size(); }
129 private:
130 struct HeapEntry
131 {
HeapEntryDynamicHeap::HeapEntry132 HeapEntry(P p, T d) : priority(p), data(d) {}
133 P priority;
134 T data;
135 };
136 std::vector<HeapEntry> heap;
137 M index;
138 C lower;
139 void ascend(unsigned int i);
140 void descend(unsigned int i);
141 void swap(unsigned int i, unsigned int j);
ordered(unsigned int i,unsigned int j) const142 bool ordered(unsigned int i, unsigned int j) const
143 {
144 return !lower(heap[i].priority, heap[j].priority);
145 }
parent(unsigned int i) const146 unsigned int parent(unsigned int i) const { return (i - 1) / 2; }
left(unsigned int i) const147 unsigned int left(unsigned int i) const { return 2 * i + 1; }
right(unsigned int i) const148 unsigned int right(unsigned int i) const { return 2 * i + 2; }
149 };
150
151 template < typename T, typename P, class C, class M >
DynamicHeap(size_t count)152 DynamicHeap<T, P, C, M>::DynamicHeap(size_t count)
153 {
154 heap.reserve(count);
155 }
156
157 template < typename T, typename P, class C, class M >
158 void
insert(T data,P priority)159 DynamicHeap<T, P, C, M>::insert(T data, P priority)
160 {
161 if (index.find(data) != index.end())
162 {
163 update(data, priority);
164 }
165 else
166 {
167 unsigned int i = (unsigned int)heap.size();
168 heap.push_back(HeapEntry(priority, data));
169 ascend(i);
170 }
171 }
172
173 template < typename T, typename P, class C, class M >
174 void
update(T data,P priority)175 DynamicHeap<T, P, C, M>::update(T data, P priority)
176 {
177 unsigned int i = index[data];
178 heap[i].priority = priority;
179 ascend(i);
180 descend(i);
181 }
182
183 template < typename T, typename P, class C, class M >
184 bool
top(T & data)185 DynamicHeap<T, P, C, M>::top(T& data)
186 {
187 if (!heap.empty())
188 {
189 data = heap[0].data;
190 return true;
191 }
192 else
193 {
194 return false;
195 }
196 }
197
198 template < typename T, typename P, class C, class M >
199 bool
top(T & data,P & priority)200 DynamicHeap<T, P, C, M>::top(T& data, P& priority)
201 {
202 if (!heap.empty())
203 {
204 data = heap[0].data;
205 priority = heap[0].priority;
206 return true;
207 }
208 else
209 {
210 return false;
211 }
212 }
213
214 template < typename T, typename P, class C, class M >
215 bool
pop()216 DynamicHeap<T, P, C, M>::pop()
217 {
218 if (!heap.empty())
219 {
220 T data = heap[0].data;
221 swap(0, (unsigned int)heap.size() - 1);
222 index.erase(data);
223 heap.pop_back();
224 if (!heap.empty())
225 {
226 descend(0);
227 }
228 return true;
229 }
230 else
231 {
232 return false;
233 }
234 }
235
236 template < typename T, typename P, class C, class M >
237 bool
extract(T & data)238 DynamicHeap<T, P, C, M>::extract(T& data)
239 {
240 if (!heap.empty())
241 {
242 data = heap[0].data;
243 return pop();
244 }
245 else
246 {
247 return false;
248 }
249 }
250
251 template < typename T, typename P, class C, class M >
252 bool
extract(T & data,P & priority)253 DynamicHeap<T, P, C, M>::extract(T& data, P& priority)
254 {
255 if (!heap.empty())
256 {
257 data = heap[0].data;
258 priority = heap[0].priority;
259 return pop();
260 }
261 else
262 {
263 return false;
264 }
265 }
266
267 template < typename T, typename P, class C, class M >
268 bool
erase(T data)269 DynamicHeap<T, P, C, M>::erase(T data)
270 {
271 if (index.find(data) == index.end())
272 {
273 return false;
274 }
275 unsigned int i = index[data];
276 swap(i, heap.size() - 1);
277 index.erase(data);
278 heap.pop_back();
279 if (i < heap.size())
280 {
281 ascend(i);
282 descend(i);
283 }
284 return true;
285 }
286
287 template < typename T, typename P, class C, class M >
288 bool
find(T data) const289 DynamicHeap<T, P, C, M>::find(T data) const
290 {
291 return index.find(data) != index.end();
292 }
293
294 template < typename T, typename P, class C, class M >
295 bool
find(T data,P & priority) const296 DynamicHeap<T, P, C, M>::find(T data, P& priority) const
297 {
298 typename M::const_iterator p;
299 if ((p = index.find(data)) == index.end())
300 {
301 return false;
302 }
303 unsigned int i = p->second;
304 priority = heap[i].priority;
305 return true;
306 }
307
308 template < typename T, typename P, class C, class M >
309 void
ascend(unsigned int i)310 DynamicHeap<T, P, C, M>::ascend(unsigned int i)
311 {
312 for (unsigned int j; i && !ordered(j = parent(i), i); i = j)
313 {
314 swap(i, j);
315 }
316 index[heap[i].data] = i;
317 }
318
319 template < typename T, typename P, class C, class M >
320 void
descend(unsigned int i)321 DynamicHeap<T, P, C, M>::descend(unsigned int i)
322 {
323 for (unsigned int j, k;
324 (j = ((k = left(i)) < heap.size() && !ordered(i, k) ? k : i),
325 j = ((k = right(i)) < heap.size() && !ordered(j, k) ? k : j)) != i;
326 i = j)
327 {
328 swap(i, j);
329 }
330 index[heap[i].data] = i;
331 }
332
333 template < typename T, typename P, class C, class M >
334 void
swap(unsigned int i,unsigned int j)335 DynamicHeap<T, P, C, M>::swap(unsigned int i, unsigned int j)
336 {
337 std::swap(heap[i], heap[j]);
338 index[heap[i].data] = i;
339 }
340
341 // ----- subgraph.h -------------------------------------------------------------
342
343 namespace Gecko
344 {
345
346 // Node in a subgraph.
347 class Subnode
348 {
349 public:
350 typedef unsigned char Index;
351 Float pos; // node position
352 WeightedSum cost; // external cost at this position
353 };
354
355 class Subgraph
356 {
357 public:
358 Subgraph(Graph* g, uint n);
~Subgraph()359 ~Subgraph() { delete[] cache; }
360 void optimize(uint k);
361
362 private:
363 Graph* const g; // full graph
364 const uint n; // number of subgraph nodes
365 Functional* const f; // ordering functional
366 WeightedSum min; // minimum cost so far
367 Subnode::Index best[GECKO_WINDOW_MAX]; // best permutation so far
368 Subnode::Index perm[GECKO_WINDOW_MAX]; // current permutation
369 const Subnode* node[GECKO_WINDOW_MAX]; // pointers to precomputed nodes
370 Subnode* cache; // precomputed node positions and costs
371 #if GECKO_WITH_ADJLIST
372 Subnode::Index
373 adj[GECKO_WINDOW_MAX][GECKO_WINDOW_MAX]; // internal adjacency list
374 #else
375 uint adj[GECKO_WINDOW_MAX]; // internal adjacency matrix
376 #endif
377 Float weight[GECKO_WINDOW_MAX][GECKO_WINDOW_MAX]; // internal arc weights
378 WeightedSum cost(uint k) const;
379 void swap(uint k);
380 void swap(uint k, uint l);
381 void optimize(WeightedSum c, uint i);
382 };
383
384 }
385
386 // ----- subgraph.cpp -----------------------------------------------------------
387
388 using namespace Gecko;
389
390 // Constructor.
Subgraph(Graph * g,uint n)391 Subgraph::Subgraph(Graph* g, uint n) : g(g), n(n), f(g->functional)
392 {
393 if (n > GECKO_WINDOW_MAX)
394 {
395 throw std::out_of_range("optimization window too large");
396 }
397 cache = new Subnode[n << n];
398 }
399
400 // Cost of k'th node's edges to external nodes and nodes at {k+1, ..., n-1}.
401 WeightedSum
cost(uint k) const402 Subgraph::cost(uint k) const
403 {
404 Subnode::Index i = perm[k];
405 WeightedSum c = node[i]->cost;
406 Float p = node[i]->pos;
407 #if GECKO_WITH_ADJLIST
408 for (k = 0; adj[i][k] != i; k++)
409 {
410 Subnode::Index j = adj[i][k];
411 Float l = node[j]->pos - p;
412 if (l > 0)
413 {
414 Float w = weight[i][k];
415 f->accumulate(c, WeightedValue(l, w));
416 }
417 }
418 #else
419 uint m = adj[i];
420 while (++k < n)
421 {
422 Subnode::Index j = perm[k];
423 if (m & (1u << j))
424 {
425 Float l = node[j]->pos - p;
426 Float w = weight[i][j];
427 f->accumulate(c, WeightedValue(l, w));
428 }
429 }
430 #endif
431 return c;
432 }
433
434 // Swap the two nodes in positions k and k + 1.
435 void
swap(uint k)436 Subgraph::swap(uint k)
437 {
438 uint l = k + 1;
439 Subnode::Index i = perm[k];
440 Subnode::Index j = perm[l];
441 perm[k] = j;
442 perm[l] = i;
443 node[i] -= ptrdiff_t(1) << j;
444 node[j] += ptrdiff_t(1) << i;
445 }
446
447 // Swap the two nodes in positions k and l, k <= l.
448 void
swap(uint k,uint l)449 Subgraph::swap(uint k, uint l)
450 {
451 Subnode::Index i = perm[k];
452 Subnode::Index j = perm[l];
453 perm[k] = j;
454 perm[l] = i;
455 // Update node positions.
456 uint m = 0;
457 while (++k < l)
458 {
459 Subnode::Index h = perm[k];
460 node[h] += ptrdiff_t(1) << i;
461 node[h] -= ptrdiff_t(1) << j;
462 m += 1u << h;
463 }
464 node[i] -= (1u << j) + m;
465 node[j] += (1u << i) + m;
466 }
467
468 #if GECKO_WITH_NONRECURSIVE
469 // Evaluate all permutations generated by Heap's nonrecursive algorithm.
470 void
optimize(WeightedSum,uint)471 Subgraph::optimize(WeightedSum, uint)
472 {
473 WeightedSum c[GECKO_WINDOW_MAX + 1];
474 uint j[GECKO_WINDOW_MAX + 1];
475 j[n] = 1;
476 c[n] = 0;
477 uint i = n;
478 do
479 {
480 i--;
481 j[i] = i;
482 loop:
483 c[i] = f->sum(c[i + 1], cost(i));
484 }
485 while (i);
486 if (f->less(c[0], min))
487 {
488 min = c[0];
489 for (uint k = 0; k < n; k++)
490 {
491 best[k] = perm[k];
492 }
493 }
494 do
495 {
496 if (++i == n)
497 {
498 return;
499 }
500 swap(i & 1 ? i - j[i] : 0, i);
501 }
502 while (!j[i]--);
503 goto loop;
504 }
505 #else
506 // Apply branch-and-bound to permutations generated by Heap's algorithm.
507 void
optimize(WeightedSum c,uint i)508 Subgraph::optimize(WeightedSum c, uint i)
509 {
510 i--;
511 if (f->less(c, min))
512 {
513 if (i)
514 {
515 uint j = i;
516 do
517 {
518 optimize(f->sum(c, cost(i)), i);
519 swap(i & 1 ? i - j : 0, i);
520 }
521 while (j--);
522 }
523 else
524 {
525 f->accumulate(c, cost(0));
526 if (f->less(c, min))
527 {
528 min = c;
529 for (uint j = 0; j < n; j++)
530 {
531 best[j] = perm[j];
532 }
533 }
534 }
535 }
536 else if (i & 1)
537 do { swap(--i); }
538 while (i);
539 }
540 #endif
541
542 // Optimize layout of nodes {p, ..., p + n - 1}.
543 void
optimize(uint p)544 Subgraph::optimize(uint p)
545 {
546 // Initialize subgraph.
547 const Float q = g->node[g->perm[p]].pos - g->node[g->perm[p]].hlen;
548 min = WeightedSum(GECKO_FLOAT_MAX, 1);
549 for (Subnode::Index k = 0; k < n; k++)
550 {
551 best[k] = perm[k] = k;
552 Node::Index i = g->perm[p + k];
553 // Copy i's outgoing arcs. We distinguish between internal
554 // and external arcs to nodes within and outside the subgraph,
555 // respectively.
556 #if GECKO_WITH_ADJLIST
557 uint m = 0;
558 #else
559 adj[k] = 0;
560 #endif
561 std::vector<Arc::Index> external;
562 for (Arc::Index a = g->node_begin(i); a < g->node_end(i); a++)
563 {
564 Node::Index j = g->adj[a];
565 Subnode::Index l;
566 for (l = 0; l < n && g->perm[p + l] != j; l++);
567 if (l == n)
568 {
569 external.push_back(a);
570 }
571 else
572 {
573 // Copy internal arc to subgraph.
574 #if GECKO_WITH_ADJLIST
575 adj[k][m] = l;
576 weight[k][m] = g->weight[a];
577 m++;
578 #else
579 adj[k] += 1u << l;
580 weight[k][l] = g->weight[a];
581 #endif
582 }
583 }
584 #if GECKO_WITH_ADJLIST
585 adj[k][m] = k;
586 #endif
587 // Precompute external costs associated with all possible positions
588 // of this node. Since node lengths can be arbitrary, there are as
589 // many as 2^(n-1) possible positions, each corresponding to an
590 // (n-1)-bit string that specifies whether the remaining n-1 nodes
591 // succeed this node or not. Caching the
592 // n
593 // 2^(n-1) n = sum k C(n, k) = A001787
594 // k=1
595 // external costs is exponentially cheaper than recomputing the
596 // n-1 n
597 // n! sum 1/k! = sum k! C(n, k) = A007526
598 // k=0 k=1
599 // costs associated with all permutations.
600 node[k] = cache + (k << n);
601 for (uint m = 0; m < (1u << n); m++)
602 if (!(m & (1u << k)))
603 {
604 Subnode* s = cache + (k << n) + m;
605 s->pos = q + g->node[i].hlen;
606 for (Subnode::Index l = 0; l < n; l++)
607 if (l != k && !(m & (1u << l)))
608 {
609 s->pos += 2 * g->node[g->perm[p + l]].hlen;
610 }
611 s->cost = g->cost(external, s->pos);
612 }
613 else
614 {
615 m += (1u << k) - 1;
616 }
617 node[k] += (1u << n) - (2u << k);
618 }
619
620 // Find optimal permutation of the n nodes.
621 optimize(0, n);
622
623 // Apply permutation to original graph.
624 for (uint i = 0; i < n; i++)
625 {
626 g->swap(p + i, p + best[i]);
627 for (uint j = i + 1; j < n; j++)
628 if (best[j] == i)
629 {
630 best[j] = best[i];
631 }
632 }
633 }
634
635 // ----- graph.cpp --------------------------------------------------------------
636
637 using namespace std;
638 using namespace Gecko;
639
640 // Constructor.
641 void
init(uint nodes)642 Graph::init(uint nodes)
643 {
644 node.push_back(Node(-1, 0, 1, Node::null));
645 adj.push_back(Node::null);
646 weight.push_back(0);
647 bond.push_back(0);
648 while (nodes--)
649 {
650 insert_node();
651 }
652 }
653
654 // Insert node.
655 Node::Index
insert_node(Float length)656 Graph::insert_node(Float length)
657 {
658 Node::Index p = Node::Index(node.size());
659 perm.push_back(p);
660 node.push_back(Node(-1, length));
661 return p;
662 }
663
664 // Return nodes adjacent to i.
665 std::vector<Node::Index>
node_neighbors(Node::Index i) const666 Graph::node_neighbors(Node::Index i) const
667 {
668 std::vector<Node::Index> neighbor;
669 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
670 {
671 neighbor.push_back(adj[a]);
672 }
673 return neighbor;
674 }
675
676 // Insert directed edge (i, j).
677 Arc::Index
insert_arc(Node::Index i,Node::Index j,Float w,Float b)678 Graph::insert_arc(Node::Index i, Node::Index j, Float w, Float b)
679 {
680 if (!i || !j || i == j || !(last_node <= i && i <= nodes()))
681 {
682 return Arc::null;
683 }
684 last_node = i;
685 for (Node::Index k = i - 1; node[k].arc == Arc::null; k--)
686 {
687 node[k].arc = Arc::Index(adj.size());
688 }
689 adj.push_back(j);
690 weight.push_back(w);
691 bond.push_back(b);
692 node[i].arc = Arc::Index(adj.size());
693 return Arc::Index(adj.size() - 1);
694 }
695
696 // Remove arc a.
697 bool
remove_arc(Arc::Index a)698 Graph::remove_arc(Arc::Index a)
699 {
700 if (a == Arc::null)
701 {
702 return false;
703 }
704 Node::Index i = arc_source(a);
705 adj.erase(adj.begin() + a);
706 weight.erase(weight.begin() + a);
707 bond.erase(bond.begin() + a);
708 for (Node::Index k = i; k < node.size(); k++)
709 {
710 node[k].arc--;
711 }
712 return true;
713 }
714
715 // Remove directed edge (i, j).
716 bool
remove_arc(Node::Index i,Node::Index j)717 Graph::remove_arc(Node::Index i, Node::Index j)
718 {
719 return remove_arc(arc_index(i, j));
720 }
721
722 // Remove edge {i, j}.
723 bool
remove_edge(Node::Index i,Node::Index j)724 Graph::remove_edge(Node::Index i, Node::Index j)
725 {
726 bool success = remove_arc(i, j);
727 if (success)
728 {
729 success = remove_arc(j, i);
730 }
731 return success;
732 }
733
734 // Index of arc (i, j) or null if not a valid arc.
735 Arc::Index
arc_index(Node::Index i,Node::Index j) const736 Graph::arc_index(Node::Index i, Node::Index j) const
737 {
738 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
739 if (adj[a] == j)
740 {
741 return a;
742 }
743 return Arc::null;
744 }
745
746 // Return source node i in arc a = (i, j).
747 Node::Index
arc_source(Arc::Index a) const748 Graph::arc_source(Arc::Index a) const
749 {
750 Node::Index j = adj[a];
751 for (Arc::Index b = node_begin(j); b < node_end(j); b++)
752 {
753 Node::Index i = adj[b];
754 if (node_begin(i) <= a && a < node_end(i))
755 {
756 return i;
757 }
758 }
759 // should never get here
760 throw std::runtime_error("internal data structure corrupted");
761 }
762
763 // Return reverse arc (j, i) of arc a = (i, j).
764 Arc::Index
reverse_arc(Arc::Index a) const765 Graph::reverse_arc(Arc::Index a) const
766 {
767 Node::Index j = adj[a];
768 for (Arc::Index b = node_begin(j); b < node_end(j); b++)
769 {
770 Node::Index i = adj[b];
771 if (node_begin(i) <= a && a < node_end(i))
772 {
773 return b;
774 }
775 }
776 return Arc::null;
777 }
778
779 // Return first directed arc if one exists or null otherwise.
780 Arc::Index
directed() const781 Graph::directed() const
782 {
783 for (Node::Index i = 1; i < node.size(); i++)
784 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
785 {
786 Node::Index j = adj[a];
787 if (!arc_index(j, i))
788 {
789 return a;
790 }
791 }
792 return Arc::null;
793 }
794
795 // Add contribution of fine arc to coarse graph.
796 void
update(Node::Index i,Node::Index j,Float w,Float b)797 Graph::update(Node::Index i, Node::Index j, Float w, Float b)
798 {
799 Arc::Index a = arc_index(i, j);
800 if (a == Arc::null)
801 {
802 insert_arc(i, j, w, b);
803 }
804 else
805 {
806 weight[a] += w;
807 bond[a] += b;
808 }
809 }
810
811 // Transfer contribution of fine arc a to coarse node p.
812 void
transfer(Graph * g,const vector<Float> & part,Node::Index p,Arc::Index a,Float f) const813 Graph::transfer(Graph* g, const vector<Float>& part, Node::Index p,
814 Arc::Index a, Float f) const
815 {
816 Float w = f * weight[a];
817 Float m = f * bond[a];
818 Node::Index j = arc_target(a);
819 Node::Index q = node[j].parent;
820 if (q == Node::null)
821 {
822 for (Arc::Index b = node_begin(j); b < node_end(j); b++)
823 if (part[b] > 0)
824 {
825 q = node[adj[b]].parent;
826 if (q != p)
827 {
828 g->update(p, q, w * part[b], m * part[b]);
829 }
830 }
831 }
832 else
833 {
834 g->update(p, q, w, m);
835 }
836 }
837
838 // Compute cost of a subset of arcs incident on node placed at pos.
839 WeightedSum
cost(const vector<Arc::Index> & subset,Float pos) const840 Graph::cost(const vector<Arc::Index>& subset, Float pos) const
841 {
842 WeightedSum c;
843 for (Arc::ConstPtr ap = subset.begin(); ap != subset.end(); ap++)
844 {
845 Arc::Index a = *ap;
846 Node::Index j = arc_target(a);
847 Float l = fabs(node[j].pos - pos);
848 Float w = weight[a];
849 functional->accumulate(c, WeightedValue(l, w));
850 }
851 return c;
852 }
853
854 // Compute cost of graph layout.
855 Float
cost() const856 Graph::cost() const
857 {
858 if (edges())
859 {
860 WeightedSum c;
861 Node::Index i = 1;
862 for (Arc::Index a = 1; a < adj.size(); a++)
863 {
864 while (node_end(i) <= a)
865 {
866 i++;
867 }
868 Node::Index j = arc_target(a);
869 Float l = length(i, j);
870 Float w = weight[a];
871 functional->accumulate(c, WeightedValue(l, w));
872 }
873 return functional->mean(c);
874 }
875 else
876 {
877 return Float(0);
878 }
879 }
880
881 // Swap the two nodes in positions k and l, k <= l.
882 void
swap(uint k,uint l)883 Graph::swap(uint k, uint l)
884 {
885 Node::Index i = perm[k];
886 perm[k] = perm[l];
887 perm[l] = i;
888 Float p = node[i].pos - node[i].hlen;
889 do
890 {
891 i = perm[k];
892 p += node[i].hlen;
893 node[i].pos = p;
894 p += node[i].hlen;
895 }
896 while (k++ != l);
897 }
898
899 // Optimize continuous position of a single node.
900 Float
optimal(Node::Index i) const901 Graph::optimal(Node::Index i) const
902 {
903 vector<WeightedValue> v;
904 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
905 {
906 Node::Index j = adj[a];
907 if (placed(j))
908 {
909 v.push_back(WeightedValue(node[j].pos, weight[a]));
910 }
911 }
912 return v.empty() ? -1 : functional->optimum(v);
913 }
914
915 // Compute coarse graph with roughly half the number of nodes.
916 Graph*
coarsen()917 Graph::coarsen()
918 {
919 progress->beginphase(this, string("coarse"));
920 Graph* g = new Graph(0, level - 1);
921 g->functional = functional;
922 g->progress = progress;
923
924 // Compute importance of nodes in fine graph.
925 DynamicHeap<Node::Index, Float> heap;
926 for (Node::Index i = 1; i < node.size(); i++)
927 {
928 node[i].parent = Node::null;
929 Float w = 0;
930 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
931 {
932 w += bond[a];
933 }
934 heap.insert(i, w);
935 }
936
937 // Select set of important nodes from fine graph that will remain in
938 // coarse graph.
939 vector<Node::Index> child(1, Node::null);
940 while (!heap.empty())
941 {
942 Node::Index i;
943 Float w = 0;
944 heap.extract(i, w);
945 if (w < 0)
946 {
947 break;
948 }
949 child.push_back(i);
950 node[i].parent = g->insert_node(2 * node[i].hlen);
951
952 // Reduce importance of neighbors.
953 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
954 {
955 Node::Index j = adj[a];
956 if (heap.find(j, w))
957 {
958 heap.update(j, w - 2 * bond[a]);
959 }
960 }
961 }
962
963 // Assign parts of remaining nodes to aggregates.
964 vector<Float> part = bond;
965 for (Node::Index i = 1; i < node.size(); i++)
966 if (!persistent(i))
967 {
968 // Find all connections to coarse nodes.
969 Float w = 0;
970 Float max = 0;
971 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
972 {
973 Node::Index j = adj[a];
974 if (persistent(j))
975 {
976 w += part[a];
977 if (max < part[a])
978 {
979 max = part[a];
980 }
981 }
982 else
983 {
984 part[a] = -1;
985 }
986 }
987 max /= GECKO_PART_FRAC;
988
989 // Weed out insignificant connections.
990 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
991 if (0 < part[a] && part[a] < max)
992 {
993 w -= part[a];
994 part[a] = -1;
995 }
996
997 // Compute node fractions (interpolation matrix) and assign
998 // partial nodes to aggregates.
999 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
1000 if (part[a] > 0)
1001 {
1002 part[a] /= w;
1003 Node::Index p = node[adj[a]].parent;
1004 g->node[p].hlen += part[a] * node[i].hlen;
1005 }
1006 }
1007
1008 // Transfer arcs to coarse graph.
1009 for (Node::Index p = 1; p < g->node.size(); p++)
1010 {
1011 Node::Index i = child[p];
1012 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
1013 {
1014 transfer(g, part, p, a);
1015 Node::Index j = adj[a];
1016 if (!persistent(j))
1017 {
1018 Arc::Index b = arc_index(j, i);
1019 if (part[b] > 0)
1020 for (Arc::Index c = node_begin(j); c < node_end(j); c++)
1021 {
1022 Node::Index k = adj[c];
1023 if (k != i)
1024 {
1025 transfer(g, part, p, c, part[b]);
1026 }
1027 }
1028 }
1029 }
1030 }
1031
1032 #if DEBUG
1033 if (g->directed())
1034 {
1035 throw runtime_error("directed edge found");
1036 }
1037 #endif
1038
1039 // Free memory.
1040 vector<Float> t = bond;
1041 bond.swap(t);
1042
1043 progress->endphase(this, false);
1044
1045 return g;
1046 }
1047
1048 // Order nodes according to coarsened graph layout.
1049 void
refine(const Graph * graph)1050 Graph::refine(const Graph* graph)
1051 {
1052 progress->beginphase(this, string("refine"));
1053
1054 // Place persistent nodes.
1055 DynamicHeap<Node::Index, Float> heap;
1056 for (Node::Index i = 1; i < node.size(); i++)
1057 if (persistent(i))
1058 {
1059 Node::Index p = node[i].parent;
1060 node[i].pos = graph->node[p].pos;
1061 }
1062 else
1063 {
1064 node[i].pos = -1;
1065 Float w = 0;
1066 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
1067 {
1068 Node::Index j = adj[a];
1069 if (persistent(j))
1070 {
1071 w += weight[a];
1072 }
1073 }
1074 heap.insert(i, w);
1075 }
1076
1077 // Place remaining nodes in order of decreasing connectivity with
1078 // already placed nodes.
1079 while (!heap.empty())
1080 {
1081 Node::Index i = 0;
1082 heap.extract(i);
1083 node[i].pos = optimal(i);
1084 for (Arc::Index a = node_begin(i); a < node_end(i); a++)
1085 {
1086 Node::Index j = adj[a];
1087 Float w;
1088 if (heap.find(j, w))
1089 {
1090 heap.update(j, w + weight[a]);
1091 }
1092 }
1093 }
1094
1095 place(true);
1096 progress->endphase(this, true);
1097 }
1098
1099 // Perform m sweeps of compatible or Gauss-Seidel relaxation.
1100 void
relax(bool compatible,uint m)1101 Graph::relax(bool compatible, uint m)
1102 {
1103 progress->beginphase(this, compatible ? string("crelax") : string("frelax"));
1104 while (m--)
1105 for (uint k = 0; k < perm.size() && !progress->quit(); k++)
1106 {
1107 Node::Index i = perm[k];
1108 if (!compatible || !persistent(i))
1109 {
1110 node[i].pos = optimal(i);
1111 }
1112 }
1113 place(true);
1114 progress->endphase(this, true);
1115 }
1116
1117 // Optimize successive n-node subgraphs.
1118 void
optimize(uint n)1119 Graph::optimize(uint n)
1120 {
1121 if (n > perm.size())
1122 {
1123 n = uint(perm.size());
1124 }
1125 ostringstream count;
1126 count << setw(2) << n;
1127 progress->beginphase(this, string("perm") + count.str());
1128 Subgraph* subgraph = new Subgraph(this, n);
1129 for (uint k = 0; k <= perm.size() - n && !progress->quit(); k++)
1130 {
1131 subgraph->optimize(k);
1132 }
1133 delete subgraph;
1134 progress->endphase(this, true);
1135 }
1136
1137 // Place all nodes according to their positions.
1138 void
place(bool sort)1139 Graph::place(bool sort)
1140 {
1141 place(sort, 0, uint(perm.size()));
1142 }
1143
1144 // Place nodes {k, ..., k + n - 1} according to their positions.
1145 void
place(bool sort,uint k,uint n)1146 Graph::place(bool sort, uint k, uint n)
1147 {
1148 // Place nodes.
1149 if (sort)
1150 {
1151 stable_sort(perm.begin() + k, perm.begin() + k + n,
1152 Node::Comparator(node.begin()));
1153 }
1154
1155 // Assign node positions according to permutation.
1156 for (Float p = k ? node[perm[k - 1]].pos + node[perm[k - 1]].hlen : 0; n--;
1157 k++)
1158 {
1159 Node::Index i = perm[k];
1160 p += node[i].hlen;
1161 node[i].pos = p;
1162 p += node[i].hlen;
1163 }
1164 }
1165
1166 // Perform one V-cycle.
1167 void
vcycle(uint n,uint work)1168 Graph::vcycle(uint n, uint work)
1169 {
1170 if (n < nodes() && nodes() < edges() && level && !progress->quit())
1171 {
1172 Graph* graph = coarsen();
1173 graph->vcycle(n, work + edges());
1174 refine(graph);
1175 delete graph;
1176 }
1177 else
1178 {
1179 place();
1180 }
1181 if (edges())
1182 {
1183 relax(true, GECKO_CR_SWEEPS);
1184 relax(false, GECKO_GS_SWEEPS);
1185 for (uint w = edges(); w * (n + 1) < work; w *= ++n);
1186 n = std::min(n, uint(GECKO_WINDOW_MAX));
1187 if (n)
1188 {
1189 optimize(n);
1190 }
1191 }
1192 }
1193
1194 // Custom random-number generator for reproducibility.
1195 // LCG from doi:10.1090/S0025-5718-99-00996-5.
1196 uint
random(uint seed)1197 Graph::random(uint seed)
1198 {
1199 static uint state = 1;
1200 state = (seed ? seed : 0x1ed0675 * state + 0xa14f);
1201 return state;
1202 }
1203
1204 // Generate a random permutation of the nodes.
1205 void
shuffle(uint seed)1206 Graph::shuffle(uint seed)
1207 {
1208 random(seed);
1209 for (uint k = 0; k < perm.size(); k++)
1210 {
1211 uint r = random() >> 8;
1212 uint l = k + r % (uint(perm.size()) - k);
1213 std::swap(perm[k], perm[l]);
1214 }
1215 place();
1216 }
1217
1218 // Recompute bonds for k'th V-cycle.
1219 void
reweight(uint k)1220 Graph::reweight(uint k)
1221 {
1222 bond.resize(weight.size());
1223 for (Arc::Index a = 1; a < adj.size(); a++)
1224 {
1225 bond[a] = functional->bond(weight[a], length(a), k);
1226 }
1227 }
1228
1229 // Linearly order graph.
1230 void
order(Functional * functional,uint iterations,uint window,uint period,uint seed,Progress * progress)1231 Graph::order(Functional* functional, uint iterations, uint window, uint period,
1232 uint seed, Progress* progress)
1233 {
1234 // Initialize graph.
1235 this->functional = functional;
1236 progress = this->progress = progress ? progress : new Progress;
1237 for (level = 0; (1u << level) < nodes(); level++);
1238 place();
1239 Float mincost = cost();
1240 vector<Node::Index> minperm = perm;
1241 if (seed)
1242 {
1243 shuffle(seed);
1244 }
1245
1246 progress->beginorder(this, mincost);
1247 if (edges())
1248 {
1249 // Perform specified number of V-cycles.
1250 for (uint k = 1; k <= iterations && !progress->quit(); k++)
1251 {
1252 progress->beginiter(this, k, iterations, window);
1253 reweight(k);
1254 vcycle(window);
1255 Float c = cost();
1256 if (c < mincost)
1257 {
1258 mincost = c;
1259 minperm = perm;
1260 }
1261 progress->enditer(this, mincost, c);
1262 if (period && !(k % period))
1263 {
1264 window++;
1265 }
1266 }
1267 perm = minperm;
1268 place();
1269 }
1270 progress->endorder(this, mincost);
1271
1272 if (!progress)
1273 {
1274 delete this->progress;
1275 this->progress = 0;
1276 }
1277 }
1278