Cloned SEACAS for EXODUS library with extra build files for internal package management.
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

/*
* 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);
}