You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
14 KiB
389 lines
14 KiB
/*
|
|
* Copyright(C) 1999-2020, 2022, 2023 National Technology & Engineering Solutions
|
|
* of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
|
|
* NTESS, the U.S. Government retains certain rights in this software.
|
|
*
|
|
* See packages/seacas/LICENSE for details
|
|
*/
|
|
|
|
#include "defs.h" // for TRUE, FALSE, max, min
|
|
#include "params.h" // for MAXSETS
|
|
#include "structs.h" // for vtx_data
|
|
|
|
/* Enact a single move from largest (smallest) set in such a way
|
|
that some small (large) set gets bigger. */
|
|
|
|
int N_VTX_CHECKS; /* number of considered moves */
|
|
int N_VTX_MOVES; /* number of actual moves */
|
|
static void nextmove(int nvtxs, /* number of vertices in graph */
|
|
int nsets, /* how many sets am I dividing into? */
|
|
double *vals[][MAXSETS], /* values in sorted lists */
|
|
int *indices[][MAXSETS], /* indices that define order in sorted lists */
|
|
int startvtx[][MAXSETS], /* index values corresponding to splitter */
|
|
double *dist, /* distances defining splitter */
|
|
int *sets, /* set assignment for each vertex */
|
|
int toobig, /* is bad set too big or too small? */
|
|
int *active, /* flags sets trying to change size */
|
|
int *next_vtx, /* vertex selected to move next */
|
|
int *next_to, /* set vertex should be moved to */
|
|
double *next_delta /* size of change in distances */
|
|
);
|
|
|
|
static void undo_coupling(struct vtx_data **graph, /* data structure with vertex weights */
|
|
int *sets, /* sets each vertex is in */
|
|
int nsets, /* number of sets being divided into */
|
|
int from, int to, /* set final vertex moved from and to */
|
|
int toobig, /* are we shrinking or enlarging a set? */
|
|
int badset, /* the set number being shrunk or enlarged */
|
|
double *size /* sizes of the different sets */
|
|
);
|
|
|
|
static void couple(int nsets, /* number of sets being divided into */
|
|
int from, int to, /* sets to be coupled */
|
|
int vtx /* vertex that they share */
|
|
);
|
|
|
|
void movevtxs(struct vtx_data **graph, /* data structure with vertex weights */
|
|
int nvtxs, /* number of vertices in graph */
|
|
int nsets, /* how many sets am I dividing into? */
|
|
double *dist, /* distances defining splitter */
|
|
int *indices[][MAXSETS], /* indices that define order in sorted lists */
|
|
double *vals[][MAXSETS], /* values in sorted lists */
|
|
int startvtx[][MAXSETS], /* index values corresponding to splitter */
|
|
int *sets, /* set assignment for each vertex */
|
|
double *size, /* sizes of the different sets */
|
|
double *goal, /* desired set sizes */
|
|
int vwgt_max /* largest vertex weight */
|
|
)
|
|
{
|
|
double largest; /* largest overshoot from desired size */
|
|
double smallest; /* largest undershoot from desired size */
|
|
int active[MAXSETS]; /* flags sets trying to change size */
|
|
double delta; /* amount distances must change */
|
|
int vtx; /* vertex being moved */
|
|
int to, from; /* set vertex is being moved to/from */
|
|
int weight; /* weight of vertex being moved */
|
|
int done; /* have I successfully move a vertex? */
|
|
|
|
/* int npass=0; */ /* counts passes through main loop */
|
|
int badset = -1; /* most unbalanced set */
|
|
int toobig = 0; /* badset too large or too small? */
|
|
int balanced; /* is balance attained? */
|
|
double imbalance; /* amount of imbalance in badset */
|
|
int i; /* loop counter */
|
|
|
|
/* Find most unbalanced set. */
|
|
|
|
imbalance = largest = smallest = 0;
|
|
for (i = 0; i < nsets; i++) {
|
|
if (size[i] - goal[i] > largest) {
|
|
largest = size[i] - goal[i];
|
|
if (largest > imbalance) {
|
|
imbalance = largest;
|
|
badset = i;
|
|
toobig = 1;
|
|
}
|
|
}
|
|
else if (goal[i] - size[i] > smallest) {
|
|
smallest = goal[i] - size[i];
|
|
if (smallest > imbalance) {
|
|
imbalance = smallest;
|
|
badset = i;
|
|
toobig = -1;
|
|
}
|
|
}
|
|
}
|
|
if (largest + smallest <= vwgt_max) {
|
|
balanced = TRUE;
|
|
}
|
|
else {
|
|
balanced = FALSE;
|
|
}
|
|
|
|
/* If not balanced, change distances to move vertices between sets. */
|
|
while (!balanced) {
|
|
/* npass++; */
|
|
for (i = 0; i < nsets; i++) {
|
|
active[i] = FALSE;
|
|
}
|
|
active[badset] = TRUE;
|
|
|
|
done = FALSE;
|
|
while (!done) {
|
|
nextmove(nvtxs, nsets, vals, indices, startvtx, dist, sets, toobig, active, &vtx, &to,
|
|
&delta);
|
|
from = sets[vtx];
|
|
weight = graph[vtx]->vwgt;
|
|
|
|
/* Now adjust all active dists to reflect this move so far. */
|
|
for (i = 0; i < nsets; i++) {
|
|
if (active[i]) {
|
|
dist[i] -= toobig * delta;
|
|
}
|
|
}
|
|
if (toobig > 0) {
|
|
if (size[to] + weight - goal[to] < largest) {
|
|
done = TRUE;
|
|
size[from] -= graph[vtx]->vwgt;
|
|
size[to] += graph[vtx]->vwgt;
|
|
sets[vtx] = to;
|
|
undo_coupling(graph, sets, nsets, from, to, toobig, badset, size);
|
|
}
|
|
else {
|
|
couple(nsets, from, to, vtx);
|
|
active[to] = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
if (goal[from] - (size[from] - weight) < smallest) {
|
|
done = TRUE;
|
|
size[from] -= graph[vtx]->vwgt;
|
|
size[to] += graph[vtx]->vwgt;
|
|
sets[vtx] = to;
|
|
undo_coupling(graph, sets, nsets, from, to, toobig, badset, size);
|
|
}
|
|
else {
|
|
couple(nsets, from, to, vtx);
|
|
active[from] = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find most unbalanced set. */
|
|
imbalance = largest = smallest = 0;
|
|
for (i = 0; i < nsets; i++) {
|
|
if (size[i] - goal[i] > largest) {
|
|
largest = size[i] - goal[i];
|
|
if (largest > imbalance) {
|
|
imbalance = largest;
|
|
badset = i;
|
|
toobig = 1;
|
|
}
|
|
}
|
|
else if (goal[i] - size[i] > smallest) {
|
|
smallest = goal[i] - size[i];
|
|
if (smallest > imbalance) {
|
|
imbalance = smallest;
|
|
badset = i;
|
|
toobig = -1;
|
|
}
|
|
}
|
|
}
|
|
if (largest + smallest <= vwgt_max) {
|
|
balanced = TRUE;
|
|
}
|
|
else {
|
|
balanced = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find the next move from an active to an inactive set, returning */
|
|
/* next_vtx, next_to, and distance. */
|
|
static void nextmove(int nvtxs, /* number of vertices in graph */
|
|
int nsets, /* how many sets am I dividing into? */
|
|
double *vals[][MAXSETS], /* values in sorted lists */
|
|
int *indices[][MAXSETS], /* indices that define order in sorted lists */
|
|
int startvtx[][MAXSETS], /* index values corresponding to splitter */
|
|
double *dist, /* distances defining splitter */
|
|
int *sets, /* set assignment for each vertex */
|
|
int toobig, /* is bad set too big or too small? */
|
|
int *active, /* flags sets trying to change size */
|
|
int *next_vtx, /* vertex selected to move next */
|
|
int *next_to, /* set vertex should be moved to */
|
|
double *next_delta /* size of change in distances */
|
|
)
|
|
{
|
|
double delta; /* amount distance must change */
|
|
double bestdelta; /* best value see so far */
|
|
int good; /* is this the delta OK? */
|
|
int first; /* is this the first OK delta I've seen? */
|
|
int maxset, minset; /* larger/smaller of two sets */
|
|
int bestfrom = 0, bestto = 0; /* sets best move comes from and goes to */
|
|
int bestdir = 0; /* direction to step in list for best move */
|
|
int bestvtx = 0; /* vertex being moved between sets */
|
|
int from, to; /* sets vertex wants to move from and to */
|
|
int index; /* offset into indices array */
|
|
int dir = 0; /* direction to step through list */
|
|
int i, j; /* loop counter */
|
|
|
|
bestdelta = 0;
|
|
first = TRUE;
|
|
while (first) {
|
|
for (i = 0; i < nsets; i++) {
|
|
if (active[i]) {
|
|
for (j = 0; j < nsets; j++) {
|
|
if (!active[j]) {
|
|
/* Look for next move from active set i to inactive set j. */
|
|
if (toobig > 0) {
|
|
from = i;
|
|
to = j;
|
|
}
|
|
else {
|
|
from = j;
|
|
to = i;
|
|
}
|
|
minset = min(to, from);
|
|
maxset = max(to, from);
|
|
|
|
index = startvtx[minset][maxset];
|
|
if (index >= nvtxs || index < 0) {
|
|
good = FALSE;
|
|
}
|
|
else {
|
|
if (j > i) {
|
|
dir = -1;
|
|
}
|
|
else {
|
|
dir = 1;
|
|
}
|
|
good = TRUE;
|
|
delta = -toobig * dir * vals[minset][maxset][indices[minset][maxset][index]] -
|
|
(dist[to] - dist[from]);
|
|
}
|
|
|
|
if (good && (first || delta < bestdelta)) {
|
|
/* Is this the best so far? */
|
|
first = FALSE;
|
|
bestdelta = delta;
|
|
bestfrom = from;
|
|
bestto = to;
|
|
bestdir = dir;
|
|
bestvtx = indices[minset][maxset][index] + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Only accept a vertex if it's from the right set. */
|
|
if (sets[bestvtx] != bestfrom || (toobig > 0 && !active[bestfrom]) ||
|
|
(toobig < 0 && !active[bestto])) {
|
|
/* Set rejection flag, and increment startvtx pointer. */
|
|
first = TRUE;
|
|
minset = min(bestto, bestfrom);
|
|
maxset = max(bestto, bestfrom);
|
|
startvtx[minset][maxset] -= toobig * bestdir;
|
|
}
|
|
++N_VTX_CHECKS;
|
|
}
|
|
*next_vtx = bestvtx;
|
|
*next_to = bestto;
|
|
*next_delta = bestdelta;
|
|
++N_VTX_MOVES;
|
|
}
|
|
|
|
static int ncoupled = 0;
|
|
static int coupled_vtxs[MAXSETS];
|
|
static int coupled_sets[MAXSETS][MAXSETS];
|
|
|
|
static void couple(int nsets, /* number of sets being divided into */
|
|
int from, int to, /* sets to be coupled */
|
|
int vtx /* vertex that they share */
|
|
)
|
|
{
|
|
int i; /* loop counter */
|
|
|
|
/* Check for degenerate case of vertex shared by more than two sets. */
|
|
for (i = 0; i < ncoupled; i++) {
|
|
if (coupled_vtxs[i] == vtx) {
|
|
coupled_sets[i][from] = -1;
|
|
coupled_sets[i][to] = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
coupled_vtxs[ncoupled] = vtx;
|
|
for (i = 0; i < nsets; i++) {
|
|
coupled_sets[ncoupled][i] = 0;
|
|
}
|
|
coupled_sets[ncoupled][from] = -1;
|
|
coupled_sets[ncoupled][to] = 1;
|
|
++ncoupled;
|
|
}
|
|
|
|
static void undo_coupling(struct vtx_data **graph, /* data structure with vertex weights */
|
|
int *sets, /* sets each vertex is in */
|
|
int nsets, /* number of sets being divided into */
|
|
int from, int to, /* set final vertex moved from and to */
|
|
int toobig, /* are we shrinking or enlarging a set? */
|
|
int badset, /* the set number being shrunk or enlarged */
|
|
double *size /* sizes of the different sets */
|
|
)
|
|
{
|
|
int done; /* have enough vertices been moved? */
|
|
int found; /* have I found the right set? */
|
|
int vtx; /* vertex being moved between sets */
|
|
int i, j; /* loop counter */
|
|
|
|
if (ncoupled == 0) {
|
|
return;
|
|
}
|
|
|
|
if (toobig > 0) {
|
|
done = FALSE;
|
|
if (from == badset) {
|
|
done = TRUE;
|
|
}
|
|
while (!done) {
|
|
found = FALSE;
|
|
to = from;
|
|
for (i = 0; i < ncoupled && !found; i++) {
|
|
if (coupled_sets[i][from] == 1) { /* Found an edge into set. */
|
|
found = TRUE;
|
|
/* And find other end of edge. */
|
|
for (j = 0; j < nsets; j++) {
|
|
if (coupled_sets[i][j] == -1) {
|
|
from = j;
|
|
}
|
|
}
|
|
|
|
/* Switch the set for this vertex. */
|
|
vtx = coupled_vtxs[i];
|
|
size[from] -= graph[vtx]->vwgt;
|
|
size[to] += graph[vtx]->vwgt;
|
|
sets[vtx] = to;
|
|
coupled_sets[i][from] = 0;
|
|
|
|
if (from == badset) {
|
|
done = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
done = FALSE;
|
|
if (to == badset) {
|
|
done = TRUE;
|
|
}
|
|
while (!done) {
|
|
found = FALSE;
|
|
from = to;
|
|
for (i = 0; i < ncoupled && !found; i++) {
|
|
if (coupled_sets[i][from] == -1) { /* Found an edge from set. */
|
|
found = TRUE;
|
|
/* And find other end of edge. */
|
|
for (j = 0; j < nsets; j++) {
|
|
if (coupled_sets[i][j] == 1) {
|
|
to = j;
|
|
}
|
|
}
|
|
|
|
/* Switch the set for this vertex. */
|
|
vtx = coupled_vtxs[i];
|
|
size[from] -= graph[vtx]->vwgt;
|
|
size[to] += graph[vtx]->vwgt;
|
|
sets[vtx] = to;
|
|
coupled_sets[i][to] = 0;
|
|
|
|
if (to == badset) {
|
|
done = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ncoupled = 0;
|
|
}
|
|
|