1278 lines
30 KiB
C++
1278 lines
30 KiB
C++
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
|
|
#include "gecko.hpp"
|
|
|
|
// This file collects the sources of the Gecko library as a single module.
|
|
// The original library can be found at https://github.com/LLNL/gecko
|
|
// Used here with permission.
|
|
|
|
// ------------------------------------------------------------------------------
|
|
|
|
// BSD 3-Clause License
|
|
//
|
|
// Copyright (c) 2019-2020, Lawrence Livermore National Security, LLC
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice, this
|
|
// list of conditions and the following disclaimer.
|
|
//
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
//
|
|
// * Neither the name of the copyright holder nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
// ------------------------------------------------------------------------------
|
|
|
|
// Copyright (c) 2019-2020, Lawrence Livermore National Security, LLC and other
|
|
// gecko project contributors. See the above license for details.
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
// LLNL-CODE-800597
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <cstdlib>
|
|
#include <cstddef>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <map>
|
|
|
|
// ----- options.h --------------------------------------------------------------
|
|
|
|
// ratio of max to min weight for aggregation
|
|
#ifndef GECKO_PART_FRAC
|
|
#define GECKO_PART_FRAC 4
|
|
#endif
|
|
|
|
// number of compatible relaxation sweeps
|
|
#ifndef GECKO_CR_SWEEPS
|
|
#define GECKO_CR_SWEEPS 1
|
|
#endif
|
|
|
|
// number of Gauss-Seidel relaxation sweeps
|
|
#ifndef GECKO_GS_SWEEPS
|
|
#define GECKO_GS_SWEEPS 1
|
|
#endif
|
|
|
|
// max number of nodes in subgraph
|
|
#ifndef GECKO_WINDOW_MAX
|
|
#define GECKO_WINDOW_MAX 16
|
|
#endif
|
|
|
|
// use adjacency list (1) or adjacency matrix (0)
|
|
#ifndef GECKO_WITH_ADJLIST
|
|
#define GECKO_WITH_ADJLIST 0
|
|
#endif
|
|
|
|
// use nonrecursive permutation algorithm
|
|
#ifndef GECKO_WITH_NONRECURSIVE
|
|
#define GECKO_WITH_NONRECURSIVE 0
|
|
#endif
|
|
|
|
// use double-precision computations
|
|
#ifndef GECKO_WITH_DOUBLE_PRECISION
|
|
#define GECKO_WITH_DOUBLE_PRECISION 0
|
|
#endif
|
|
|
|
// ----- heap.h -----------------------------------------------------------------
|
|
|
|
template <
|
|
typename T, // data type
|
|
typename P, // priority type
|
|
class C = std::less<P>, // comparator for priorities
|
|
class M = std::map<T, unsigned int> // maps type T to unsigned integer
|
|
>
|
|
class DynamicHeap
|
|
{
|
|
public:
|
|
DynamicHeap(size_t count = 0);
|
|
~DynamicHeap() {}
|
|
void insert(T data, P priority);
|
|
void update(T data, P priority);
|
|
bool top(T& data);
|
|
bool top(T& data, P& priority);
|
|
bool pop();
|
|
bool extract(T& data);
|
|
bool extract(T& data, P& priority);
|
|
bool erase(T data);
|
|
bool find(T data) const;
|
|
bool find(T data, P& priority) const;
|
|
bool empty() const { return heap.empty(); }
|
|
size_t size() const { return heap.size(); }
|
|
private:
|
|
struct HeapEntry
|
|
{
|
|
HeapEntry(P p, T d) : priority(p), data(d) {}
|
|
P priority;
|
|
T data;
|
|
};
|
|
std::vector<HeapEntry> heap;
|
|
M index;
|
|
C lower;
|
|
void ascend(unsigned int i);
|
|
void descend(unsigned int i);
|
|
void swap(unsigned int i, unsigned int j);
|
|
bool ordered(unsigned int i, unsigned int j) const
|
|
{
|
|
return !lower(heap[i].priority, heap[j].priority);
|
|
}
|
|
unsigned int parent(unsigned int i) const { return (i - 1) / 2; }
|
|
unsigned int left(unsigned int i) const { return 2 * i + 1; }
|
|
unsigned int right(unsigned int i) const { return 2 * i + 2; }
|
|
};
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
DynamicHeap<T, P, C, M>::DynamicHeap(size_t count)
|
|
{
|
|
heap.reserve(count);
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
void
|
|
DynamicHeap<T, P, C, M>::insert(T data, P priority)
|
|
{
|
|
if (index.find(data) != index.end())
|
|
{
|
|
update(data, priority);
|
|
}
|
|
else
|
|
{
|
|
unsigned int i = (unsigned int)heap.size();
|
|
heap.push_back(HeapEntry(priority, data));
|
|
ascend(i);
|
|
}
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
void
|
|
DynamicHeap<T, P, C, M>::update(T data, P priority)
|
|
{
|
|
unsigned int i = index[data];
|
|
heap[i].priority = priority;
|
|
ascend(i);
|
|
descend(i);
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::top(T& data)
|
|
{
|
|
if (!heap.empty())
|
|
{
|
|
data = heap[0].data;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::top(T& data, P& priority)
|
|
{
|
|
if (!heap.empty())
|
|
{
|
|
data = heap[0].data;
|
|
priority = heap[0].priority;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::pop()
|
|
{
|
|
if (!heap.empty())
|
|
{
|
|
T data = heap[0].data;
|
|
swap(0, (unsigned int)heap.size() - 1);
|
|
index.erase(data);
|
|
heap.pop_back();
|
|
if (!heap.empty())
|
|
{
|
|
descend(0);
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::extract(T& data)
|
|
{
|
|
if (!heap.empty())
|
|
{
|
|
data = heap[0].data;
|
|
return pop();
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::extract(T& data, P& priority)
|
|
{
|
|
if (!heap.empty())
|
|
{
|
|
data = heap[0].data;
|
|
priority = heap[0].priority;
|
|
return pop();
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::erase(T data)
|
|
{
|
|
if (index.find(data) == index.end())
|
|
{
|
|
return false;
|
|
}
|
|
unsigned int i = index[data];
|
|
swap(i, heap.size() - 1);
|
|
index.erase(data);
|
|
heap.pop_back();
|
|
if (i < heap.size())
|
|
{
|
|
ascend(i);
|
|
descend(i);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::find(T data) const
|
|
{
|
|
return index.find(data) != index.end();
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
bool
|
|
DynamicHeap<T, P, C, M>::find(T data, P& priority) const
|
|
{
|
|
typename M::const_iterator p;
|
|
if ((p = index.find(data)) == index.end())
|
|
{
|
|
return false;
|
|
}
|
|
unsigned int i = p->second;
|
|
priority = heap[i].priority;
|
|
return true;
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
void
|
|
DynamicHeap<T, P, C, M>::ascend(unsigned int i)
|
|
{
|
|
for (unsigned int j; i && !ordered(j = parent(i), i); i = j)
|
|
{
|
|
swap(i, j);
|
|
}
|
|
index[heap[i].data] = i;
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
void
|
|
DynamicHeap<T, P, C, M>::descend(unsigned int i)
|
|
{
|
|
for (unsigned int j, k;
|
|
(j = ((k = left(i)) < heap.size() && !ordered(i, k) ? k : i),
|
|
j = ((k = right(i)) < heap.size() && !ordered(j, k) ? k : j)) != i;
|
|
i = j)
|
|
{
|
|
swap(i, j);
|
|
}
|
|
index[heap[i].data] = i;
|
|
}
|
|
|
|
template < typename T, typename P, class C, class M >
|
|
void
|
|
DynamicHeap<T, P, C, M>::swap(unsigned int i, unsigned int j)
|
|
{
|
|
std::swap(heap[i], heap[j]);
|
|
index[heap[i].data] = i;
|
|
}
|
|
|
|
// ----- subgraph.h -------------------------------------------------------------
|
|
|
|
namespace Gecko
|
|
{
|
|
|
|
// Node in a subgraph.
|
|
class Subnode
|
|
{
|
|
public:
|
|
typedef unsigned char Index;
|
|
Float pos; // node position
|
|
WeightedSum cost; // external cost at this position
|
|
};
|
|
|
|
class Subgraph
|
|
{
|
|
public:
|
|
Subgraph(Graph* g, uint n);
|
|
~Subgraph() { delete[] cache; }
|
|
void optimize(uint k);
|
|
|
|
private:
|
|
Graph* const g; // full graph
|
|
const uint n; // number of subgraph nodes
|
|
Functional* const f; // ordering functional
|
|
WeightedSum min; // minimum cost so far
|
|
Subnode::Index best[GECKO_WINDOW_MAX]; // best permutation so far
|
|
Subnode::Index perm[GECKO_WINDOW_MAX]; // current permutation
|
|
const Subnode* node[GECKO_WINDOW_MAX]; // pointers to precomputed nodes
|
|
Subnode* cache; // precomputed node positions and costs
|
|
#if GECKO_WITH_ADJLIST
|
|
Subnode::Index
|
|
adj[GECKO_WINDOW_MAX][GECKO_WINDOW_MAX]; // internal adjacency list
|
|
#else
|
|
uint adj[GECKO_WINDOW_MAX]; // internal adjacency matrix
|
|
#endif
|
|
Float weight[GECKO_WINDOW_MAX][GECKO_WINDOW_MAX]; // internal arc weights
|
|
WeightedSum cost(uint k) const;
|
|
void swap(uint k);
|
|
void swap(uint k, uint l);
|
|
void optimize(WeightedSum c, uint i);
|
|
};
|
|
|
|
}
|
|
|
|
// ----- subgraph.cpp -----------------------------------------------------------
|
|
|
|
using namespace Gecko;
|
|
|
|
// Constructor.
|
|
Subgraph::Subgraph(Graph* g, uint n) : g(g), n(n), f(g->functional)
|
|
{
|
|
if (n > GECKO_WINDOW_MAX)
|
|
{
|
|
throw std::out_of_range("optimization window too large");
|
|
}
|
|
cache = new Subnode[n << n];
|
|
}
|
|
|
|
// Cost of k'th node's edges to external nodes and nodes at {k+1, ..., n-1}.
|
|
WeightedSum
|
|
Subgraph::cost(uint k) const
|
|
{
|
|
Subnode::Index i = perm[k];
|
|
WeightedSum c = node[i]->cost;
|
|
Float p = node[i]->pos;
|
|
#if GECKO_WITH_ADJLIST
|
|
for (k = 0; adj[i][k] != i; k++)
|
|
{
|
|
Subnode::Index j = adj[i][k];
|
|
Float l = node[j]->pos - p;
|
|
if (l > 0)
|
|
{
|
|
Float w = weight[i][k];
|
|
f->accumulate(c, WeightedValue(l, w));
|
|
}
|
|
}
|
|
#else
|
|
uint m = adj[i];
|
|
while (++k < n)
|
|
{
|
|
Subnode::Index j = perm[k];
|
|
if (m & (1u << j))
|
|
{
|
|
Float l = node[j]->pos - p;
|
|
Float w = weight[i][j];
|
|
f->accumulate(c, WeightedValue(l, w));
|
|
}
|
|
}
|
|
#endif
|
|
return c;
|
|
}
|
|
|
|
// Swap the two nodes in positions k and k + 1.
|
|
void
|
|
Subgraph::swap(uint k)
|
|
{
|
|
uint l = k + 1;
|
|
Subnode::Index i = perm[k];
|
|
Subnode::Index j = perm[l];
|
|
perm[k] = j;
|
|
perm[l] = i;
|
|
node[i] -= ptrdiff_t(1) << j;
|
|
node[j] += ptrdiff_t(1) << i;
|
|
}
|
|
|
|
// Swap the two nodes in positions k and l, k <= l.
|
|
void
|
|
Subgraph::swap(uint k, uint l)
|
|
{
|
|
Subnode::Index i = perm[k];
|
|
Subnode::Index j = perm[l];
|
|
perm[k] = j;
|
|
perm[l] = i;
|
|
// Update node positions.
|
|
uint m = 0;
|
|
while (++k < l)
|
|
{
|
|
Subnode::Index h = perm[k];
|
|
node[h] += ptrdiff_t(1) << i;
|
|
node[h] -= ptrdiff_t(1) << j;
|
|
m += 1u << h;
|
|
}
|
|
node[i] -= (1u << j) + m;
|
|
node[j] += (1u << i) + m;
|
|
}
|
|
|
|
#if GECKO_WITH_NONRECURSIVE
|
|
// Evaluate all permutations generated by Heap's nonrecursive algorithm.
|
|
void
|
|
Subgraph::optimize(WeightedSum, uint)
|
|
{
|
|
WeightedSum c[GECKO_WINDOW_MAX + 1];
|
|
uint j[GECKO_WINDOW_MAX + 1];
|
|
j[n] = 1;
|
|
c[n] = 0;
|
|
uint i = n;
|
|
do
|
|
{
|
|
i--;
|
|
j[i] = i;
|
|
loop:
|
|
c[i] = f->sum(c[i + 1], cost(i));
|
|
}
|
|
while (i);
|
|
if (f->less(c[0], min))
|
|
{
|
|
min = c[0];
|
|
for (uint k = 0; k < n; k++)
|
|
{
|
|
best[k] = perm[k];
|
|
}
|
|
}
|
|
do
|
|
{
|
|
if (++i == n)
|
|
{
|
|
return;
|
|
}
|
|
swap(i & 1 ? i - j[i] : 0, i);
|
|
}
|
|
while (!j[i]--);
|
|
goto loop;
|
|
}
|
|
#else
|
|
// Apply branch-and-bound to permutations generated by Heap's algorithm.
|
|
void
|
|
Subgraph::optimize(WeightedSum c, uint i)
|
|
{
|
|
i--;
|
|
if (f->less(c, min))
|
|
{
|
|
if (i)
|
|
{
|
|
uint j = i;
|
|
do
|
|
{
|
|
optimize(f->sum(c, cost(i)), i);
|
|
swap(i & 1 ? i - j : 0, i);
|
|
}
|
|
while (j--);
|
|
}
|
|
else
|
|
{
|
|
f->accumulate(c, cost(0));
|
|
if (f->less(c, min))
|
|
{
|
|
min = c;
|
|
for (uint j = 0; j < n; j++)
|
|
{
|
|
best[j] = perm[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (i & 1)
|
|
do { swap(--i); }
|
|
while (i);
|
|
}
|
|
#endif
|
|
|
|
// Optimize layout of nodes {p, ..., p + n - 1}.
|
|
void
|
|
Subgraph::optimize(uint p)
|
|
{
|
|
// Initialize subgraph.
|
|
const Float q = g->node[g->perm[p]].pos - g->node[g->perm[p]].hlen;
|
|
min = WeightedSum(GECKO_FLOAT_MAX, 1);
|
|
for (Subnode::Index k = 0; k < n; k++)
|
|
{
|
|
best[k] = perm[k] = k;
|
|
Node::Index i = g->perm[p + k];
|
|
// Copy i's outgoing arcs. We distinguish between internal
|
|
// and external arcs to nodes within and outside the subgraph,
|
|
// respectively.
|
|
#if GECKO_WITH_ADJLIST
|
|
uint m = 0;
|
|
#else
|
|
adj[k] = 0;
|
|
#endif
|
|
std::vector<Arc::Index> external;
|
|
for (Arc::Index a = g->node_begin(i); a < g->node_end(i); a++)
|
|
{
|
|
Node::Index j = g->adj[a];
|
|
Subnode::Index l;
|
|
for (l = 0; l < n && g->perm[p + l] != j; l++);
|
|
if (l == n)
|
|
{
|
|
external.push_back(a);
|
|
}
|
|
else
|
|
{
|
|
// Copy internal arc to subgraph.
|
|
#if GECKO_WITH_ADJLIST
|
|
adj[k][m] = l;
|
|
weight[k][m] = g->weight[a];
|
|
m++;
|
|
#else
|
|
adj[k] += 1u << l;
|
|
weight[k][l] = g->weight[a];
|
|
#endif
|
|
}
|
|
}
|
|
#if GECKO_WITH_ADJLIST
|
|
adj[k][m] = k;
|
|
#endif
|
|
// Precompute external costs associated with all possible positions
|
|
// of this node. Since node lengths can be arbitrary, there are as
|
|
// many as 2^(n-1) possible positions, each corresponding to an
|
|
// (n-1)-bit string that specifies whether the remaining n-1 nodes
|
|
// succeed this node or not. Caching the
|
|
// n
|
|
// 2^(n-1) n = sum k C(n, k) = A001787
|
|
// k=1
|
|
// external costs is exponentially cheaper than recomputing the
|
|
// n-1 n
|
|
// n! sum 1/k! = sum k! C(n, k) = A007526
|
|
// k=0 k=1
|
|
// costs associated with all permutations.
|
|
node[k] = cache + (k << n);
|
|
for (uint m = 0; m < (1u << n); m++)
|
|
if (!(m & (1u << k)))
|
|
{
|
|
Subnode* s = cache + (k << n) + m;
|
|
s->pos = q + g->node[i].hlen;
|
|
for (Subnode::Index l = 0; l < n; l++)
|
|
if (l != k && !(m & (1u << l)))
|
|
{
|
|
s->pos += 2 * g->node[g->perm[p + l]].hlen;
|
|
}
|
|
s->cost = g->cost(external, s->pos);
|
|
}
|
|
else
|
|
{
|
|
m += (1u << k) - 1;
|
|
}
|
|
node[k] += (1u << n) - (2u << k);
|
|
}
|
|
|
|
// Find optimal permutation of the n nodes.
|
|
optimize(0, n);
|
|
|
|
// Apply permutation to original graph.
|
|
for (uint i = 0; i < n; i++)
|
|
{
|
|
g->swap(p + i, p + best[i]);
|
|
for (uint j = i + 1; j < n; j++)
|
|
if (best[j] == i)
|
|
{
|
|
best[j] = best[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----- graph.cpp --------------------------------------------------------------
|
|
|
|
using namespace std;
|
|
using namespace Gecko;
|
|
|
|
// Constructor.
|
|
void
|
|
Graph::init(uint nodes)
|
|
{
|
|
node.push_back(Node(-1, 0, 1, Node::null));
|
|
adj.push_back(Node::null);
|
|
weight.push_back(0);
|
|
bond.push_back(0);
|
|
while (nodes--)
|
|
{
|
|
insert_node();
|
|
}
|
|
}
|
|
|
|
// Insert node.
|
|
Node::Index
|
|
Graph::insert_node(Float length)
|
|
{
|
|
Node::Index p = Node::Index(node.size());
|
|
perm.push_back(p);
|
|
node.push_back(Node(-1, length));
|
|
return p;
|
|
}
|
|
|
|
// Return nodes adjacent to i.
|
|
std::vector<Node::Index>
|
|
Graph::node_neighbors(Node::Index i) const
|
|
{
|
|
std::vector<Node::Index> neighbor;
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
neighbor.push_back(adj[a]);
|
|
}
|
|
return neighbor;
|
|
}
|
|
|
|
// Insert directed edge (i, j).
|
|
Arc::Index
|
|
Graph::insert_arc(Node::Index i, Node::Index j, Float w, Float b)
|
|
{
|
|
if (!i || !j || i == j || !(last_node <= i && i <= nodes()))
|
|
{
|
|
return Arc::null;
|
|
}
|
|
last_node = i;
|
|
for (Node::Index k = i - 1; node[k].arc == Arc::null; k--)
|
|
{
|
|
node[k].arc = Arc::Index(adj.size());
|
|
}
|
|
adj.push_back(j);
|
|
weight.push_back(w);
|
|
bond.push_back(b);
|
|
node[i].arc = Arc::Index(adj.size());
|
|
return Arc::Index(adj.size() - 1);
|
|
}
|
|
|
|
// Remove arc a.
|
|
bool
|
|
Graph::remove_arc(Arc::Index a)
|
|
{
|
|
if (a == Arc::null)
|
|
{
|
|
return false;
|
|
}
|
|
Node::Index i = arc_source(a);
|
|
adj.erase(adj.begin() + a);
|
|
weight.erase(weight.begin() + a);
|
|
bond.erase(bond.begin() + a);
|
|
for (Node::Index k = i; k < node.size(); k++)
|
|
{
|
|
node[k].arc--;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Remove directed edge (i, j).
|
|
bool
|
|
Graph::remove_arc(Node::Index i, Node::Index j)
|
|
{
|
|
return remove_arc(arc_index(i, j));
|
|
}
|
|
|
|
// Remove edge {i, j}.
|
|
bool
|
|
Graph::remove_edge(Node::Index i, Node::Index j)
|
|
{
|
|
bool success = remove_arc(i, j);
|
|
if (success)
|
|
{
|
|
success = remove_arc(j, i);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
// Index of arc (i, j) or null if not a valid arc.
|
|
Arc::Index
|
|
Graph::arc_index(Node::Index i, Node::Index j) const
|
|
{
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
if (adj[a] == j)
|
|
{
|
|
return a;
|
|
}
|
|
return Arc::null;
|
|
}
|
|
|
|
// Return source node i in arc a = (i, j).
|
|
Node::Index
|
|
Graph::arc_source(Arc::Index a) const
|
|
{
|
|
Node::Index j = adj[a];
|
|
for (Arc::Index b = node_begin(j); b < node_end(j); b++)
|
|
{
|
|
Node::Index i = adj[b];
|
|
if (node_begin(i) <= a && a < node_end(i))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
// should never get here
|
|
throw std::runtime_error("internal data structure corrupted");
|
|
}
|
|
|
|
// Return reverse arc (j, i) of arc a = (i, j).
|
|
Arc::Index
|
|
Graph::reverse_arc(Arc::Index a) const
|
|
{
|
|
Node::Index j = adj[a];
|
|
for (Arc::Index b = node_begin(j); b < node_end(j); b++)
|
|
{
|
|
Node::Index i = adj[b];
|
|
if (node_begin(i) <= a && a < node_end(i))
|
|
{
|
|
return b;
|
|
}
|
|
}
|
|
return Arc::null;
|
|
}
|
|
|
|
// Return first directed arc if one exists or null otherwise.
|
|
Arc::Index
|
|
Graph::directed() const
|
|
{
|
|
for (Node::Index i = 1; i < node.size(); i++)
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
Node::Index j = adj[a];
|
|
if (!arc_index(j, i))
|
|
{
|
|
return a;
|
|
}
|
|
}
|
|
return Arc::null;
|
|
}
|
|
|
|
// Add contribution of fine arc to coarse graph.
|
|
void
|
|
Graph::update(Node::Index i, Node::Index j, Float w, Float b)
|
|
{
|
|
Arc::Index a = arc_index(i, j);
|
|
if (a == Arc::null)
|
|
{
|
|
insert_arc(i, j, w, b);
|
|
}
|
|
else
|
|
{
|
|
weight[a] += w;
|
|
bond[a] += b;
|
|
}
|
|
}
|
|
|
|
// Transfer contribution of fine arc a to coarse node p.
|
|
void
|
|
Graph::transfer(Graph* g, const vector<Float>& part, Node::Index p,
|
|
Arc::Index a, Float f) const
|
|
{
|
|
Float w = f * weight[a];
|
|
Float m = f * bond[a];
|
|
Node::Index j = arc_target(a);
|
|
Node::Index q = node[j].parent;
|
|
if (q == Node::null)
|
|
{
|
|
for (Arc::Index b = node_begin(j); b < node_end(j); b++)
|
|
if (part[b] > 0)
|
|
{
|
|
q = node[adj[b]].parent;
|
|
if (q != p)
|
|
{
|
|
g->update(p, q, w * part[b], m * part[b]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g->update(p, q, w, m);
|
|
}
|
|
}
|
|
|
|
// Compute cost of a subset of arcs incident on node placed at pos.
|
|
WeightedSum
|
|
Graph::cost(const vector<Arc::Index>& subset, Float pos) const
|
|
{
|
|
WeightedSum c;
|
|
for (Arc::ConstPtr ap = subset.begin(); ap != subset.end(); ap++)
|
|
{
|
|
Arc::Index a = *ap;
|
|
Node::Index j = arc_target(a);
|
|
Float l = fabs(node[j].pos - pos);
|
|
Float w = weight[a];
|
|
functional->accumulate(c, WeightedValue(l, w));
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// Compute cost of graph layout.
|
|
Float
|
|
Graph::cost() const
|
|
{
|
|
if (edges())
|
|
{
|
|
WeightedSum c;
|
|
Node::Index i = 1;
|
|
for (Arc::Index a = 1; a < adj.size(); a++)
|
|
{
|
|
while (node_end(i) <= a)
|
|
{
|
|
i++;
|
|
}
|
|
Node::Index j = arc_target(a);
|
|
Float l = length(i, j);
|
|
Float w = weight[a];
|
|
functional->accumulate(c, WeightedValue(l, w));
|
|
}
|
|
return functional->mean(c);
|
|
}
|
|
else
|
|
{
|
|
return Float(0);
|
|
}
|
|
}
|
|
|
|
// Swap the two nodes in positions k and l, k <= l.
|
|
void
|
|
Graph::swap(uint k, uint l)
|
|
{
|
|
Node::Index i = perm[k];
|
|
perm[k] = perm[l];
|
|
perm[l] = i;
|
|
Float p = node[i].pos - node[i].hlen;
|
|
do
|
|
{
|
|
i = perm[k];
|
|
p += node[i].hlen;
|
|
node[i].pos = p;
|
|
p += node[i].hlen;
|
|
}
|
|
while (k++ != l);
|
|
}
|
|
|
|
// Optimize continuous position of a single node.
|
|
Float
|
|
Graph::optimal(Node::Index i) const
|
|
{
|
|
vector<WeightedValue> v;
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
Node::Index j = adj[a];
|
|
if (placed(j))
|
|
{
|
|
v.push_back(WeightedValue(node[j].pos, weight[a]));
|
|
}
|
|
}
|
|
return v.empty() ? -1 : functional->optimum(v);
|
|
}
|
|
|
|
// Compute coarse graph with roughly half the number of nodes.
|
|
Graph*
|
|
Graph::coarsen()
|
|
{
|
|
progress->beginphase(this, string("coarse"));
|
|
Graph* g = new Graph(0, level - 1);
|
|
g->functional = functional;
|
|
g->progress = progress;
|
|
|
|
// Compute importance of nodes in fine graph.
|
|
DynamicHeap<Node::Index, Float> heap;
|
|
for (Node::Index i = 1; i < node.size(); i++)
|
|
{
|
|
node[i].parent = Node::null;
|
|
Float w = 0;
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
w += bond[a];
|
|
}
|
|
heap.insert(i, w);
|
|
}
|
|
|
|
// Select set of important nodes from fine graph that will remain in
|
|
// coarse graph.
|
|
vector<Node::Index> child(1, Node::null);
|
|
while (!heap.empty())
|
|
{
|
|
Node::Index i;
|
|
Float w = 0;
|
|
heap.extract(i, w);
|
|
if (w < 0)
|
|
{
|
|
break;
|
|
}
|
|
child.push_back(i);
|
|
node[i].parent = g->insert_node(2 * node[i].hlen);
|
|
|
|
// Reduce importance of neighbors.
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
Node::Index j = adj[a];
|
|
if (heap.find(j, w))
|
|
{
|
|
heap.update(j, w - 2 * bond[a]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assign parts of remaining nodes to aggregates.
|
|
vector<Float> part = bond;
|
|
for (Node::Index i = 1; i < node.size(); i++)
|
|
if (!persistent(i))
|
|
{
|
|
// Find all connections to coarse nodes.
|
|
Float w = 0;
|
|
Float max = 0;
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
Node::Index j = adj[a];
|
|
if (persistent(j))
|
|
{
|
|
w += part[a];
|
|
if (max < part[a])
|
|
{
|
|
max = part[a];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
part[a] = -1;
|
|
}
|
|
}
|
|
max /= GECKO_PART_FRAC;
|
|
|
|
// Weed out insignificant connections.
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
if (0 < part[a] && part[a] < max)
|
|
{
|
|
w -= part[a];
|
|
part[a] = -1;
|
|
}
|
|
|
|
// Compute node fractions (interpolation matrix) and assign
|
|
// partial nodes to aggregates.
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
if (part[a] > 0)
|
|
{
|
|
part[a] /= w;
|
|
Node::Index p = node[adj[a]].parent;
|
|
g->node[p].hlen += part[a] * node[i].hlen;
|
|
}
|
|
}
|
|
|
|
// Transfer arcs to coarse graph.
|
|
for (Node::Index p = 1; p < g->node.size(); p++)
|
|
{
|
|
Node::Index i = child[p];
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
transfer(g, part, p, a);
|
|
Node::Index j = adj[a];
|
|
if (!persistent(j))
|
|
{
|
|
Arc::Index b = arc_index(j, i);
|
|
if (part[b] > 0)
|
|
for (Arc::Index c = node_begin(j); c < node_end(j); c++)
|
|
{
|
|
Node::Index k = adj[c];
|
|
if (k != i)
|
|
{
|
|
transfer(g, part, p, c, part[b]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
if (g->directed())
|
|
{
|
|
throw runtime_error("directed edge found");
|
|
}
|
|
#endif
|
|
|
|
// Free memory.
|
|
vector<Float> t = bond;
|
|
bond.swap(t);
|
|
|
|
progress->endphase(this, false);
|
|
|
|
return g;
|
|
}
|
|
|
|
// Order nodes according to coarsened graph layout.
|
|
void
|
|
Graph::refine(const Graph* graph)
|
|
{
|
|
progress->beginphase(this, string("refine"));
|
|
|
|
// Place persistent nodes.
|
|
DynamicHeap<Node::Index, Float> heap;
|
|
for (Node::Index i = 1; i < node.size(); i++)
|
|
if (persistent(i))
|
|
{
|
|
Node::Index p = node[i].parent;
|
|
node[i].pos = graph->node[p].pos;
|
|
}
|
|
else
|
|
{
|
|
node[i].pos = -1;
|
|
Float w = 0;
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
Node::Index j = adj[a];
|
|
if (persistent(j))
|
|
{
|
|
w += weight[a];
|
|
}
|
|
}
|
|
heap.insert(i, w);
|
|
}
|
|
|
|
// Place remaining nodes in order of decreasing connectivity with
|
|
// already placed nodes.
|
|
while (!heap.empty())
|
|
{
|
|
Node::Index i = 0;
|
|
heap.extract(i);
|
|
node[i].pos = optimal(i);
|
|
for (Arc::Index a = node_begin(i); a < node_end(i); a++)
|
|
{
|
|
Node::Index j = adj[a];
|
|
Float w;
|
|
if (heap.find(j, w))
|
|
{
|
|
heap.update(j, w + weight[a]);
|
|
}
|
|
}
|
|
}
|
|
|
|
place(true);
|
|
progress->endphase(this, true);
|
|
}
|
|
|
|
// Perform m sweeps of compatible or Gauss-Seidel relaxation.
|
|
void
|
|
Graph::relax(bool compatible, uint m)
|
|
{
|
|
progress->beginphase(this, compatible ? string("crelax") : string("frelax"));
|
|
while (m--)
|
|
for (uint k = 0; k < perm.size() && !progress->quit(); k++)
|
|
{
|
|
Node::Index i = perm[k];
|
|
if (!compatible || !persistent(i))
|
|
{
|
|
node[i].pos = optimal(i);
|
|
}
|
|
}
|
|
place(true);
|
|
progress->endphase(this, true);
|
|
}
|
|
|
|
// Optimize successive n-node subgraphs.
|
|
void
|
|
Graph::optimize(uint n)
|
|
{
|
|
if (n > perm.size())
|
|
{
|
|
n = uint(perm.size());
|
|
}
|
|
ostringstream count;
|
|
count << setw(2) << n;
|
|
progress->beginphase(this, string("perm") + count.str());
|
|
Subgraph* subgraph = new Subgraph(this, n);
|
|
for (uint k = 0; k <= perm.size() - n && !progress->quit(); k++)
|
|
{
|
|
subgraph->optimize(k);
|
|
}
|
|
delete subgraph;
|
|
progress->endphase(this, true);
|
|
}
|
|
|
|
// Place all nodes according to their positions.
|
|
void
|
|
Graph::place(bool sort)
|
|
{
|
|
place(sort, 0, uint(perm.size()));
|
|
}
|
|
|
|
// Place nodes {k, ..., k + n - 1} according to their positions.
|
|
void
|
|
Graph::place(bool sort, uint k, uint n)
|
|
{
|
|
// Place nodes.
|
|
if (sort)
|
|
{
|
|
stable_sort(perm.begin() + k, perm.begin() + k + n,
|
|
Node::Comparator(node.begin()));
|
|
}
|
|
|
|
// Assign node positions according to permutation.
|
|
for (Float p = k ? node[perm[k - 1]].pos + node[perm[k - 1]].hlen : 0; n--;
|
|
k++)
|
|
{
|
|
Node::Index i = perm[k];
|
|
p += node[i].hlen;
|
|
node[i].pos = p;
|
|
p += node[i].hlen;
|
|
}
|
|
}
|
|
|
|
// Perform one V-cycle.
|
|
void
|
|
Graph::vcycle(uint n, uint work)
|
|
{
|
|
if (n < nodes() && nodes() < edges() && level && !progress->quit())
|
|
{
|
|
Graph* graph = coarsen();
|
|
graph->vcycle(n, work + edges());
|
|
refine(graph);
|
|
delete graph;
|
|
}
|
|
else
|
|
{
|
|
place();
|
|
}
|
|
if (edges())
|
|
{
|
|
relax(true, GECKO_CR_SWEEPS);
|
|
relax(false, GECKO_GS_SWEEPS);
|
|
for (uint w = edges(); w * (n + 1) < work; w *= ++n);
|
|
n = std::min(n, uint(GECKO_WINDOW_MAX));
|
|
if (n)
|
|
{
|
|
optimize(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Custom random-number generator for reproducibility.
|
|
// LCG from doi:10.1090/S0025-5718-99-00996-5.
|
|
uint
|
|
Graph::random(uint seed)
|
|
{
|
|
static uint state = 1;
|
|
state = (seed ? seed : 0x1ed0675 * state + 0xa14f);
|
|
return state;
|
|
}
|
|
|
|
// Generate a random permutation of the nodes.
|
|
void
|
|
Graph::shuffle(uint seed)
|
|
{
|
|
random(seed);
|
|
for (uint k = 0; k < perm.size(); k++)
|
|
{
|
|
uint r = random() >> 8;
|
|
uint l = k + r % (uint(perm.size()) - k);
|
|
std::swap(perm[k], perm[l]);
|
|
}
|
|
place();
|
|
}
|
|
|
|
// Recompute bonds for k'th V-cycle.
|
|
void
|
|
Graph::reweight(uint k)
|
|
{
|
|
bond.resize(weight.size());
|
|
for (Arc::Index a = 1; a < adj.size(); a++)
|
|
{
|
|
bond[a] = functional->bond(weight[a], length(a), k);
|
|
}
|
|
}
|
|
|
|
// Linearly order graph.
|
|
void
|
|
Graph::order(Functional* functional, uint iterations, uint window, uint period,
|
|
uint seed, Progress* progress)
|
|
{
|
|
// Initialize graph.
|
|
this->functional = functional;
|
|
progress = this->progress = progress ? progress : new Progress;
|
|
for (level = 0; (1u << level) < nodes(); level++);
|
|
place();
|
|
Float mincost = cost();
|
|
vector<Node::Index> minperm = perm;
|
|
if (seed)
|
|
{
|
|
shuffle(seed);
|
|
}
|
|
|
|
progress->beginorder(this, mincost);
|
|
if (edges())
|
|
{
|
|
// Perform specified number of V-cycles.
|
|
for (uint k = 1; k <= iterations && !progress->quit(); k++)
|
|
{
|
|
progress->beginiter(this, k, iterations, window);
|
|
reweight(k);
|
|
vcycle(window);
|
|
Float c = cost();
|
|
if (c < mincost)
|
|
{
|
|
mincost = c;
|
|
minperm = perm;
|
|
}
|
|
progress->enditer(this, mincost, c);
|
|
if (period && !(k % period))
|
|
{
|
|
window++;
|
|
}
|
|
}
|
|
perm = minperm;
|
|
place();
|
|
}
|
|
progress->endorder(this, mincost);
|
|
|
|
if (!progress)
|
|
{
|
|
delete this->progress;
|
|
this->progress = 0;
|
|
}
|
|
}
|