1
/* This software was developed by Bruce Hendrickson and Robert Leland *
2
* at Sandia National Laboratories under US Department of Energy *
3
* contract DE-AC04-76DP00789 and is copyrighted by Sandia Corporation. */
11
int improve_internal(graph, nvtxs, assign, goal, int_list, set_list,
12
vtx_elems, set1, locked, nlocked, using_ewgts, vwgt_max, total_vwgt)
13
struct vtx_data **graph; /* graph data structure */
14
int nvtxs; /* number of vertices in graph */
15
short *assign; /* current assignment */
16
double *goal; /* desired set sizes */
17
struct bidint *int_list; /* sorted list of internal vtx values */
18
struct bidint *set_list; /* headers of vtx_elems lists */
19
struct bidint *vtx_elems; /* lists of vtxs in each set */
20
short set1; /* set to try to improve */
21
short *locked; /* indicates vertices not allowed to move */
22
int *nlocked; /* number of vertices that can't move */
23
int using_ewgts; /* are edge weights being used? */
24
int vwgt_max; /* largest vertex weight */
25
int *total_vwgt; /* total vertex weight in each set */
27
struct bidint *move_list; /* list of vertices changing sets */
28
struct bidint *ptr, *ptr2; /* loop through bidints */
29
struct bidint *changed_sets;/* list of sets that were modified */
30
double vwgt_avg; /* average vertex weight in current set */
31
double degree_avg; /* average vertex degree in current set */
32
double frac = .4; /* fraction of neighbors acceptable to move. */
33
double cost, min_cost; /* cost of making a vertex internal */
34
double min_cost_start; /* larger than any possible cost */
35
double cost_limit; /* acceptable cost of internalization */
36
double ratio; /* possible wgt / desired wgt */
37
float ewgt; /* weight of an edge */
38
short set2, set3; /* sets of two vertices */
39
int vtx, best_vtx; /* vertex to make internal */
40
int move_vtx; /* vertex to move between sets */
41
int neighbor; /* neighbor of a vertex */
42
int nguys; /* number of vertices in current set */
43
int internal; /* is a vertex internal or not? */
44
int balanced; /* are two sets balanced? */
45
int flag; /* did I improve things: return code */
46
int size; /* array spacing */
47
int i, j; /* loop counters */
49
/* First find best candidate vertex to internalize. */
50
/* This is vertex which is already most nearly internal. */
51
min_cost_start = 2.0 * vwgt_max * nvtxs;
52
min_cost = min_cost_start;
55
size = (int) (&(vtx_elems[1]) - &(vtx_elems[0]));
57
for (ptr = set_list[set1].next; ptr != NULL; ptr = ptr->next) {
59
vtx = ((int) (ptr - vtx_elems)) / size;
60
vwgt_avg += graph[vtx]->vwgt;
61
degree_avg += (graph[vtx]->nedges - 1);
63
for (i = 1; i < graph[vtx]->nedges; i++) {
64
neighbor = graph[vtx]->edges[i];
65
set2 = assign[neighbor];
68
cost = min_cost_start;
70
cost += graph[neighbor]->vwgt;
73
if (cost == 0) { /* Lock vertex and all it's neighbors. */
74
for (i = 1; i < graph[vtx]->nedges; i++) {
75
neighbor = graph[vtx]->edges[i];
76
if (!locked[neighbor]) {
77
locked[neighbor] = TRUE;
83
if (cost < min_cost && cost != 0) {
91
cost_limit = frac * vwgt_avg * degree_avg;
93
if (min_cost > cost_limit) {
97
/* Lock the candidate vertex in current set */
98
if (!locked[best_vtx]) {
99
locked[best_vtx] = TRUE;
103
/* Also lock all his neighbors in set. */
104
for (i = 1; i < graph[best_vtx]->nedges; i++) {
105
neighbor = graph[best_vtx]->edges[i];
106
set2 = assign[neighbor];
107
if (set1 == set2 && !locked[neighbor]) {
108
locked[neighbor] = TRUE;
111
vtx_elems[neighbor].val = set1;
117
/* Now move neighbors of best_vtx to set1. */
118
for (i = 1; i < graph[best_vtx]->nedges; i++) {
119
neighbor = graph[best_vtx]->edges[i];
120
set2 = assign[neighbor];
122
/* Add vertex to list of guys to move to set1. */
123
/* Don't move it yet in case I get stuck later. */
124
/* But change his assignment so that swapping vertex has current info. */
125
/* Note: This will require me to undo changes if I fail. */
127
locked[neighbor] = TRUE;
130
/* Remove him from his set list. */
131
if (vtx_elems[neighbor].next != NULL) {
132
vtx_elems[neighbor].next->prev = vtx_elems[neighbor].prev;
134
if (vtx_elems[neighbor].prev != NULL) {
135
vtx_elems[neighbor].prev->next = vtx_elems[neighbor].next;
138
/* Put him in list of moved vertices */
139
vtx_elems[neighbor].next = move_list;
140
vtx_elems[neighbor].val = set2;
141
move_list = &(vtx_elems[neighbor]);
142
assign[neighbor] = set1;
144
total_vwgt[set2] -= graph[neighbor]->vwgt;
145
total_vwgt[set1] += graph[neighbor]->vwgt;
149
/* Now check if vertices need to be handed back to restore balance. */
151
for (i = 1; i < graph[best_vtx]->nedges && flag; i++) {
152
neighbor = graph[best_vtx]->edges[i];
153
set2 = vtx_elems[neighbor].val;
155
ratio = (total_vwgt[set1] + total_vwgt[set2]) /
156
(goal[set1] + goal[set2]);
157
balanced = (total_vwgt[set1] - goal[set1]*ratio +
158
goal[set2]*ratio - total_vwgt[set2]) <= vwgt_max;
159
while (!balanced && flag) {
160
/* Find a vertex to move back to set2. Use a KL metric. */
161
min_cost = min_cost_start;
163
for (ptr = set_list[set1].next; ptr != NULL; ptr = ptr->next) {
164
vtx = ((int) (ptr - vtx_elems)) / size;
167
for (j = 1; j < graph[vtx]->nedges; j++) {
168
neighbor = graph[vtx]->edges[j];
170
ewgt = graph[vtx]->ewgts[j];
171
set3 = assign[neighbor];
174
else if (set3 == set2)
177
if (cost < min_cost) {
183
if (min_cost >= min_cost_start)
186
/* Add move_vtx to list of guys to move to set2. */
187
/* Don't move it yet in case I get stuck later. */
188
/* But change assign so later decisions have up-to-date info. */
189
if (vtx_elems[move_vtx].next != NULL) {
190
vtx_elems[move_vtx].next->prev = vtx_elems[move_vtx].prev;
192
if (vtx_elems[move_vtx].prev != NULL) {
193
vtx_elems[move_vtx].prev->next = vtx_elems[move_vtx].next;
195
vtx_elems[move_vtx].next = move_list;
196
vtx_elems[move_vtx].val = -(set2 + 1);
197
move_list = &(vtx_elems[move_vtx]);
198
assign[move_vtx] = set2;
200
total_vwgt[set2] += graph[move_vtx]->vwgt;
201
total_vwgt[set1] -= graph[move_vtx]->vwgt;
203
balanced = total_vwgt[set1] - goal[set1] +
204
goal[set2] - total_vwgt[set2] <= vwgt_max;
210
/* Can't rebalance sets. Give up, but first restore the data structures. */
211
/* These include vtx_lists, total_vwgts and assign. */
213
for (ptr = move_list; ptr != NULL;) {
215
vtx = ((int) (ptr - vtx_elems)) / size;
216
if (ptr->val >= 0) {/* Almost moved from set2 to set1. */
219
total_vwgt[set2] += graph[vtx]->vwgt;
220
total_vwgt[set1] -= graph[vtx]->vwgt;
224
else { /* Almost moved from set1 to set2. */
225
set2 = -(ptr->val + 1);
227
total_vwgt[set2] -= graph[vtx]->vwgt;
228
total_vwgt[set1] += graph[vtx]->vwgt;
232
/* Now add vertex back into its old vtx_list (now indicated by set2) */
233
ptr->next = set_list[set2].next;
234
if (ptr->next != NULL)
235
ptr->next->prev = ptr;
236
ptr->prev = &(set_list[set2]);
237
set_list[set2].next = ptr;
244
else { /* Now perform actual moves. */
245
/* First, update assignment and place vertices into their new sets. */
247
for (ptr = move_list; ptr != NULL;) {
249
vtx = ((int) (ptr - vtx_elems)) / size;
253
set2 = -(ptr->val + 1);
255
ptr->next = set_list[set2].next;
256
if (ptr->next != NULL)
257
ptr->next->prev = ptr;
258
ptr->prev = &(set_list[set2]);
259
set_list[set2].next = ptr;
261
/* Pull int_list[set2] out of its list to be used later. */
264
if (int_list[set2].val >= 0) {
265
int_list[set2].val = -(int_list[set2].val + 1);
266
if (int_list[set2].next != NULL) {
267
int_list[set2].next->prev = int_list[set2].prev;
269
if (int_list[set2].prev != NULL) {
270
int_list[set2].prev->next = int_list[set2].next;
273
int_list[set2].next = changed_sets;
274
changed_sets = &(int_list[set2]);
278
if (int_list[set1].val >= 0) {
279
if (int_list[set1].next != NULL) {
280
int_list[set1].next->prev = int_list[set1].prev;
282
if (int_list[set1].prev != NULL) {
283
int_list[set1].prev->next = int_list[set1].next;
286
int_list[set1].next = changed_sets;
287
changed_sets = &(int_list[set1]);
292
/* Finally, update internal node calculations for all modified sets. */
293
while (changed_sets != NULL) {
294
set2 = ((int) (changed_sets - int_list)) / size;
295
changed_sets = changed_sets->next;
297
/* Next line uses fact that list has dummy header so prev isn't NULL. */
298
int_list[set2].next = int_list[set2].prev->next;
299
int_list[set2].val = 0;
300
/* Recompute internal nodes for this set */
301
for (ptr = set_list[set2].next; ptr != NULL; ptr = ptr->next) {
302
vtx = ((int) (ptr - vtx_elems)) / size;
304
for (j = 1; j < graph[vtx]->nedges && internal; j++) {
305
set3 = assign[graph[vtx]->edges[j]];
306
internal = (set3 == set2);
309
int_list[set2].val += graph[vtx]->vwgt;
313
/* Now move internal value in doubly linked list. */
314
/* Move higher in list? */
315
while (int_list[set2].next != NULL &&
316
int_list[set2].val >= int_list[set2].next->val) {
317
int_list[set2].prev = int_list[set2].next;
318
int_list[set2].next = int_list[set2].next->next;
320
/* Move lower in list? */
321
while (int_list[set2].prev != NULL &&
322
int_list[set2].val < int_list[set2].prev->val) {
323
int_list[set2].next = int_list[set2].prev;
324
int_list[set2].prev = int_list[set2].prev->prev;
327
if (int_list[set2].next != NULL)
328
int_list[set2].next->prev = &(int_list[set2]);
329
if (int_list[set2].prev != NULL)
330
int_list[set2].prev->next = &(int_list[set2]);