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. */
12
Keep guys moved in and guys moving out of separator.
14
To restore, move all undesirable guys back.
15
(1) guys moved out of separator get put back in.
16
(2) guys moved into separator (bspace) get put back.
17
Note: Should be done in this order.
18
Note: No neighbors need be considered.
20
(3) To clear dvals, I should compute touch all guys that
21
were ever in separator (bspace) and their neighbors.
25
int nway_klv(graph, nvtxs, lbuckets, rbuckets, llistspace, rlistspace,
26
ldvals, rdvals, sets, maxdval, goal, max_dev, bndy_list,
28
struct vtx_data **graph; /* data structure for graph */
29
int nvtxs; /* number of vtxs in graph */
30
struct bilist **lbuckets; /* array of lists for bucket sort */
31
struct bilist **rbuckets; /* array of lists for bucket sort */
32
struct bilist *llistspace; /* list data structure for each vertex */
33
struct bilist *rlistspace; /* list data structure for each vertex */
34
int *ldvals; /* d-values for each transition */
35
int *rdvals; /* d-values for each transition */
36
short *sets; /* processor each vertex is assigned to */
37
int maxdval; /* maximum d-value for a vertex */
38
double *goal; /* desired set sizes */
39
int max_dev; /* largest allowed deviation from balance */
40
int **bndy_list; /* list of vertices on boundary (0 ends) */
41
double *weightsum; /* sum of vweights in each set (in and out) */
43
struct bilist **to_buckets; /* buckets I'm moving to */
44
struct bilist **from_buckets; /* buckets I'm moving from */
45
struct bilist *to_listspace;/* list structure I'm moving to */
46
struct bilist *from_listspace; /* list structure I'm moving from */
47
struct bilist *out_list; /* list of vtxs moved out of separator */
48
int *to_dvals; /* d-values I'm moving to */
49
int *from_dvals; /* d-values I'm moving from */
50
extern double kl_bucket_time; /* time spent in KL bucketsort */
51
extern int KL_BAD_MOVES; /* # bad moves in a row to stop KL */
52
extern int DEBUG_KL; /* debug flag for KL */
53
extern int KL_NTRIES_BAD; /* number of unhelpful passes before quitting */
54
extern int KL_MAX_PASS; /* maximum # outer KL loops */
55
int *bspace; /* list of active vertices for bucketsort */
56
int *edges; /* edge list for a vertex */
57
int *edges2; /* edge list for a vertex */
58
int *bdy_ptr; /* loops through bndy_list */
59
double total_weight; /* weight of all vertices */
60
double partial_weight; /* weight of vertices not in separator */
61
double ratio; /* fraction of weight not in separator */
62
double time; /* timing parameter */
63
double delta0; /* largest negative deviation from goal size */
64
double delta1; /* largest negative deviation from goal size */
65
double left_imbalance; /* imbalance if I move to the left */
66
double right_imbalance; /* imbalance if I move to the right */
67
double balance_val; /* how imbalanced is it */
68
double balance_best; /* best balance yet if trying hard */
69
int flag; /* condition indicator */
70
int to, from; /* sets moving into / out of */
71
int rtop, ltop; /* top of each set of buckets */
72
int *to_top; /* ptr to top of set moving to */
73
int lvtx, rvtx; /* next vertex to move left/right */
74
int lweight, rweight; /* weights of moving vertices */
75
int weightfrom; /* weight moving out of a set */
76
int list_length; /* how long is list of vertices to bucketsort? */
77
int balanced; /* is partition balanced? */
78
int temp_balanced; /* is intermediate partition balanced? */
79
int ever_balanced; /* has any partition been balanced? */
80
int bestvtx; /* best vertex to move */
81
int bestval; /* best change in value for a vtx move */
82
int vweight; /* weight of best vertex */
83
int gtotal; /* sum of changes from moving */
84
int improved; /* total improvement from KL */
85
double bestg; /* maximum gtotal found in KL loop */
86
double bestg_min; /* smaller than any possible bestg */
87
int beststep; /* step where maximum value occurred */
88
int bestlength; /* step where maximum value occurred */
89
int neighbor; /* neighbor of a vertex */
90
int step_cutoff; /* number of negative steps in a row allowed */
91
int cost_cutoff; /* amount of negative d-values allowed */
92
int neg_steps; /* number of negative steps in a row */
93
int neg_cost; /* decrease in sum of d-values */
94
int vtx; /* vertex number */
95
int dval; /* dval of a vertex */
96
int group, group2; /* set that a vertex is assigned to */
97
int left_too_big; /* is left set too large? */
98
int right_too_big; /* is right set too large? */
99
int vwgt; /* weight of a vertex */
100
int gain; /* reduction in separator due to a move */
101
int neighbor2; /* neighbor of a vertex */
102
int step; /* loops through movements of vertices */
103
int parity; /* sort forwards or backwards? */
104
int done; /* has termination criteria been achieved? */
105
int nbad; /* number of unhelpful passes in a row */
106
int npass; /* total number of passes */
107
int nbadtries; /* number of unhelpful passes before quitting */
108
int enforce_balance; /* force a balanced partition? */
109
int enforce_balance_hard; /* really force a balanced partition? */
110
int i, j, k; /* loop counters */
112
double seconds(), drandom();
114
void bucketsortsv(), clear_dvals(), p1bucket();
115
void removebilist(), movebilist(), add2bilist();
117
nbadtries = KL_NTRIES_BAD;
119
enforce_balance = FALSE;
120
enforce_balance_hard = FALSE;
122
total_weight = goal[0] + goal[1];
124
bspace = smalloc_ret((nvtxs + 1) * sizeof(int));
126
if (bspace == NULL) {
130
bdy_ptr = *bndy_list;
132
while (*bdy_ptr != 0) {
133
bspace[list_length++] = *bdy_ptr++;
138
clear_dvals(graph, nvtxs, ldvals, rdvals, bspace, list_length);
140
step_cutoff = KL_BAD_MOVES;
141
cost_cutoff = maxdval * step_cutoff / 7;
142
if (cost_cutoff < step_cutoff)
143
cost_cutoff = step_cutoff;
145
partial_weight = weightsum[0] + weightsum[1];
146
ratio = partial_weight / total_weight;
147
delta0 = fabs(weightsum[0] - goal[0] * ratio);
148
delta1 = fabs(weightsum[1] - goal[1] * ratio);
149
balanced = (delta0 + delta1 <= max_dev) &&
150
weightsum[0] != total_weight &&
151
weightsum[1] != total_weight;
153
bestg_min = -2.0 * nvtxs * maxdval;
161
ever_balanced = FALSE;
162
balance_best = delta0 + delta1;
164
/* Initialize various quantities. */
165
ltop = rtop = 2 * maxdval;
170
bestlength = list_length;
175
/* Compute the initial d-values, and bucket-sort them. */
177
bucketsortsv(graph, nvtxs, lbuckets, rbuckets, llistspace,
178
rlistspace, ldvals, rdvals, sets, maxdval, parity,
179
bspace, list_length);
181
kl_bucket_time += seconds() - time;
184
printf("After sorting, left buckets:\n");
185
p1bucket(lbuckets, llistspace, maxdval);
186
printf(" right buckets:\n");
187
p1bucket(rbuckets, rlistspace, maxdval);
190
/* Now determine the set of vertex moves. */
192
for (step = 1;; step++) {
194
/* Find the highest d-value in each set. */
195
/* But only consider moves from large to small sets, or moves */
196
/* in which balance is preserved. */
197
/* Break ties in some nonarbitrary manner. */
198
bestval = -maxdval - 1;
200
partial_weight = weightsum[0] + weightsum[1];
201
ratio = partial_weight / total_weight;
202
left_too_big = (weightsum[0] > (goal[0] + .5 * max_dev) * ratio);
203
right_too_big = (weightsum[1] > (goal[1] + .5 * max_dev) * ratio);
205
while (ltop >= 0 && lbuckets[ltop] == NULL) {
208
if (ltop >= 0 && !left_too_big) {
209
lvtx = ((long) lbuckets[ltop] - (long) llistspace) /
210
sizeof(struct bilist);
211
lweight = graph[lvtx]->vwgt;
212
rweight = lweight - (ltop - maxdval);
213
weightfrom = rweight;
216
bestval = ltop - maxdval;
217
partial_weight = weightsum[0] + lweight +
218
weightsum[1] - rweight;
219
ratio = partial_weight / total_weight;
220
left_imbalance = max(fabs(weightsum[0] + lweight - goal[0] * ratio),
221
fabs(weightsum[1] - rweight - goal[1] * ratio));
224
while (rtop >= 0 && rbuckets[rtop] == NULL) {
227
if (rtop >= 0 && !right_too_big) {
228
rvtx = ((long) rbuckets[rtop] - (long) rlistspace) /
229
sizeof(struct bilist);
230
rweight = graph[rvtx]->vwgt;
231
lweight = rweight - (rtop - maxdval);
232
partial_weight = weightsum[0] - lweight +
233
weightsum[1] + rweight;
234
ratio = partial_weight / total_weight;
235
right_imbalance = max(fabs(weightsum[0] - lweight - goal[0] * ratio),
236
fabs(weightsum[1] + rweight - goal[1] * ratio));
237
if (rtop - maxdval > bestval ||
238
(rtop - maxdval == bestval &&
239
(right_imbalance < left_imbalance ||
240
(right_imbalance == left_imbalance &&
243
weightfrom = lweight;
245
bestval = rtop - maxdval;
249
if (bestval == -maxdval - 1) { /* No allowed moves */
251
printf("No KLV moves at step %d. bestg = %g at step %d.\n",
252
step, bestg, beststep);
259
to_listspace = llistspace;
260
from_listspace = rlistspace;
263
to_buckets = lbuckets;
264
from_buckets = rbuckets;
269
to_listspace = rlistspace;
270
from_listspace = llistspace;
273
to_buckets = rbuckets;
274
from_buckets = lbuckets;
278
vweight = graph[bestvtx]->vwgt;
280
weightsum[to] += vweight;
281
weightsum[from] -= weightfrom;
283
/* Check if this partition is balanced. */
284
partial_weight = weightsum[0] + weightsum[1];
285
ratio = partial_weight / total_weight;
286
delta0 = fabs(weightsum[0] - goal[0] * ratio);
287
delta1 = fabs(weightsum[1] - goal[1] * ratio);
288
temp_balanced = (delta0 + delta1 <= max_dev) &&
289
weightsum[0] != total_weight &&
290
weightsum[1] != total_weight;
291
ever_balanced = (ever_balanced || temp_balanced);
292
balance_val = delta0 + delta1;
296
if ((gtotal > bestg && temp_balanced) ||
297
(enforce_balance_hard && balance_val < balance_best)) {
300
if (balance_val < balance_best) {
301
balance_best = balance_val;
304
enforce_balance_hard = FALSE;
309
printf("At KLV step %d, bestvtx=%d, bestval=%d (2->%d), wt0 = %g, wt1 = %g\n",
310
step, bestvtx, bestval, to, weightsum[0], weightsum[1]);
313
/* Monitor the stopping criteria. */
315
if (!enforce_balance || ever_balanced)
317
if (bestg != bestg_min)
318
neg_cost = bestg - gtotal;
320
neg_cost = -maxdval - 1;
321
if ((neg_steps > step_cutoff || neg_cost > cost_cutoff) &&
322
!(enforce_balance && bestg == bestg_min)) {
324
if (neg_steps > step_cutoff) {
325
printf("KLV step cutoff at step %d. bestg = %g at step %d.\n",
326
step, bestg, beststep);
328
else if (neg_cost > cost_cutoff) {
329
printf("KLV cost cutoff at step %d. bestg = %g at step %d.\n",
330
step, bestg, beststep);
334
weightsum[to] -= vweight;
335
weightsum[from] += weightfrom;
339
else if (bestval > 0) {
343
/* Remove vertex from its buckets, and flag it as finished. */
345
removebilist(&to_listspace[bestvtx],
346
&to_buckets[bestval + maxdval]);
348
printf("After to removebilist\n");
349
p1bucket(to_buckets, to_listspace, maxdval);
352
if (from_dvals[bestvtx] != -maxdval - 1) {
353
removebilist(&from_listspace[bestvtx],
354
&from_buckets[from_dvals[bestvtx] + maxdval]);
356
printf("After from removebilist\n");
357
p1bucket(from_buckets, from_listspace, maxdval);
360
from_dvals[bestvtx] = -maxdval - 1;
362
/* Now keep track of vertices moved out of separator so */
363
/* I can restore them as needed. */
364
llistspace[bestvtx].next = out_list;
365
out_list = &(llistspace[bestvtx]);
368
/* Now update the d-values of all the neighbors */
369
/* And neighbors of neighbors ... */
372
1. Separator neighbors right gain => infinity
373
2. Left neighbors unaffected.
374
3. Right neighbors move into separator.
375
A. Right gain = infinity.
376
B. Left gain = computed.
377
C. For any of their neighbors in separator increase left gain.
380
edges = graph[bestvtx]->edges;
381
for (j = graph[bestvtx]->nedges - 1; j; j--) {
382
neighbor = *(++edges);
384
group = sets[neighbor];
386
if (group == 2) { /* In separator. */
387
gain = from_dvals[neighbor] + maxdval;
388
/* Gain in the from direction => -infinity */
390
removebilist(&from_listspace[neighbor], &from_buckets[gain]);
392
printf("\n After removing %d\n", neighbor);
393
p1bucket(from_buckets, from_listspace, maxdval);
395
from_dvals[neighbor] = -maxdval - 1;
398
else if (group == from) {
399
/* Gain in the from direction => -infinity */
401
from_dvals[neighbor] = -maxdval - 1;
404
bspace[list_length++] = -neighbor;
407
bspace[list_length++] = neighbor;
410
edges2 = graph[neighbor]->edges;
411
vwgt = graph[neighbor]->vwgt;
412
gain = graph[neighbor]->vwgt;
414
for (k = graph[neighbor]->nedges - 1; k; k--) {
415
neighbor2 = *(++edges2);
416
group2 = sets[neighbor2];
418
dval = to_dvals[neighbor2] + maxdval;
420
movebilist(&to_listspace[neighbor2],
422
&to_buckets[dval + vwgt]);
424
printf("\n After moving %d from bucket %d to bucket %d\n", neighbor2, dval, dval + vwgt);
425
p1bucket(to_buckets, to_listspace, maxdval);
427
to_dvals[neighbor2] += vwgt;
429
if (dval > *to_top) {
434
else if (group2 == from) {
435
gain -= graph[neighbor2]->vwgt;
436
if (to_dvals[neighbor2] + maxdval < 0) {
442
if (flag) { /* Not allowed to move further. */
443
to_dvals[neighbor] = -maxdval - 1;
446
to_dvals[neighbor] = gain;
447
/* place in appropriate bucket */
450
add2bilist(&to_listspace[neighbor], &to_buckets[gain]);
452
printf("\nAfter adding %d to bucket %d\n", neighbor, gain - maxdval);
453
p1bucket(to_buckets, to_listspace, maxdval);
462
if (beststep == step) {
463
bestlength = list_length;
466
printf("\n-- After step, left buckets:\n");
467
p1bucket(lbuckets, llistspace, maxdval);
468
printf(" right buckets:\n");
469
p1bucket(rbuckets, rlistspace, maxdval);
474
/* Done with a pass; should we actually perform any swaps? */
475
if (bestg > 0 || (bestg != bestg_min && !balanced && enforce_balance)) {
479
if (enforce_balance_hard) {
480
/* I've done the best I can, give up. */
483
if (enforce_balance) {
484
enforce_balance_hard = TRUE;
486
enforce_balance = TRUE;
490
/* Work backwards, undoing all the undesirable moves. */
492
/* First reset vertices moved out of the separator. */
496
for (i = step - 1; i > beststep; i--) {
497
vtx = ((long) out_list - (long) llistspace) /
498
sizeof(struct bilist);
499
if (sets[vtx] != 2) {
500
weightsum[sets[vtx]] -= graph[vtx]->vwgt;
503
out_list = out_list->next;
507
for (i = list_length - 1; i >= bestlength; i--) {
510
if (sets[-vtx] == 2) {
511
weightsum[1] += graph[-vtx]->vwgt;
516
if (sets[vtx] == 2) {
517
weightsum[0] += graph[vtx]->vwgt;
523
partial_weight = weightsum[0] + weightsum[1];
524
ratio = partial_weight / total_weight;
525
delta0 = fabs(weightsum[0] - goal[0] * ratio);
526
delta1 = fabs(weightsum[1] - goal[1] * ratio);
527
balanced = (delta0 + delta1 <= max_dev) &&
528
weightsum[0] != total_weight &&
529
weightsum[1] != total_weight;
531
done = done || (nbad >= nbadtries && balanced);
532
if (KL_MAX_PASS > 0) {
533
done = done || (npass == KL_MAX_PASS && balanced);
536
if (!done) { /* Rezero dval values. */
537
clear_dvals(graph, nvtxs, ldvals, rdvals, bspace, list_length);
540
/* Construct list of separator vertices to pass to buckets or return */
541
list_length = make_sep_list(bspace, list_length, sets);
544
bspace[list_length] = 0;
545
bspace = srealloc(bspace, (list_length + 1) * sizeof(int));
551
for (i = 1; i <= nvtxs; i++) {
554
else if (sets[i] == 1)
556
else if (sets[i] == 2)
557
gain += graph[i]->vwgt;
560
printf("\nAfter pass of KLV: sets = %d/%d, sep = %d (bestg = %g)\n\n\n",
566
printf(" KLV required %d passes to improve by %d.\n", npass, improved);