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.
341 lines
12 KiB
341 lines
12 KiB
/*
|
|
* Copyright(C) 1999-2020 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"
|
|
#include "internal.h"
|
|
#include "structs.h"
|
|
#include <stdio.h>
|
|
|
|
int improve_internal(struct vtx_data **graph, /* graph data structure */
|
|
int nvtxs, /* number of vertices in graph */
|
|
int * assign, /* current assignment */
|
|
double * goal, /* desired set sizes */
|
|
struct bidint * int_list, /* sorted list of internal vtx values */
|
|
struct bidint * set_list, /* headers of vtx_elems lists */
|
|
struct bidint * vtx_elems, /* lists of vtxs in each set */
|
|
int set1, /* set to try to improve */
|
|
int * locked, /* indicates vertices not allowed to move */
|
|
int * nlocked, /* number of vertices that can't move */
|
|
int using_ewgts, /* are edge weights being used? */
|
|
int vwgt_max, /* largest vertex weight */
|
|
int * total_vwgt /* total vertex weight in each set */
|
|
)
|
|
{
|
|
struct bidint *move_list; /* list of vertices changing sets */
|
|
struct bidint *ptr, *ptr2; /* loop through bidints */
|
|
struct bidint *changed_sets; /* list of sets that were modified */
|
|
double vwgt_avg = 0.0; /* average vertex weight in current set */
|
|
double degree_avg = 0.0; /* average vertex degree in current set */
|
|
double frac = .4; /* fraction of neighbors acceptable to move. */
|
|
double cost, min_cost; /* cost of making a vertex internal */
|
|
double min_cost_start; /* larger than any possible cost */
|
|
double cost_limit; /* acceptable cost of internalization */
|
|
double ratio; /* possible wgt / desired wgt */
|
|
float ewgt; /* weight of an edge */
|
|
int set2, set3; /* sets of two vertices */
|
|
int vtx, best_vtx = -1; /* vertex to make internal */
|
|
int move_vtx = -1; /* vertex to move between sets */
|
|
int neighbor; /* neighbor of a vertex */
|
|
int nguys = 0; /* number of vertices in current set */
|
|
int internal; /* is a vertex internal or not? */
|
|
int balanced; /* are two sets balanced? */
|
|
int flag; /* did I improve things: return code */
|
|
int size; /* array spacing */
|
|
int i, j; /* loop counters */
|
|
|
|
/* First find best candidate vertex to internalize. */
|
|
/* This is vertex which is already most nearly internal. */
|
|
min_cost_start = 2.0 * vwgt_max * nvtxs;
|
|
min_cost = min_cost_start;
|
|
size = (int)(&(vtx_elems[1]) - &(vtx_elems[0]));
|
|
for (ptr = set_list[set1].next; ptr != NULL; ptr = ptr->next) {
|
|
++nguys;
|
|
vtx = ((int)(ptr - vtx_elems)) / size;
|
|
vwgt_avg += graph[vtx]->vwgt;
|
|
degree_avg += (graph[vtx]->nedges - 1);
|
|
cost = 0;
|
|
for (i = 1; i < graph[vtx]->nedges; i++) {
|
|
neighbor = graph[vtx]->edges[i];
|
|
set2 = assign[neighbor];
|
|
if (set2 != set1) {
|
|
if (locked[neighbor]) {
|
|
cost = min_cost_start;
|
|
}
|
|
else {
|
|
cost += graph[neighbor]->vwgt;
|
|
}
|
|
}
|
|
}
|
|
if (cost == 0) { /* Lock vertex and all it's neighbors. */
|
|
for (i = 1; i < graph[vtx]->nedges; i++) {
|
|
neighbor = graph[vtx]->edges[i];
|
|
if (!locked[neighbor]) {
|
|
locked[neighbor] = TRUE;
|
|
++(*nlocked);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cost < min_cost && cost != 0) {
|
|
min_cost = cost;
|
|
best_vtx = vtx;
|
|
}
|
|
}
|
|
|
|
if (nguys > 0) {
|
|
vwgt_avg /= nguys;
|
|
degree_avg /= nguys;
|
|
}
|
|
cost_limit = frac * vwgt_avg * degree_avg;
|
|
|
|
if (min_cost > cost_limit) {
|
|
return (FALSE);
|
|
}
|
|
|
|
/* Lock the candidate vertex in current set */
|
|
if (!locked[best_vtx]) {
|
|
locked[best_vtx] = TRUE;
|
|
++(*nlocked);
|
|
}
|
|
|
|
/* Also lock all his neighbors in set. */
|
|
for (i = 1; i < graph[best_vtx]->nedges; i++) {
|
|
neighbor = graph[best_vtx]->edges[i];
|
|
set2 = assign[neighbor];
|
|
if (set1 == set2 && !locked[neighbor]) {
|
|
locked[neighbor] = TRUE;
|
|
++(*nlocked);
|
|
}
|
|
vtx_elems[neighbor].val = set1;
|
|
}
|
|
|
|
ewgt = 1;
|
|
move_list = NULL;
|
|
|
|
/* Now move neighbors of best_vtx to set1. */
|
|
for (i = 1; i < graph[best_vtx]->nedges; i++) {
|
|
neighbor = graph[best_vtx]->edges[i];
|
|
set2 = assign[neighbor];
|
|
if (set2 != set1) {
|
|
/* Add vertex to list of guys to move to set1. */
|
|
/* Don't move it yet in case I get stuck later. */
|
|
/* But change his assignment so that swapping vertex has current info. */
|
|
/* Note: This will require me to undo changes if I fail. */
|
|
|
|
locked[neighbor] = TRUE;
|
|
++(*nlocked);
|
|
|
|
/* Remove him from his set list. */
|
|
if (vtx_elems[neighbor].next != NULL) {
|
|
vtx_elems[neighbor].next->prev = vtx_elems[neighbor].prev;
|
|
}
|
|
if (vtx_elems[neighbor].prev != NULL) {
|
|
vtx_elems[neighbor].prev->next = vtx_elems[neighbor].next;
|
|
}
|
|
|
|
/* Put him in list of moved vertices */
|
|
vtx_elems[neighbor].next = move_list;
|
|
vtx_elems[neighbor].val = set2;
|
|
move_list = &(vtx_elems[neighbor]);
|
|
assign[neighbor] = set1;
|
|
|
|
total_vwgt[set2] -= graph[neighbor]->vwgt;
|
|
total_vwgt[set1] += graph[neighbor]->vwgt;
|
|
}
|
|
}
|
|
|
|
/* Now check if vertices need to be handed back to restore balance. */
|
|
flag = TRUE;
|
|
for (i = 1; i < graph[best_vtx]->nedges && flag; i++) {
|
|
neighbor = graph[best_vtx]->edges[i];
|
|
set2 = vtx_elems[neighbor].val;
|
|
if (set2 != set1) {
|
|
ratio = (total_vwgt[set1] + total_vwgt[set2]) / (goal[set1] + goal[set2]);
|
|
balanced = (total_vwgt[set1] - goal[set1] * ratio + goal[set2] * ratio - total_vwgt[set2]) <=
|
|
vwgt_max;
|
|
while (!balanced && flag) {
|
|
/* Find a vertex to move back to set2. Use a KL metric. */
|
|
min_cost = min_cost_start;
|
|
|
|
for (ptr = set_list[set1].next; ptr != NULL; ptr = ptr->next) {
|
|
vtx = ((int)(ptr - vtx_elems)) / size;
|
|
if (!locked[vtx]) {
|
|
cost = 0;
|
|
for (j = 1; j < graph[vtx]->nedges; j++) {
|
|
neighbor = graph[vtx]->edges[j];
|
|
if (using_ewgts) {
|
|
ewgt = graph[vtx]->ewgts[j];
|
|
}
|
|
set3 = assign[neighbor];
|
|
if (set3 == set1) {
|
|
cost += ewgt;
|
|
}
|
|
else if (set3 == set2) {
|
|
cost -= ewgt;
|
|
}
|
|
}
|
|
if (cost < min_cost) {
|
|
min_cost = cost;
|
|
move_vtx = vtx;
|
|
}
|
|
}
|
|
}
|
|
if (min_cost >= min_cost_start) {
|
|
flag = FALSE;
|
|
}
|
|
else {
|
|
/* Add move_vtx to list of guys to move to set2. */
|
|
/* Don't move it yet in case I get stuck later. */
|
|
/* But change assign so later decisions have up-to-date info. */
|
|
if (vtx_elems[move_vtx].next != NULL) {
|
|
vtx_elems[move_vtx].next->prev = vtx_elems[move_vtx].prev;
|
|
}
|
|
if (vtx_elems[move_vtx].prev != NULL) {
|
|
vtx_elems[move_vtx].prev->next = vtx_elems[move_vtx].next;
|
|
}
|
|
vtx_elems[move_vtx].next = move_list;
|
|
vtx_elems[move_vtx].val = -(set2 + 1);
|
|
move_list = &(vtx_elems[move_vtx]);
|
|
assign[move_vtx] = set2;
|
|
|
|
total_vwgt[set2] += graph[move_vtx]->vwgt;
|
|
total_vwgt[set1] -= graph[move_vtx]->vwgt;
|
|
}
|
|
balanced = total_vwgt[set1] - goal[set1] + goal[set2] - total_vwgt[set2] <= vwgt_max;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!flag) {
|
|
/* Can't rebalance sets. Give up, but first restore the data structures. */
|
|
/* These include vtx_lists, total_vwgts and assign. */
|
|
|
|
for (ptr = move_list; ptr != NULL;) {
|
|
ptr2 = ptr->next;
|
|
vtx = ((int)(ptr - vtx_elems)) / size;
|
|
if (ptr->val >= 0) { /* Almost moved from set2 to set1. */
|
|
set2 = ptr->val;
|
|
assign[vtx] = set2;
|
|
total_vwgt[set2] += graph[vtx]->vwgt;
|
|
total_vwgt[set1] -= graph[vtx]->vwgt;
|
|
locked[vtx] = FALSE;
|
|
--(*nlocked);
|
|
}
|
|
else { /* Almost moved from set1 to set2. */
|
|
set2 = -(ptr->val + 1);
|
|
assign[vtx] = set1;
|
|
total_vwgt[set2] -= graph[vtx]->vwgt;
|
|
total_vwgt[set1] += graph[vtx]->vwgt;
|
|
set2 = set1;
|
|
}
|
|
|
|
/* Now add vertex back into its old vtx_list (now indicated by set2) */
|
|
ptr->next = set_list[set2].next;
|
|
if (ptr->next != NULL) {
|
|
ptr->next->prev = ptr;
|
|
}
|
|
ptr->prev = &(set_list[set2]);
|
|
set_list[set2].next = ptr;
|
|
|
|
ptr = ptr2;
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
/* Now perform actual moves. */
|
|
/* First, update assignment and place vertices into their new sets. */
|
|
changed_sets = NULL;
|
|
for (ptr = move_list; ptr != NULL;) {
|
|
ptr2 = ptr->next;
|
|
vtx = ((int)(ptr - vtx_elems)) / size;
|
|
if (ptr->val >= 0) {
|
|
set2 = set1;
|
|
}
|
|
else {
|
|
set2 = -(ptr->val + 1);
|
|
}
|
|
|
|
ptr->next = set_list[set2].next;
|
|
if (ptr->next != NULL) {
|
|
ptr->next->prev = ptr;
|
|
}
|
|
ptr->prev = &(set_list[set2]);
|
|
set_list[set2].next = ptr;
|
|
|
|
/* Pull int_list[set2] out of its list to be used later. */
|
|
if (ptr->val >= 0) {
|
|
set2 = ptr->val;
|
|
}
|
|
if (int_list[set2].val >= 0) {
|
|
int_list[set2].val = -(int_list[set2].val + 1);
|
|
if (int_list[set2].next != NULL) {
|
|
int_list[set2].next->prev = int_list[set2].prev;
|
|
}
|
|
if (int_list[set2].prev != NULL) {
|
|
int_list[set2].prev->next = int_list[set2].next;
|
|
}
|
|
|
|
int_list[set2].next = changed_sets;
|
|
changed_sets = &(int_list[set2]);
|
|
}
|
|
ptr = ptr2;
|
|
}
|
|
if (int_list[set1].val >= 0) {
|
|
if (int_list[set1].next != NULL) {
|
|
int_list[set1].next->prev = int_list[set1].prev;
|
|
}
|
|
if (int_list[set1].prev != NULL) {
|
|
int_list[set1].prev->next = int_list[set1].next;
|
|
}
|
|
|
|
int_list[set1].next = changed_sets;
|
|
changed_sets = &(int_list[set1]);
|
|
}
|
|
|
|
/* Finally, update internal node calculations for all modified sets. */
|
|
while (changed_sets != NULL) {
|
|
set2 = ((int)(changed_sets - int_list)) / size;
|
|
changed_sets = changed_sets->next;
|
|
|
|
/* Next line uses fact that list has dummy header so prev isn't NULL. */
|
|
int_list[set2].next = int_list[set2].prev->next;
|
|
int_list[set2].val = 0;
|
|
/* Recompute internal nodes for this set */
|
|
for (ptr = set_list[set2].next; ptr != NULL; ptr = ptr->next) {
|
|
vtx = ((int)(ptr - vtx_elems)) / size;
|
|
internal = TRUE;
|
|
for (j = 1; j < graph[vtx]->nedges && internal; j++) {
|
|
set3 = assign[graph[vtx]->edges[j]];
|
|
internal = (set3 == set2);
|
|
}
|
|
if (internal) {
|
|
int_list[set2].val += graph[vtx]->vwgt;
|
|
}
|
|
}
|
|
|
|
/* Now move internal value in doubly linked list. */
|
|
/* Move higher in list? */
|
|
while (int_list[set2].next != NULL && int_list[set2].val >= int_list[set2].next->val) {
|
|
int_list[set2].prev = int_list[set2].next;
|
|
int_list[set2].next = int_list[set2].next->next;
|
|
}
|
|
/* Move lower in list? */
|
|
while (int_list[set2].prev != NULL && int_list[set2].val < int_list[set2].prev->val) {
|
|
int_list[set2].next = int_list[set2].prev;
|
|
int_list[set2].prev = int_list[set2].prev->prev;
|
|
}
|
|
|
|
if (int_list[set2].next != NULL) {
|
|
int_list[set2].next->prev = &(int_list[set2]);
|
|
}
|
|
if (int_list[set2].prev != NULL) {
|
|
int_list[set2].prev->next = &(int_list[set2]);
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|