1 /*
2  * Copyright 1997, Regents of the University of Minnesota
3  *
4  * mbalance2.c
5  *
6  * This file contains code that is used to forcefully balance either
7  * bisections or k-sections
8  *
9  * Started 7/29/97
10  * George
11  *
12  * $Id: mbalance2.c,v 1.1 1998/11/27 17:59:19 karypis Exp $
13  *
14  */
15 
16 #include <metis.h>
17 
18 
19 /*************************************************************************
20 * This function is the entry point of the bisection balancing algorithms.
21 **************************************************************************/
MocBalance2Way2(CtrlType * ctrl,GraphType * graph,float * tpwgts,float * ubvec)22 void MocBalance2Way2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
23 {
24   int i;
25   float tvec[MAXNCON];
26 
27   Compute2WayHLoadImbalanceVec(graph->ncon, graph->npwgts, tpwgts, tvec);
28   if (!AreAllBelow(graph->ncon, tvec, ubvec))
29     MocGeneral2WayBalance2(ctrl, graph, tpwgts, ubvec);
30 }
31 
32 
33 
34 /*************************************************************************
35 * This function performs an edge-based FM refinement
36 **************************************************************************/
MocGeneral2WayBalance2(CtrlType * ctrl,GraphType * graph,float * tpwgts,float * ubvec)37 void MocGeneral2WayBalance2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
38 {
39   int i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum;
40   idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind;
41   idxtype *moved, *swaps, *perm, *qnum;
42   float *nvwgt, *npwgts, origbal[MAXNCON], minbal[MAXNCON], newbal[MAXNCON];
43   PQueueType parts[MAXNCON][2];
44   int higain, oldgain, mincut, newcut, mincutorder;
45   float *maxwgt, *minwgt, tvec[MAXNCON];
46 
47 
48   nvtxs = graph->nvtxs;
49   ncon = graph->ncon;
50   xadj = graph->xadj;
51   nvwgt = graph->nvwgt;
52   adjncy = graph->adjncy;
53   adjwgt = graph->adjwgt;
54   where = graph->where;
55   id = graph->id;
56   ed = graph->ed;
57   npwgts = graph->npwgts;
58   bndptr = graph->bndptr;
59   bndind = graph->bndind;
60 
61   moved = idxwspacemalloc(ctrl, nvtxs);
62   swaps = idxwspacemalloc(ctrl, nvtxs);
63   perm = idxwspacemalloc(ctrl, nvtxs);
64   qnum = idxwspacemalloc(ctrl, nvtxs);
65 
66   limit = amin(amax(0.01*nvtxs, 15), 100);
67 
68   /* Setup the weight intervals of the two subdomains */
69   minwgt = fwspacemalloc(ctrl, 2*ncon);
70   maxwgt = fwspacemalloc(ctrl, 2*ncon);
71 
72   for (i=0; i<2; i++) {
73     for (j=0; j<ncon; j++) {
74       maxwgt[i*ncon+j] = tpwgts[i]*ubvec[j];
75       minwgt[i*ncon+j] = tpwgts[i]*(1.0/ubvec[j]);
76     }
77   }
78 
79 
80   /* Initialize the queues */
81   for (i=0; i<ncon; i++) {
82     PQueueInit(ctrl, &parts[i][0], nvtxs, PLUS_GAINSPAN+1);
83     PQueueInit(ctrl, &parts[i][1], nvtxs, PLUS_GAINSPAN+1);
84   }
85   for (i=0; i<nvtxs; i++)
86     qnum[i] = samax(ncon, nvwgt+i*ncon);
87 
88   Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, origbal);
89   for (i=0; i<ncon; i++)
90     minbal[i] = origbal[i];
91 
92   newcut = mincut = graph->mincut;
93   mincutorder = -1;
94 
95   if (ctrl->dbglvl&DBG_REFINE) {
96     printf("Parts: [");
97     for (l=0; l<ncon; l++)
98       printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
99     printf("] T[%.3f %.3f], Nv-Nb[%5d, %5d]. ICut: %6d, LB: ", tpwgts[0], tpwgts[1],
100             graph->nvtxs, graph->nbnd, graph->mincut);
101     for (i=0; i<ncon; i++)
102       printf("%.3f ", origbal[i]);
103     printf("[B]\n");
104   }
105 
106   idxset(nvtxs, -1, moved);
107 
108   ASSERT(ComputeCut(graph, where) == graph->mincut);
109   ASSERT(CheckBnd(graph));
110 
111   /* Insert all nodes in the priority queues */
112   nbnd = graph->nbnd;
113   RandomPermute(nvtxs, perm, 1);
114   for (ii=0; ii<nvtxs; ii++) {
115     i = perm[ii];
116     PQueueInsert(&parts[qnum[i]][where[i]], i, ed[i]-id[i]);
117   }
118 
119 
120   for (nswaps=0; nswaps<nvtxs; nswaps++) {
121     if (AreAllBelow(ncon, minbal, ubvec))
122       break;
123 
124     SelectQueue3(ncon, npwgts, tpwgts, &from, &cnum, parts, maxwgt);
125     to = (from+1)%2;
126 
127     if (from == -1 || (higain = PQueueGetMax(&parts[cnum][from])) == -1)
128       break;
129 
130     saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
131     saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
132     newcut -= (ed[higain]-id[higain]);
133     Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, newbal);
134 
135     if (IsBetter2wayBalance(ncon, newbal, minbal, ubvec) ||
136         (IsBetter2wayBalance(ncon, newbal, origbal, ubvec) && newcut < mincut)) {
137       mincut = newcut;
138       for (i=0; i<ncon; i++)
139         minbal[i] = newbal[i];
140       mincutorder = nswaps;
141     }
142     else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */
143       newcut += (ed[higain]-id[higain]);
144       saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
145       saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
146       break;
147     }
148 
149     where[higain] = to;
150     moved[higain] = nswaps;
151     swaps[nswaps] = higain;
152 
153     if (ctrl->dbglvl&DBG_MOVEINFO) {
154       printf("Moved %6d from %d(%d). Gain: %5d, Cut: %5d, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut);
155       for (i=0; i<ncon; i++)
156         printf("(%.3f, %.3f) ", npwgts[i], npwgts[ncon+i]);
157 
158       Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, tvec);
159       printf(", LB: ");
160       for (i=0; i<ncon; i++)
161         printf("%.3f ", tvec[i]);
162       if (mincutorder == nswaps)
163         printf(" *\n");
164       else
165         printf("\n");
166     }
167 
168 
169     /**************************************************************
170     * Update the id[i]/ed[i] values of the affected nodes
171     ***************************************************************/
172     SWAP(id[higain], ed[higain], tmp);
173     if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1])
174       BNDDelete(nbnd, bndind,  bndptr, higain);
175     if (ed[higain] > 0 && bndptr[higain] == -1)
176       BNDInsert(nbnd, bndind,  bndptr, higain);
177 
178     for (j=xadj[higain]; j<xadj[higain+1]; j++) {
179       k = adjncy[j];
180       oldgain = ed[k]-id[k];
181 
182       kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
183       INC_DEC(id[k], ed[k], kwgt);
184 
185       /* Update the queue position */
186       if (moved[k] == -1)
187         PQueueUpdate(&parts[qnum[k]][where[k]], k, oldgain, ed[k]-id[k]);
188 
189       /* Update its boundary information */
190       if (ed[k] == 0 && bndptr[k] != -1)
191         BNDDelete(nbnd, bndind, bndptr, k);
192       else if (ed[k] > 0 && bndptr[k] == -1)
193         BNDInsert(nbnd, bndind, bndptr, k);
194     }
195 
196   }
197 
198 
199 
200   /****************************************************************
201   * Roll back computations
202   *****************************************************************/
203   for (i=0; i<nswaps; i++)
204     moved[swaps[i]] = -1;  /* reset moved array */
205   for (nswaps--; nswaps>mincutorder; nswaps--) {
206     higain = swaps[nswaps];
207 
208     to = where[higain] = (where[higain]+1)%2;
209     SWAP(id[higain], ed[higain], tmp);
210     if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1])
211       BNDDelete(nbnd, bndind,  bndptr, higain);
212     else if (ed[higain] > 0 && bndptr[higain] == -1)
213       BNDInsert(nbnd, bndind,  bndptr, higain);
214 
215     saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
216     saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1);
217     for (j=xadj[higain]; j<xadj[higain+1]; j++) {
218       k = adjncy[j];
219 
220       kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
221       INC_DEC(id[k], ed[k], kwgt);
222 
223       if (bndptr[k] != -1 && ed[k] == 0)
224         BNDDelete(nbnd, bndind, bndptr, k);
225       if (bndptr[k] == -1 && ed[k] > 0)
226         BNDInsert(nbnd, bndind, bndptr, k);
227     }
228   }
229 
230   if (ctrl->dbglvl&DBG_REFINE) {
231     printf("\tMincut: %6d at %5d, NBND: %6d, NPwgts: [", mincut, mincutorder, nbnd);
232     for (i=0; i<ncon; i++)
233       printf("(%.3f, %.3f) ", npwgts[i], npwgts[ncon+i]);
234     printf("], LB: ");
235     Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, tvec);
236     for (i=0; i<ncon; i++)
237       printf("%.3f ", tvec[i]);
238     printf("\n");
239   }
240 
241   graph->mincut = mincut;
242   graph->nbnd = nbnd;
243 
244 
245   for (i=0; i<ncon; i++) {
246     PQueueFree(ctrl, &parts[i][0]);
247     PQueueFree(ctrl, &parts[i][1]);
248   }
249 
250   idxwspacefree(ctrl, nvtxs);
251   idxwspacefree(ctrl, nvtxs);
252   idxwspacefree(ctrl, nvtxs);
253   idxwspacefree(ctrl, nvtxs);
254   fwspacefree(ctrl, 2*ncon);
255   fwspacefree(ctrl, 2*ncon);
256 
257 }
258 
259 
260 
261 
262 /*************************************************************************
263 * This function selects the partition number and the queue from which
264 * we will move vertices out
265 **************************************************************************/
SelectQueue3(int ncon,float * npwgts,float * tpwgts,int * from,int * cnum,PQueueType queues[MAXNCON][2],float * maxwgt)266 void SelectQueue3(int ncon, float *npwgts, float *tpwgts, int *from, int *cnum,
267        PQueueType queues[MAXNCON][2], float *maxwgt)
268 {
269   int i, j, maxgain=0;
270   float maxdiff=0.0, diff;
271 
272   *from = -1;
273   *cnum = -1;
274 
275   /* First determine the side and the queue, irrespective of the presence of nodes */
276   for (j=0; j<2; j++) {
277     for (i=0; i<ncon; i++) {
278       diff = npwgts[j*ncon+i]-maxwgt[j*ncon+i];
279       if (diff >= maxdiff) {
280         maxdiff = diff;
281         *from = j;
282         *cnum = i;
283       }
284     }
285   }
286 
287 /* DELETE
288 j = *from;
289 for (i=0; i<ncon; i++)
290   printf("[%5d %5d %.4f %.4f] ", i, PQueueGetSize(&queues[i][j]), npwgts[j*ncon+i], maxwgt[j*ncon+i]);
291 printf("***[%5d %5d]\n", *cnum, *from);
292 */
293 
294   /* If the desired queue is empty, select a node from that side anyway */
295   if (*from != -1 && PQueueGetSize(&queues[*cnum][*from]) == 0) {
296     for (i=0; i<ncon; i++) {
297       if (PQueueGetSize(&queues[i][*from]) > 0) {
298         maxdiff = (npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i]);
299         *cnum = i;
300         break;
301       }
302     }
303 
304     for (i++; i<ncon; i++) {
305       diff = npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i];
306       if (diff > maxdiff && PQueueGetSize(&queues[i][*from]) > 0) {
307         maxdiff = diff;
308         *cnum = i;
309       }
310     }
311   }
312 
313   /* If the constraints ar OK, select a high gain vertex */
314   if (*from == -1) {
315     maxgain = -100000;
316     for (j=0; j<2; j++) {
317       for (i=0; i<ncon; i++) {
318         if (PQueueGetSize(&queues[i][j]) > 0 && PQueueGetKey(&queues[i][j]) > maxgain) {
319           maxgain = PQueueGetKey(&queues[i][0]);
320           *from = j;
321           *cnum = i;
322         }
323       }
324     }
325 
326     /* printf("(%2d %2d) %3d\n", *from, *cnum, maxgain); */
327   }
328 }
329