1 /*!
2 \file
3 \brief Routines for k-way refinement
4 
5 \date Started 7/28/97
6 \author George
7 \author Copyright 1997-2009, Regents of the University of Minnesota
8 \version $Id: kwayfm.c 10567 2011-07-13 16:17:07Z karypis $
9 */
10 
11 #include "metislib.h"
12 
13 
14 
15 /*************************************************************************/
16 /* Top-level routine for k-way partitioning refinement. This routine just
17    calls the appropriate refinement routine based on the objectives and
18    constraints. */
19 /*************************************************************************/
Greedy_KWayOptimize(ctrl_t * ctrl,graph_t * graph,idx_t niter,real_t ffactor,idx_t omode)20 void Greedy_KWayOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter,
21          real_t ffactor, idx_t omode)
22 {
23   switch (ctrl->objtype) {
24     case METIS_OBJTYPE_CUT:
25       if (graph->ncon == 1)
26         Greedy_KWayCutOptimize(ctrl, graph, niter, ffactor, omode);
27       else
28         Greedy_McKWayCutOptimize(ctrl, graph, niter, ffactor, omode);
29       break;
30 
31     case METIS_OBJTYPE_VOL:
32       if (graph->ncon == 1)
33         Greedy_KWayVolOptimize(ctrl, graph, niter, ffactor, omode);
34       else
35         Greedy_McKWayVolOptimize(ctrl, graph, niter, ffactor, omode);
36       break;
37 
38     default:
39       gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype);
40   }
41 }
42 
43 
44 /*************************************************************************/
45 /*! K-way partitioning optimization in which the vertices are visited in
46     decreasing ed/sqrt(nnbrs)-id order. Note this is just an
47     approximation, as the ed is often split across different subdomains
48     and the sqrt(nnbrs) is just a crude approximation.
49 
50   \param graph is the graph that is being refined.
51   \param niter is the number of refinement iterations.
52   \param ffactor is the \em fudge-factor for allowing positive gain moves
53          to violate the max-pwgt constraint.
54   \param omode is the type of optimization that will performed among
55          OMODE_REFINE and OMODE_BALANCE
56 
57 
58 */
59 /**************************************************************************/
Greedy_KWayCutOptimize(ctrl_t * ctrl,graph_t * graph,idx_t niter,real_t ffactor,idx_t omode)60 void Greedy_KWayCutOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter,
61          real_t ffactor, idx_t omode)
62 {
63   /* Common variables to all types of kway-refinement/balancing routines */
64   idx_t i, ii, iii, j, k, l, pass, nvtxs, nparts, gain;
65   idx_t from, me, to, oldcut, vwgt;
66   idx_t *xadj, *adjncy, *adjwgt;
67   idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts;
68   idx_t nmoved, nupd, *vstatus, *updptr, *updind;
69   idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL;
70   idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL;
71   idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE);
72 
73   /* Edgecut-specific/different variables */
74   idx_t nbnd, oldnnbrs;
75   rpq_t *queue;
76   real_t rgain;
77   ckrinfo_t *myrinfo;
78   cnbr_t *mynbrs;
79 
80   WCOREPUSH;
81 
82   /* Link the graph fields */
83   nvtxs  = graph->nvtxs;
84   xadj   = graph->xadj;
85   adjncy = graph->adjncy;
86   adjwgt = graph->adjwgt;
87 
88   bndind = graph->bndind;
89   bndptr = graph->bndptr;
90 
91   where = graph->where;
92   pwgts = graph->pwgts;
93 
94   nparts = ctrl->nparts;
95 
96   /* Setup the weight intervals of the various subdomains */
97   minwgt  = iwspacemalloc(ctrl, nparts);
98   maxwgt  = iwspacemalloc(ctrl, nparts);
99   itpwgts = iwspacemalloc(ctrl, nparts);
100 
101   for (i=0; i<nparts; i++) {
102     itpwgts[i] = ctrl->tpwgts[i]*graph->tvwgt[0];
103     maxwgt[i]  = ctrl->tpwgts[i]*graph->tvwgt[0]*ctrl->ubfactors[0];
104     minwgt[i]  = ctrl->tpwgts[i]*graph->tvwgt[0]*(1.0/ctrl->ubfactors[0]);
105   }
106 
107   perm = iwspacemalloc(ctrl, nvtxs);
108 
109 
110   /* This stores the valid target subdomains. It is used when ctrl->minconn to
111      control the subdomains to which moves are allowed to be made.
112      When ctrl->minconn is false, the default values of 2 allow all moves to
113      go through and it does not interfere with the zero-gain move selection. */
114   safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts));
115 
116   if (ctrl->minconn) {
117     ComputeSubDomainGraph(ctrl, graph);
118 
119     nads    = ctrl->nads;
120     adids   = ctrl->adids;
121     adwgts  = ctrl->adwgts;
122     doms    = iset(nparts, 0, ctrl->pvec1);
123   }
124 
125 
126   /* Setup updptr, updind like boundary info to keep track of the vertices whose
127      vstatus's need to be reset at the end of the inner iteration */
128   vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs));
129   updptr  = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs));
130   updind  = iwspacemalloc(ctrl, nvtxs);
131 
132   if (ctrl->contig) {
133     /* The arrays that will be used for limited check of articulation points */
134     bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
135     bfsind = iwspacemalloc(ctrl, nvtxs);
136     bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
137   }
138 
139   if (ctrl->dbglvl&METIS_DBG_REFINE) {
140      printf("%s: [%6"PRIDX" %6"PRIDX"]-[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL","
141             " Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %6"PRIDX,
142             (omode == OMODE_REFINE ? "GRC" : "GBC"),
143             pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts), minwgt[0], maxwgt[0],
144             ComputeLoadImbalance(graph, nparts, ctrl->pijbm),
145             graph->nvtxs, graph->nbnd, graph->mincut);
146      if (ctrl->minconn)
147        printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
148      printf("\n");
149   }
150 
151   queue = rpqCreate(nvtxs);
152 
153   /*=====================================================================
154   * The top-level refinement loop
155   *======================================================================*/
156   for (pass=0; pass<niter; pass++) {
157     ASSERT(ComputeCut(graph, where) == graph->mincut);
158 
159     if (omode == OMODE_BALANCE) {
160       /* Check to see if things are out of balance, given the tolerance */
161       for (i=0; i<nparts; i++) {
162         if (pwgts[i] > maxwgt[i])
163           break;
164       }
165       if (i == nparts) /* Things are balanced. Return right away */
166         break;
167     }
168 
169     oldcut = graph->mincut;
170     nbnd   = graph->nbnd;
171     nupd   = 0;
172 
173     if (ctrl->minconn)
174       maxndoms = imax(nparts, nads);
175 
176     /* Insert the boundary vertices in the priority queue */
177     irandArrayPermute(nbnd, perm, nbnd/4, 1);
178     for (ii=0; ii<nbnd; ii++) {
179       i = bndind[perm[ii]];
180       rgain = (graph->ckrinfo[i].nnbrs > 0 ?
181                1.0*graph->ckrinfo[i].ed/sqrt(graph->ckrinfo[i].nnbrs) : 0.0)
182                - graph->ckrinfo[i].id;
183       rpqInsert(queue, i, rgain);
184       vstatus[i] = VPQSTATUS_PRESENT;
185       ListInsert(nupd, updind, updptr, i);
186     }
187 
188     /* Start extracting vertices from the queue and try to move them */
189     for (nmoved=0, iii=0;;iii++) {
190       if ((i = rpqGetTop(queue)) == -1)
191         break;
192       vstatus[i] = VPQSTATUS_EXTRACTED;
193 
194       myrinfo = graph->ckrinfo+i;
195       mynbrs  = ctrl->cnbrpool + myrinfo->inbr;
196 
197       from = where[i];
198       vwgt = graph->vwgt[i];
199 
200       /* Prevent moves that make 'from' domain underbalanced */
201       if (omode == OMODE_REFINE) {
202         if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from])
203           continue;
204       }
205       else { /* OMODE_BALANCE */
206         if (pwgts[from]-vwgt < minwgt[from])
207           continue;
208       }
209 
210       if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk))
211         continue;
212 
213       if (ctrl->minconn)
214         SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms);
215 
216       /* Find the most promising subdomain to move to */
217       if (omode == OMODE_REFINE) {
218         for (k=myrinfo->nnbrs-1; k>=0; k--) {
219           if (!safetos[to=mynbrs[k].pid])
220             continue;
221           gain = mynbrs[k].ed-myrinfo->id;
222           if (gain >= 0 && pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain)
223             break;
224         }
225         if (k < 0)
226           continue;  /* break out if you did not find a candidate */
227 
228         for (j=k-1; j>=0; j--) {
229           if (!safetos[to=mynbrs[j].pid])
230             continue;
231           gain = mynbrs[j].ed-myrinfo->id;
232           if ((mynbrs[j].ed > mynbrs[k].ed && pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain)
233               ||
234               (mynbrs[j].ed == mynbrs[k].ed &&
235                itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid]))
236             k = j;
237         }
238 
239         to = mynbrs[k].pid;
240 
241         gain = mynbrs[k].ed-myrinfo->id;
242         if (!(gain > 0
243               || (gain == 0
244                   && (pwgts[from] >= maxwgt[from]
245                       || itpwgts[to]*pwgts[from] > itpwgts[from]*(pwgts[to]+vwgt)
246                       || (iii%2 == 0 && safetos[to] == 2)
247                      )
248                  )
249              )
250            )
251           continue;
252       }
253       else {  /* OMODE_BALANCE */
254         for (k=myrinfo->nnbrs-1; k>=0; k--) {
255           if (!safetos[to=mynbrs[k].pid])
256             continue;
257           if (pwgts[to]+vwgt <= maxwgt[to] ||
258               itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from])
259             break;
260         }
261         if (k < 0)
262           continue;  /* break out if you did not find a candidate */
263 
264         for (j=k-1; j>=0; j--) {
265           if (!safetos[to=mynbrs[j].pid])
266             continue;
267           if (itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid])
268             k = j;
269         }
270 
271         to = mynbrs[k].pid;
272 
273         if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] &&
274             mynbrs[k].ed-myrinfo->id < 0)
275           continue;
276       }
277 
278 
279 
280       /*=====================================================================
281       * If we got here, we can now move the vertex from 'from' to 'to'
282       *======================================================================*/
283       graph->mincut -= mynbrs[k].ed-myrinfo->id;
284       nmoved++;
285 
286       IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO,
287           printf("\t\tMoving %6"PRIDX" to %3"PRIDX". Gain: %4"PRIDX". Cut: %6"PRIDX"\n",
288               i, to, mynbrs[k].ed-myrinfo->id, graph->mincut));
289 
290       /* Update the subdomain connectivity information */
291       if (ctrl->minconn) {
292         /* take care of i's move itself */
293         UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->id-mynbrs[k].ed, &maxndoms);
294 
295         /* take care of the adjancent vertices */
296         for (j=xadj[i]; j<xadj[i+1]; j++) {
297           me = where[adjncy[j]];
298           if (me != from && me != to) {
299             UpdateEdgeSubDomainGraph(ctrl, from, me, -adjwgt[j], &maxndoms);
300             UpdateEdgeSubDomainGraph(ctrl, to, me, adjwgt[j], &maxndoms);
301           }
302         }
303       }
304 
305       /* Update ID/ED and BND related information for the moved vertex */
306       INC_DEC(pwgts[to], pwgts[from], vwgt);
307       UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where, nbnd,
308           bndptr, bndind, bndtype);
309 
310       /* Update the degrees of adjacent vertices */
311       for (j=xadj[i]; j<xadj[i+1]; j++) {
312         ii = adjncy[j];
313         me = where[ii];
314         myrinfo = graph->ckrinfo+ii;
315 
316         oldnnbrs = myrinfo->nnbrs;
317 
318         UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me,
319             from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, bndtype);
320 
321         UpdateQueueInfo(queue, vstatus, ii, me, from, to, myrinfo, oldnnbrs,
322             nupd, updptr, updind, bndtype);
323 
324         ASSERT(myrinfo->nnbrs <= xadj[ii+1]-xadj[ii]);
325       }
326     }
327 
328     graph->nbnd = nbnd;
329 
330     /* Reset the vstatus and associated data structures */
331     for (i=0; i<nupd; i++) {
332       ASSERT(updptr[updind[i]] != -1);
333       ASSERT(vstatus[updind[i]] != VPQSTATUS_NOTPRESENT);
334       vstatus[updind[i]] = VPQSTATUS_NOTPRESENT;
335       updptr[updind[i]]  = -1;
336     }
337 
338     if (ctrl->dbglvl&METIS_DBG_REFINE) {
339        printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"."
340               " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX,
341               pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts),
342               ComputeLoadImbalance(graph, nparts, ctrl->pijbm),
343               graph->nbnd, nmoved, graph->mincut, ComputeVolume(graph, where));
344        if (ctrl->minconn)
345          printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
346        printf("\n");
347     }
348 
349     if (nmoved == 0 || (omode == OMODE_REFINE && graph->mincut == oldcut))
350       break;
351   }
352 
353   rpqDestroy(queue);
354 
355   WCOREPOP;
356 }
357 
358 
359 /*************************************************************************/
360 /*! K-way refinement that minimizes the communication volume. This is a
361     greedy routine and the vertices are visited in decreasing gv order.
362 
363   \param graph is the graph that is being refined.
364   \param niter is the number of refinement iterations.
365   \param ffactor is the \em fudge-factor for allowing positive gain moves
366          to violate the max-pwgt constraint.
367 
368 */
369 /**************************************************************************/
Greedy_KWayVolOptimize(ctrl_t * ctrl,graph_t * graph,idx_t niter,real_t ffactor,idx_t omode)370 void Greedy_KWayVolOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter,
371          real_t ffactor, idx_t omode)
372 {
373   /* Common variables to all types of kway-refinement/balancing routines */
374   idx_t i, ii, iii, j, k, l, pass, nvtxs, nparts, gain;
375   idx_t from, me, to, oldcut, vwgt;
376   idx_t *xadj, *adjncy;
377   idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts;
378   idx_t nmoved, nupd, *vstatus, *updptr, *updind;
379   idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL;
380   idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL;
381   idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE);
382 
383   /* Volume-specific/different variables */
384   ipq_t *queue;
385   idx_t oldvol, xgain;
386   idx_t *vmarker, *pmarker, *modind;
387   vkrinfo_t *myrinfo;
388   vnbr_t *mynbrs;
389 
390   WCOREPUSH;
391 
392   /* Link the graph fields */
393   nvtxs  = graph->nvtxs;
394   xadj   = graph->xadj;
395   adjncy = graph->adjncy;
396   bndptr = graph->bndptr;
397   bndind = graph->bndind;
398   where  = graph->where;
399   pwgts  = graph->pwgts;
400 
401   nparts = ctrl->nparts;
402 
403   /* Setup the weight intervals of the various subdomains */
404   minwgt  = iwspacemalloc(ctrl, nparts);
405   maxwgt  = iwspacemalloc(ctrl, nparts);
406   itpwgts = iwspacemalloc(ctrl, nparts);
407 
408   for (i=0; i<nparts; i++) {
409     itpwgts[i] = ctrl->tpwgts[i]*graph->tvwgt[0];
410     maxwgt[i]  = ctrl->tpwgts[i]*graph->tvwgt[0]*ctrl->ubfactors[0];
411     minwgt[i]  = ctrl->tpwgts[i]*graph->tvwgt[0]*(1.0/ctrl->ubfactors[0]);
412   }
413 
414   perm = iwspacemalloc(ctrl, nvtxs);
415 
416 
417   /* This stores the valid target subdomains. It is used when ctrl->minconn to
418      control the subdomains to which moves are allowed to be made.
419      When ctrl->minconn is false, the default values of 2 allow all moves to
420      go through and it does not interfere with the zero-gain move selection. */
421   safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts));
422 
423   if (ctrl->minconn) {
424     ComputeSubDomainGraph(ctrl, graph);
425 
426     nads    = ctrl->nads;
427     adids   = ctrl->adids;
428     adwgts  = ctrl->adwgts;
429     doms    = iset(nparts, 0, ctrl->pvec1);
430   }
431 
432 
433   /* Setup updptr, updind like boundary info to keep track of the vertices whose
434      vstatus's need to be reset at the end of the inner iteration */
435   vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs));
436   updptr  = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs));
437   updind  = iwspacemalloc(ctrl, nvtxs);
438 
439   if (ctrl->contig) {
440     /* The arrays that will be used for limited check of articulation points */
441     bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
442     bfsind = iwspacemalloc(ctrl, nvtxs);
443     bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
444   }
445 
446   /* Vol-refinement specific working arrays */
447   modind  = iwspacemalloc(ctrl, nvtxs);
448   vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
449   pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts));
450 
451   if (ctrl->dbglvl&METIS_DBG_REFINE) {
452      printf("%s: [%6"PRIDX" %6"PRIDX"]-[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL
453          ", Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %5"PRIDX", Vol: %5"PRIDX,
454          (omode == OMODE_REFINE ? "GRV" : "GBV"),
455          pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts), minwgt[0], maxwgt[0],
456          ComputeLoadImbalance(graph, nparts, ctrl->pijbm),
457          graph->nvtxs, graph->nbnd, graph->mincut, graph->minvol);
458      if (ctrl->minconn)
459        printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
460      printf("\n");
461   }
462 
463   queue = ipqCreate(nvtxs);
464 
465 
466   /*=====================================================================
467   * The top-level refinement loop
468   *======================================================================*/
469   for (pass=0; pass<niter; pass++) {
470     ASSERT(ComputeVolume(graph, where) == graph->minvol);
471 
472     if (omode == OMODE_BALANCE) {
473       /* Check to see if things are out of balance, given the tolerance */
474       for (i=0; i<nparts; i++) {
475         if (pwgts[i] > maxwgt[i])
476           break;
477       }
478       if (i == nparts) /* Things are balanced. Return right away */
479         break;
480     }
481 
482     oldcut = graph->mincut;
483     oldvol = graph->minvol;
484     nupd   = 0;
485 
486     if (ctrl->minconn)
487       maxndoms = imax(nparts, nads);
488 
489     /* Insert the boundary vertices in the priority queue */
490     irandArrayPermute(graph->nbnd, perm, graph->nbnd/4, 1);
491     for (ii=0; ii<graph->nbnd; ii++) {
492       i = bndind[perm[ii]];
493       ipqInsert(queue, i, graph->vkrinfo[i].gv);
494       vstatus[i] = VPQSTATUS_PRESENT;
495       ListInsert(nupd, updind, updptr, i);
496     }
497 
498     /* Start extracting vertices from the queue and try to move them */
499     for (nmoved=0, iii=0;;iii++) {
500       if ((i = ipqGetTop(queue)) == -1)
501         break;
502       vstatus[i] = VPQSTATUS_EXTRACTED;
503 
504       myrinfo = graph->vkrinfo+i;
505       mynbrs  = ctrl->vnbrpool + myrinfo->inbr;
506 
507       from = where[i];
508       vwgt = graph->vwgt[i];
509 
510       /* Prevent moves that make 'from' domain underbalanced */
511       if (omode == OMODE_REFINE) {
512         if (myrinfo->nid > 0 && pwgts[from]-vwgt < minwgt[from])
513           continue;
514       }
515       else { /* OMODE_BALANCE */
516         if (pwgts[from]-vwgt < minwgt[from])
517           continue;
518       }
519 
520       if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk))
521         continue;
522 
523       if (ctrl->minconn)
524         SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms);
525 
526       xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? graph->vsize[i] : 0);
527 
528       /* Find the most promising subdomain to move to */
529       if (omode == OMODE_REFINE) {
530         for (k=myrinfo->nnbrs-1; k>=0; k--) {
531           if (!safetos[to=mynbrs[k].pid])
532             continue;
533           gain = mynbrs[k].gv + xgain;
534           if (gain >= 0 && pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain)
535             break;
536         }
537         if (k < 0)
538           continue;  /* break out if you did not find a candidate */
539 
540         for (j=k-1; j>=0; j--) {
541           if (!safetos[to=mynbrs[j].pid])
542             continue;
543           gain = mynbrs[j].gv + xgain;
544           if ((mynbrs[j].gv > mynbrs[k].gv &&
545                pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain)
546               ||
547               (mynbrs[j].gv == mynbrs[k].gv &&
548                mynbrs[j].ned > mynbrs[k].ned &&
549                pwgts[to]+vwgt <= maxwgt[to])
550               ||
551               (mynbrs[j].gv == mynbrs[k].gv &&
552                mynbrs[j].ned == mynbrs[k].ned &&
553                itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid])
554              )
555             k = j;
556         }
557         to = mynbrs[k].pid;
558 
559         ASSERT(xgain+mynbrs[k].gv >= 0);
560 
561         j = 0;
562         if (xgain+mynbrs[k].gv > 0 || mynbrs[k].ned-myrinfo->nid > 0)
563           j = 1;
564         else if (mynbrs[k].ned-myrinfo->nid == 0) {
565           if ((iii%2 == 0 && safetos[to] == 2) ||
566               pwgts[from] >= maxwgt[from] ||
567               itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from])
568             j = 1;
569         }
570         if (j == 0)
571           continue;
572       }
573       else { /* OMODE_BALANCE */
574         for (k=myrinfo->nnbrs-1; k>=0; k--) {
575           if (!safetos[to=mynbrs[k].pid])
576             continue;
577           if (pwgts[to]+vwgt <= maxwgt[to] ||
578               itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from])
579             break;
580         }
581         if (k < 0)
582           continue;  /* break out if you did not find a candidate */
583 
584         for (j=k-1; j>=0; j--) {
585           if (!safetos[to=mynbrs[j].pid])
586             continue;
587           if (itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid])
588             k = j;
589         }
590         to = mynbrs[k].pid;
591 
592         if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] &&
593             (xgain+mynbrs[k].gv < 0 ||
594              (xgain+mynbrs[k].gv == 0 &&  mynbrs[k].ned-myrinfo->nid < 0))
595            )
596           continue;
597       }
598 
599 
600       /*=====================================================================
601       * If we got here, we can now move the vertex from 'from' to 'to'
602       *======================================================================*/
603       INC_DEC(pwgts[to], pwgts[from], vwgt);
604       graph->mincut -= mynbrs[k].ned-myrinfo->nid;
605       graph->minvol -= (xgain+mynbrs[k].gv);
606       where[i] = to;
607       nmoved++;
608 
609       IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO,
610           printf("\t\tMoving %6"PRIDX" from %3"PRIDX" to %3"PRIDX". "
611                  "Gain: [%4"PRIDX" %4"PRIDX"]. Cut: %6"PRIDX", Vol: %6"PRIDX"\n",
612               i, from, to, xgain+mynbrs[k].gv, mynbrs[k].ned-myrinfo->nid,
613               graph->mincut, graph->minvol));
614 
615       /* Update the subdomain connectivity information */
616       if (ctrl->minconn) {
617         /* take care of i's move itself */
618         UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->nid-mynbrs[k].ned, &maxndoms);
619 
620         /* take care of the adjancent vertices */
621         for (j=xadj[i]; j<xadj[i+1]; j++) {
622           me = where[adjncy[j]];
623           if (me != from && me != to) {
624             UpdateEdgeSubDomainGraph(ctrl, from, me, -1, &maxndoms);
625             UpdateEdgeSubDomainGraph(ctrl, to, me, 1, &maxndoms);
626           }
627         }
628       }
629 
630       /* Update the id/ed/gains/bnd/queue of potentially affected nodes */
631       KWayVolUpdate(ctrl, graph, i, from, to, queue, vstatus, &nupd, updptr,
632           updind, bndtype, vmarker, pmarker, modind);
633 
634       /*CheckKWayVolPartitionParams(ctrl, graph); */
635     }
636 
637 
638     /* Reset the vstatus and associated data structures */
639     for (i=0; i<nupd; i++) {
640       ASSERT(updptr[updind[i]] != -1);
641       ASSERT(vstatus[updind[i]] != VPQSTATUS_NOTPRESENT);
642       vstatus[updind[i]] = VPQSTATUS_NOTPRESENT;
643       updptr[updind[i]]  = -1;
644     }
645 
646     if (ctrl->dbglvl&METIS_DBG_REFINE) {
647        printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"."
648               " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX,
649               pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts),
650               ComputeLoadImbalance(graph, nparts, ctrl->pijbm),
651               graph->nbnd, nmoved, graph->mincut, graph->minvol);
652        if (ctrl->minconn)
653          printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
654        printf("\n");
655     }
656 
657     if (nmoved == 0 ||
658         (omode == OMODE_REFINE && graph->minvol == oldvol && graph->mincut == oldcut))
659       break;
660   }
661 
662   ipqDestroy(queue);
663 
664   WCOREPOP;
665 }
666 
667 
668 /*************************************************************************/
669 /*! K-way partitioning optimization in which the vertices are visited in
670     decreasing ed/sqrt(nnbrs)-id order. Note this is just an
671     approximation, as the ed is often split across different subdomains
672     and the sqrt(nnbrs) is just a crude approximation.
673 
674   \param graph is the graph that is being refined.
675   \param niter is the number of refinement iterations.
676   \param ffactor is the \em fudge-factor for allowing positive gain moves
677          to violate the max-pwgt constraint.
678   \param omode is the type of optimization that will performed among
679          OMODE_REFINE and OMODE_BALANCE
680 
681 
682 */
683 /**************************************************************************/
Greedy_McKWayCutOptimize(ctrl_t * ctrl,graph_t * graph,idx_t niter,real_t ffactor,idx_t omode)684 void Greedy_McKWayCutOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter,
685          real_t ffactor, idx_t omode)
686 {
687   /* Common variables to all types of kway-refinement/balancing routines */
688   idx_t i, ii, iii, j, k, l, pass, nvtxs, ncon, nparts, gain;
689   idx_t from, me, to, cto, oldcut;
690   idx_t *xadj, *vwgt, *adjncy, *adjwgt;
691   idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt;
692   idx_t nmoved, nupd, *vstatus, *updptr, *updind;
693   idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL;
694   idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL;
695   idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE);
696   real_t *ubfactors, *pijbm;
697   real_t origbal;
698 
699   /* Edgecut-specific/different variables */
700   idx_t nbnd, oldnnbrs;
701   rpq_t *queue;
702   real_t rgain;
703   ckrinfo_t *myrinfo;
704   cnbr_t *mynbrs;
705 
706   WCOREPUSH;
707 
708   /* Link the graph fields */
709   nvtxs  = graph->nvtxs;
710   ncon   = graph->ncon;
711   xadj   = graph->xadj;
712   vwgt   = graph->vwgt;
713   adjncy = graph->adjncy;
714   adjwgt = graph->adjwgt;
715 
716   bndind = graph->bndind;
717   bndptr = graph->bndptr;
718 
719   where = graph->where;
720   pwgts = graph->pwgts;
721 
722   nparts = ctrl->nparts;
723   pijbm  = ctrl->pijbm;
724 
725 
726   /* Determine the ubfactors. The method used is different based on omode.
727      When OMODE_BALANCE, the ubfactors are those supplied by the user.
728      When OMODE_REFINE, the ubfactors are the max of the current partition
729      and the user-specified ones. */
730   ubfactors = rwspacemalloc(ctrl, ncon);
731   ComputeLoadImbalanceVec(graph, nparts, pijbm, ubfactors);
732   origbal = rvecmaxdiff(ncon, ubfactors, ctrl->ubfactors);
733   if (omode == OMODE_BALANCE) {
734     rcopy(ncon, ctrl->ubfactors, ubfactors);
735   }
736   else {
737     for (i=0; i<ncon; i++)
738       ubfactors[i] = (ubfactors[i] > ctrl->ubfactors[i] ? ubfactors[i] : ctrl->ubfactors[i]);
739   }
740 
741 
742   /* Setup the weight intervals of the various subdomains */
743   minwgt  = iwspacemalloc(ctrl, nparts*ncon);
744   maxwgt  = iwspacemalloc(ctrl, nparts*ncon);
745 
746   for (i=0; i<nparts; i++) {
747     for (j=0; j<ncon; j++) {
748       maxwgt[i*ncon+j]  = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*ubfactors[j];
749       /*minwgt[i*ncon+j]  = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*(.9/ubfactors[j]);*/
750       minwgt[i*ncon+j]  = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*.2;
751     }
752   }
753 
754   perm = iwspacemalloc(ctrl, nvtxs);
755 
756 
757   /* This stores the valid target subdomains. It is used when ctrl->minconn to
758      control the subdomains to which moves are allowed to be made.
759      When ctrl->minconn is false, the default values of 2 allow all moves to
760      go through and it does not interfere with the zero-gain move selection. */
761   safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts));
762 
763   if (ctrl->minconn) {
764     ComputeSubDomainGraph(ctrl, graph);
765 
766     nads    = ctrl->nads;
767     adids   = ctrl->adids;
768     adwgts  = ctrl->adwgts;
769     doms    = iset(nparts, 0, ctrl->pvec1);
770   }
771 
772 
773   /* Setup updptr, updind like boundary info to keep track of the vertices whose
774      vstatus's need to be reset at the end of the inner iteration */
775   vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs));
776   updptr  = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs));
777   updind  = iwspacemalloc(ctrl, nvtxs);
778 
779   if (ctrl->contig) {
780     /* The arrays that will be used for limited check of articulation points */
781     bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
782     bfsind = iwspacemalloc(ctrl, nvtxs);
783     bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
784   }
785 
786   if (ctrl->dbglvl&METIS_DBG_REFINE) {
787      printf("%s: [%6"PRIDX" %6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL"(%.3"PRREAL"),"
788             " Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %6"PRIDX", (%"PRIDX")",
789             (omode == OMODE_REFINE ? "GRC" : "GBC"),
790             imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts), imax(nparts*ncon, maxwgt),
791             ComputeLoadImbalance(graph, nparts, pijbm), origbal,
792             graph->nvtxs, graph->nbnd, graph->mincut, niter);
793      if (ctrl->minconn)
794        printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
795      printf("\n");
796   }
797 
798   queue = rpqCreate(nvtxs);
799 
800 
801   /*=====================================================================
802   * The top-level refinement loop
803   *======================================================================*/
804   for (pass=0; pass<niter; pass++) {
805     ASSERT(ComputeCut(graph, where) == graph->mincut);
806 
807     /* In balancing mode, exit as soon as balance is reached */
808     if (omode == OMODE_BALANCE && IsBalanced(ctrl, graph, 0))
809       break;
810 
811     oldcut = graph->mincut;
812     nbnd   = graph->nbnd;
813     nupd   = 0;
814 
815     if (ctrl->minconn)
816       maxndoms = imax(nparts, nads);
817 
818     /* Insert the boundary vertices in the priority queue */
819     irandArrayPermute(nbnd, perm, nbnd/4, 1);
820     for (ii=0; ii<nbnd; ii++) {
821       i = bndind[perm[ii]];
822       rgain = (graph->ckrinfo[i].nnbrs > 0 ?
823                1.0*graph->ckrinfo[i].ed/sqrt(graph->ckrinfo[i].nnbrs) : 0.0)
824                - graph->ckrinfo[i].id;
825       rpqInsert(queue, i, rgain);
826       vstatus[i] = VPQSTATUS_PRESENT;
827       ListInsert(nupd, updind, updptr, i);
828     }
829 
830     /* Start extracting vertices from the queue and try to move them */
831     for (nmoved=0, iii=0;;iii++) {
832       if ((i = rpqGetTop(queue)) == -1)
833         break;
834       vstatus[i] = VPQSTATUS_EXTRACTED;
835 
836       myrinfo = graph->ckrinfo+i;
837       mynbrs  = ctrl->cnbrpool + myrinfo->inbr;
838 
839       from = where[i];
840 
841       /* Prevent moves that make 'from' domain underbalanced */
842       if (omode == OMODE_REFINE) {
843         if (myrinfo->id > 0 &&
844             !ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon))
845           continue;
846       }
847       else { /* OMODE_BALANCE */
848         if (!ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon))
849           continue;
850       }
851 
852       if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk))
853         continue;
854 
855       if (ctrl->minconn)
856         SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms);
857 
858       /* Find the most promising subdomain to move to */
859       if (omode == OMODE_REFINE) {
860         for (k=myrinfo->nnbrs-1; k>=0; k--) {
861           if (!safetos[to=mynbrs[k].pid])
862             continue;
863           gain = mynbrs[k].ed-myrinfo->id;
864           if (gain >= 0 && ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon))
865             break;
866         }
867         if (k < 0)
868           continue;  /* break out if you did not find a candidate */
869 
870         cto = to;
871         for (j=k-1; j>=0; j--) {
872           if (!safetos[to=mynbrs[j].pid])
873             continue;
874           if ((mynbrs[j].ed > mynbrs[k].ed &&
875                ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon))
876               ||
877               (mynbrs[j].ed == mynbrs[k].ed &&
878                BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
879                    1, pwgts+cto*ncon, pijbm+cto*ncon,
880                    1, pwgts+to*ncon, pijbm+to*ncon))) {
881             k   = j;
882             cto = to;
883           }
884         }
885         to = cto;
886 
887         gain = mynbrs[k].ed-myrinfo->id;
888         if (!(gain > 0
889               || (gain == 0
890                   && (BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
891                              -1, pwgts+from*ncon, pijbm+from*ncon,
892                              +1, pwgts+to*ncon, pijbm+to*ncon)
893                       || (iii%2 == 0 && safetos[to] == 2)
894                      )
895                  )
896              )
897            )
898           continue;
899       }
900       else {  /* OMODE_BALANCE */
901         for (k=myrinfo->nnbrs-1; k>=0; k--) {
902           if (!safetos[to=mynbrs[k].pid])
903             continue;
904           if (ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon) ||
905               BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
906                   -1, pwgts+from*ncon, pijbm+from*ncon,
907                   +1, pwgts+to*ncon, pijbm+to*ncon))
908             break;
909         }
910         if (k < 0)
911           continue;  /* break out if you did not find a candidate */
912 
913         cto = to;
914         for (j=k-1; j>=0; j--) {
915           if (!safetos[to=mynbrs[j].pid])
916             continue;
917           if (BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
918                    1, pwgts+cto*ncon, pijbm+cto*ncon,
919                    1, pwgts+to*ncon, pijbm+to*ncon)) {
920             k   = j;
921             cto = to;
922           }
923         }
924         to = cto;
925 
926         if (mynbrs[k].ed-myrinfo->id < 0 &&
927             !BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
928                   -1, pwgts+from*ncon, pijbm+from*ncon,
929                   +1, pwgts+to*ncon, pijbm+to*ncon))
930           continue;
931       }
932 
933 
934 
935       /*=====================================================================
936       * If we got here, we can now move the vertex from 'from' to 'to'
937       *======================================================================*/
938       graph->mincut -= mynbrs[k].ed-myrinfo->id;
939       nmoved++;
940 
941       IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO,
942           printf("\t\tMoving %6"PRIDX" to %3"PRIDX". Gain: %4"PRIDX". Cut: %6"PRIDX"\n",
943               i, to, mynbrs[k].ed-myrinfo->id, graph->mincut));
944 
945       /* Update the subdomain connectivity information */
946       if (ctrl->minconn) {
947         /* take care of i's move itself */
948         UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->id-mynbrs[k].ed, &maxndoms);
949 
950         /* take care of the adjancent vertices */
951         for (j=xadj[i]; j<xadj[i+1]; j++) {
952           me = where[adjncy[j]];
953           if (me != from && me != to) {
954             UpdateEdgeSubDomainGraph(ctrl, from, me, -adjwgt[j], &maxndoms);
955             UpdateEdgeSubDomainGraph(ctrl, to, me, adjwgt[j], &maxndoms);
956           }
957         }
958       }
959 
960       /* Update ID/ED and BND related information for the moved vertex */
961       iaxpy(ncon,  1, vwgt+i*ncon, 1, pwgts+to*ncon,   1);
962       iaxpy(ncon, -1, vwgt+i*ncon, 1, pwgts+from*ncon, 1);
963       UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where,
964           nbnd, bndptr, bndind, bndtype);
965 
966       /* Update the degrees of adjacent vertices */
967       for (j=xadj[i]; j<xadj[i+1]; j++) {
968         ii = adjncy[j];
969         me = where[ii];
970         myrinfo = graph->ckrinfo+ii;
971 
972         oldnnbrs = myrinfo->nnbrs;
973 
974         UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me,
975             from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, bndtype);
976 
977         UpdateQueueInfo(queue, vstatus, ii, me, from, to, myrinfo, oldnnbrs,
978             nupd, updptr, updind, bndtype);
979 
980         ASSERT(myrinfo->nnbrs <= xadj[ii+1]-xadj[ii]);
981       }
982     }
983 
984     graph->nbnd = nbnd;
985 
986     /* Reset the vstatus and associated data structures */
987     for (i=0; i<nupd; i++) {
988       ASSERT(updptr[updind[i]] != -1);
989       ASSERT(vstatus[updind[i]] != VPQSTATUS_NOTPRESENT);
990       vstatus[updind[i]] = VPQSTATUS_NOTPRESENT;
991       updptr[updind[i]]  = -1;
992     }
993 
994     if (ctrl->dbglvl&METIS_DBG_REFINE) {
995        printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"."
996               " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX,
997               imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts),
998               ComputeLoadImbalance(graph, nparts, pijbm),
999               graph->nbnd, nmoved, graph->mincut, ComputeVolume(graph, where));
1000        if (ctrl->minconn)
1001          printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
1002        printf("\n");
1003     }
1004 
1005     if (nmoved == 0 || (omode == OMODE_REFINE && graph->mincut == oldcut))
1006       break;
1007   }
1008 
1009   rpqDestroy(queue);
1010 
1011   WCOREPOP;
1012 }
1013 
1014 
1015 /*************************************************************************/
1016 /*! K-way refinement that minimizes the communication volume. This is a
1017     greedy routine and the vertices are visited in decreasing gv order.
1018 
1019   \param graph is the graph that is being refined.
1020   \param niter is the number of refinement iterations.
1021   \param ffactor is the \em fudge-factor for allowing positive gain moves
1022          to violate the max-pwgt constraint.
1023 
1024 */
1025 /**************************************************************************/
Greedy_McKWayVolOptimize(ctrl_t * ctrl,graph_t * graph,idx_t niter,real_t ffactor,idx_t omode)1026 void Greedy_McKWayVolOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter,
1027          real_t ffactor, idx_t omode)
1028 {
1029   /* Common variables to all types of kway-refinement/balancing routines */
1030   idx_t i, ii, iii, j, k, l, pass, nvtxs, ncon, nparts, gain;
1031   idx_t from, me, to, cto, oldcut;
1032   idx_t *xadj, *vwgt, *adjncy;
1033   idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt;
1034   idx_t nmoved, nupd, *vstatus, *updptr, *updind;
1035   idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL;
1036   idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL;
1037   idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE);
1038   real_t *ubfactors, *pijbm;
1039   real_t origbal;
1040 
1041   /* Volume-specific/different variables */
1042   ipq_t *queue;
1043   idx_t oldvol, xgain;
1044   idx_t *vmarker, *pmarker, *modind;
1045   vkrinfo_t *myrinfo;
1046   vnbr_t *mynbrs;
1047 
1048   WCOREPUSH;
1049 
1050   /* Link the graph fields */
1051   nvtxs  = graph->nvtxs;
1052   ncon   = graph->ncon;
1053   xadj   = graph->xadj;
1054   vwgt   = graph->vwgt;
1055   adjncy = graph->adjncy;
1056   bndptr = graph->bndptr;
1057   bndind = graph->bndind;
1058   where  = graph->where;
1059   pwgts  = graph->pwgts;
1060 
1061   nparts = ctrl->nparts;
1062   pijbm  = ctrl->pijbm;
1063 
1064 
1065   /* Determine the ubfactors. The method used is different based on omode.
1066      When OMODE_BALANCE, the ubfactors are those supplied by the user.
1067      When OMODE_REFINE, the ubfactors are the max of the current partition
1068      and the user-specified ones. */
1069   ubfactors = rwspacemalloc(ctrl, ncon);
1070   ComputeLoadImbalanceVec(graph, nparts, pijbm, ubfactors);
1071   origbal = rvecmaxdiff(ncon, ubfactors, ctrl->ubfactors);
1072   if (omode == OMODE_BALANCE) {
1073     rcopy(ncon, ctrl->ubfactors, ubfactors);
1074   }
1075   else {
1076     for (i=0; i<ncon; i++)
1077       ubfactors[i] = (ubfactors[i] > ctrl->ubfactors[i] ? ubfactors[i] : ctrl->ubfactors[i]);
1078   }
1079 
1080 
1081   /* Setup the weight intervals of the various subdomains */
1082   minwgt  = iwspacemalloc(ctrl, nparts*ncon);
1083   maxwgt  = iwspacemalloc(ctrl, nparts*ncon);
1084 
1085   for (i=0; i<nparts; i++) {
1086     for (j=0; j<ncon; j++) {
1087       maxwgt[i*ncon+j]  = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*ubfactors[j];
1088       /*minwgt[i*ncon+j]  = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*(.9/ubfactors[j]); */
1089       minwgt[i*ncon+j]  = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*.2;
1090     }
1091   }
1092 
1093   perm = iwspacemalloc(ctrl, nvtxs);
1094 
1095 
1096   /* This stores the valid target subdomains. It is used when ctrl->minconn to
1097      control the subdomains to which moves are allowed to be made.
1098      When ctrl->minconn is false, the default values of 2 allow all moves to
1099      go through and it does not interfere with the zero-gain move selection. */
1100   safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts));
1101 
1102   if (ctrl->minconn) {
1103     ComputeSubDomainGraph(ctrl, graph);
1104 
1105     nads    = ctrl->nads;
1106     adids   = ctrl->adids;
1107     adwgts  = ctrl->adwgts;
1108     doms    = iset(nparts, 0, ctrl->pvec1);
1109   }
1110 
1111 
1112   /* Setup updptr, updind like boundary info to keep track of the vertices whose
1113      vstatus's need to be reset at the end of the inner iteration */
1114   vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs));
1115   updptr  = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs));
1116   updind  = iwspacemalloc(ctrl, nvtxs);
1117 
1118   if (ctrl->contig) {
1119     /* The arrays that will be used for limited check of articulation points */
1120     bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
1121     bfsind = iwspacemalloc(ctrl, nvtxs);
1122     bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
1123   }
1124 
1125   /* Vol-refinement specific working arrays */
1126   modind  = iwspacemalloc(ctrl, nvtxs);
1127   vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));
1128   pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts));
1129 
1130   if (ctrl->dbglvl&METIS_DBG_REFINE) {
1131      printf("%s: [%6"PRIDX" %6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL"(%.3"PRREAL"),"
1132          ", Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %5"PRIDX", Vol: %5"PRIDX", (%"PRIDX")",
1133          (omode == OMODE_REFINE ? "GRV" : "GBV"),
1134          imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts), imax(nparts*ncon, maxwgt),
1135          ComputeLoadImbalance(graph, nparts, pijbm), origbal,
1136          graph->nvtxs, graph->nbnd, graph->mincut, graph->minvol, niter);
1137      if (ctrl->minconn)
1138        printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
1139      printf("\n");
1140   }
1141 
1142   queue = ipqCreate(nvtxs);
1143 
1144 
1145   /*=====================================================================
1146   * The top-level refinement loop
1147   *======================================================================*/
1148   for (pass=0; pass<niter; pass++) {
1149     ASSERT(ComputeVolume(graph, where) == graph->minvol);
1150 
1151     /* In balancing mode, exit as soon as balance is reached */
1152     if (omode == OMODE_BALANCE && IsBalanced(ctrl, graph, 0))
1153       break;
1154 
1155     oldcut = graph->mincut;
1156     oldvol = graph->minvol;
1157     nupd   = 0;
1158 
1159     if (ctrl->minconn)
1160       maxndoms = imax(nparts, nads);
1161 
1162     /* Insert the boundary vertices in the priority queue */
1163     irandArrayPermute(graph->nbnd, perm, graph->nbnd/4, 1);
1164     for (ii=0; ii<graph->nbnd; ii++) {
1165       i = bndind[perm[ii]];
1166       ipqInsert(queue, i, graph->vkrinfo[i].gv);
1167       vstatus[i] = VPQSTATUS_PRESENT;
1168       ListInsert(nupd, updind, updptr, i);
1169     }
1170 
1171     /* Start extracting vertices from the queue and try to move them */
1172     for (nmoved=0, iii=0;;iii++) {
1173       if ((i = ipqGetTop(queue)) == -1)
1174         break;
1175       vstatus[i] = VPQSTATUS_EXTRACTED;
1176 
1177       myrinfo = graph->vkrinfo+i;
1178       mynbrs  = ctrl->vnbrpool + myrinfo->inbr;
1179 
1180       from = where[i];
1181 
1182       /* Prevent moves that make 'from' domain underbalanced */
1183       if (omode == OMODE_REFINE) {
1184         if (myrinfo->nid > 0 &&
1185             !ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon))
1186           continue;
1187       }
1188       else { /* OMODE_BALANCE */
1189         if (!ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon))
1190           continue;
1191       }
1192 
1193       if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk))
1194         continue;
1195 
1196       if (ctrl->minconn)
1197         SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms);
1198 
1199       xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? graph->vsize[i] : 0);
1200 
1201       /* Find the most promising subdomain to move to */
1202       if (omode == OMODE_REFINE) {
1203         for (k=myrinfo->nnbrs-1; k>=0; k--) {
1204           if (!safetos[to=mynbrs[k].pid])
1205             continue;
1206           gain = mynbrs[k].gv + xgain;
1207           if (gain >= 0 && ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon))
1208             break;
1209         }
1210         if (k < 0)
1211           continue;  /* break out if you did not find a candidate */
1212 
1213         cto = to;
1214         for (j=k-1; j>=0; j--) {
1215           if (!safetos[to=mynbrs[j].pid])
1216             continue;
1217           gain = mynbrs[j].gv + xgain;
1218           if ((mynbrs[j].gv > mynbrs[k].gv &&
1219                ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon))
1220               ||
1221               (mynbrs[j].gv == mynbrs[k].gv &&
1222                mynbrs[j].ned > mynbrs[k].ned &&
1223                ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon))
1224               ||
1225               (mynbrs[j].gv == mynbrs[k].gv &&
1226                mynbrs[j].ned == mynbrs[k].ned &&
1227                BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
1228                    1, pwgts+cto*ncon, pijbm+cto*ncon,
1229                    1, pwgts+to*ncon, pijbm+to*ncon))) {
1230             k   = j;
1231             cto = to;
1232           }
1233         }
1234         to = cto;
1235 
1236         j = 0;
1237         if (xgain+mynbrs[k].gv > 0 || mynbrs[k].ned-myrinfo->nid > 0)
1238           j = 1;
1239         else if (mynbrs[k].ned-myrinfo->nid == 0) {
1240           if ((iii%2 == 0 && safetos[to] == 2) ||
1241               BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
1242                   -1, pwgts+from*ncon, pijbm+from*ncon,
1243                   +1, pwgts+to*ncon, pijbm+to*ncon))
1244             j = 1;
1245         }
1246         if (j == 0)
1247           continue;
1248       }
1249       else { /* OMODE_BALANCE */
1250         for (k=myrinfo->nnbrs-1; k>=0; k--) {
1251           if (!safetos[to=mynbrs[k].pid])
1252             continue;
1253           if (ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon) ||
1254               BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
1255                   -1, pwgts+from*ncon, pijbm+from*ncon,
1256                   +1, pwgts+to*ncon, pijbm+to*ncon))
1257             break;
1258         }
1259         if (k < 0)
1260           continue;  /* break out if you did not find a candidate */
1261 
1262         cto = to;
1263         for (j=k-1; j>=0; j--) {
1264           if (!safetos[to=mynbrs[j].pid])
1265             continue;
1266           if (BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
1267                   1, pwgts+cto*ncon, pijbm+cto*ncon,
1268                   1, pwgts+to*ncon, pijbm+to*ncon)) {
1269             k   = j;
1270             cto = to;
1271           }
1272         }
1273         to = cto;
1274 
1275         if ((xgain+mynbrs[k].gv < 0 ||
1276              (xgain+mynbrs[k].gv == 0 && mynbrs[k].ned-myrinfo->nid < 0))
1277             &&
1278             !BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors,
1279                  -1, pwgts+from*ncon, pijbm+from*ncon,
1280                  +1, pwgts+to*ncon, pijbm+to*ncon))
1281           continue;
1282       }
1283 
1284 
1285       /*=====================================================================
1286       * If we got here, we can now move the vertex from 'from' to 'to'
1287       *======================================================================*/
1288       graph->mincut -= mynbrs[k].ned-myrinfo->nid;
1289       graph->minvol -= (xgain+mynbrs[k].gv);
1290       where[i] = to;
1291       nmoved++;
1292 
1293       IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO,
1294           printf("\t\tMoving %6"PRIDX" from %3"PRIDX" to %3"PRIDX". "
1295                  "Gain: [%4"PRIDX" %4"PRIDX"]. Cut: %6"PRIDX", Vol: %6"PRIDX"\n",
1296               i, from, to, xgain+mynbrs[k].gv, mynbrs[k].ned-myrinfo->nid,
1297               graph->mincut, graph->minvol));
1298 
1299       /* Update the subdomain connectivity information */
1300       if (ctrl->minconn) {
1301         /* take care of i's move itself */
1302         UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->nid-mynbrs[k].ned, &maxndoms);
1303 
1304         /* take care of the adjancent vertices */
1305         for (j=xadj[i]; j<xadj[i+1]; j++) {
1306           me = where[adjncy[j]];
1307           if (me != from && me != to) {
1308             UpdateEdgeSubDomainGraph(ctrl, from, me, -1, &maxndoms);
1309             UpdateEdgeSubDomainGraph(ctrl, to, me, 1, &maxndoms);
1310           }
1311         }
1312       }
1313 
1314       /* Update pwgts */
1315       iaxpy(ncon,  1, vwgt+i*ncon, 1, pwgts+to*ncon,   1);
1316       iaxpy(ncon, -1, vwgt+i*ncon, 1, pwgts+from*ncon, 1);
1317 
1318       /* Update the id/ed/gains/bnd/queue of potentially affected nodes */
1319       KWayVolUpdate(ctrl, graph, i, from, to, queue, vstatus, &nupd, updptr,
1320           updind, bndtype, vmarker, pmarker, modind);
1321 
1322       /*CheckKWayVolPartitionParams(ctrl, graph); */
1323     }
1324 
1325 
1326     /* Reset the vstatus and associated data structures */
1327     for (i=0; i<nupd; i++) {
1328       ASSERT(updptr[updind[i]] != -1);
1329       ASSERT(vstatus[updind[i]] != VPQSTATUS_NOTPRESENT);
1330       vstatus[updind[i]] = VPQSTATUS_NOTPRESENT;
1331       updptr[updind[i]]  = -1;
1332     }
1333 
1334     if (ctrl->dbglvl&METIS_DBG_REFINE) {
1335        printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"."
1336               " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX,
1337               imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts),
1338               ComputeLoadImbalance(graph, nparts, pijbm),
1339               graph->nbnd, nmoved, graph->mincut, graph->minvol);
1340        if (ctrl->minconn)
1341          printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1));
1342        printf("\n");
1343     }
1344 
1345     if (nmoved == 0 ||
1346         (omode == OMODE_REFINE && graph->minvol == oldvol && graph->mincut == oldcut))
1347       break;
1348   }
1349 
1350   ipqDestroy(queue);
1351 
1352   WCOREPOP;
1353 }
1354 
1355 
1356 /*************************************************************************/
1357 /*! This function performs an approximate articulation vertex test.
1358     It assumes that the bfslvl, bfsind, and bfsmrk arrays are initialized
1359     appropriately. */
1360 /*************************************************************************/
IsArticulationNode(idx_t i,idx_t * xadj,idx_t * adjncy,idx_t * where,idx_t * bfslvl,idx_t * bfsind,idx_t * bfsmrk)1361 idx_t IsArticulationNode(idx_t i, idx_t *xadj, idx_t *adjncy, idx_t *where,
1362           idx_t *bfslvl, idx_t *bfsind, idx_t *bfsmrk)
1363 {
1364   idx_t ii, j, k=0, head, tail, nhits, tnhits, from, BFSDEPTH=5;
1365 
1366   from = where[i];
1367 
1368   /* Determine if the vertex is safe to move from a contiguity standpoint */
1369   for (tnhits=0, j=xadj[i]; j<xadj[i+1]; j++) {
1370     if (where[adjncy[j]] == from) {
1371       ASSERT(bfsmrk[adjncy[j]] == 0);
1372       ASSERT(bfslvl[adjncy[j]] == 0);
1373       bfsmrk[k=adjncy[j]] = 1;
1374       tnhits++;
1375     }
1376   }
1377 
1378   /* Easy cases */
1379   if (tnhits == 0)
1380     return 0;
1381   if (tnhits == 1) {
1382     bfsmrk[k] = 0;
1383     return 0;
1384   }
1385 
1386   ASSERT(bfslvl[i] == 0);
1387   bfslvl[i] = 1;
1388 
1389   bfsind[0] = k; /* That was the last one from the previous loop */
1390   bfslvl[k] = 1;
1391   bfsmrk[k] = 0;
1392   head = 0;
1393   tail = 1;
1394 
1395   /* Do a limited BFS traversal to see if you can get to all the other nodes */
1396   for (nhits=1; head<tail; ) {
1397     ii = bfsind[head++];
1398     for (j=xadj[ii]; j<xadj[ii+1]; j++) {
1399       if (where[k=adjncy[j]] == from) {
1400         if (bfsmrk[k]) {
1401           bfsmrk[k] = 0;
1402           if (++nhits == tnhits)
1403             break;
1404         }
1405         if (bfslvl[k] == 0 && bfslvl[ii] < BFSDEPTH) {
1406           bfsind[tail++] = k;
1407           bfslvl[k] = bfslvl[ii]+1;
1408         }
1409       }
1410     }
1411     if (nhits == tnhits)
1412       break;
1413   }
1414 
1415   /* Reset the various BFS related arrays */
1416   bfslvl[i] = 0;
1417   for (j=0; j<tail; j++)
1418     bfslvl[bfsind[j]] = 0;
1419 
1420 
1421   /* Reset the bfsmrk array for the next vertex when has not already being cleared */
1422   if (nhits < tnhits) {
1423     for (j=xadj[i]; j<xadj[i+1]; j++)
1424       if (where[adjncy[j]] == from)
1425         bfsmrk[adjncy[j]] = 0;
1426   }
1427 
1428   return (nhits != tnhits);
1429 }
1430 
1431 
1432 /*************************************************************************/
1433 /*!
1434  This function updates the edge and volume gains due to a vertex movement.
1435  v from 'from' to 'to'.
1436 
1437  \param ctrl is the control structure.
1438  \param graph is the graph being partitioned.
1439  \param v is the vertex that is moving.
1440  \param from is the original partition of v.
1441  \param to is the new partition of v.
1442  \param queue is the priority queue. If the queue is NULL, no priority-queue
1443         related updates are performed.
1444  \param vstatus is an array that marks the status of the vertex in terms
1445         of the priority queue. If queue is NULL, this parameter is ignored.
1446  \param r_nqupd is the number of vertices that have been inserted/removed
1447         from the queue. If queue is NULL, this parameter is ignored.
1448  \param updptr stores the index of each vertex in updind. If queue is NULL,
1449         this parameter is ignored.
1450  \param updind is the list of vertices that have been inserted/removed from
1451         the queue. If queue is NULL, this parameter is ignored.
1452  \param vmarker is of size nvtxs and is used internally as a temporary array.
1453         On entry and return all of its entries are 0.
1454  \param pmarker is of sie nparts and is used internally as a temporary marking
1455         array. On entry and return all of its entries are -1.
1456  \param modind is an array of size nvtxs and is used to keep track of the
1457         list of vertices whose gains need to be updated.
1458 */
1459 /*************************************************************************/
KWayVolUpdate(ctrl_t * ctrl,graph_t * graph,idx_t v,idx_t from,idx_t to,ipq_t * queue,idx_t * vstatus,idx_t * r_nupd,idx_t * updptr,idx_t * updind,idx_t bndtype,idx_t * vmarker,idx_t * pmarker,idx_t * modind)1460 void KWayVolUpdate(ctrl_t *ctrl, graph_t *graph, idx_t v, idx_t from,
1461          idx_t to, ipq_t *queue, idx_t *vstatus, idx_t *r_nupd, idx_t *updptr,
1462          idx_t *updind, idx_t bndtype, idx_t *vmarker, idx_t *pmarker,
1463          idx_t *modind)
1464 {
1465   idx_t i, ii, iii, j, jj, k, kk, l, u, nmod, other, me, myidx;
1466   idx_t *xadj, *vsize, *adjncy, *where;
1467   vkrinfo_t *myrinfo, *orinfo;
1468   vnbr_t *mynbrs, *onbrs;
1469 
1470   xadj   = graph->xadj;
1471   adjncy = graph->adjncy;
1472   vsize  = graph->vsize;
1473   where  = graph->where;
1474 
1475   myrinfo = graph->vkrinfo+v;
1476   mynbrs  = ctrl->vnbrpool + myrinfo->inbr;
1477 
1478 
1479   /*======================================================================
1480    * Remove the contributions on the gain made by 'v'.
1481    *=====================================================================*/
1482   for (k=0; k<myrinfo->nnbrs; k++)
1483     pmarker[mynbrs[k].pid] = k;
1484   pmarker[from] = k;
1485 
1486   myidx = pmarker[to];  /* Keep track of the index in mynbrs of the 'to' domain */
1487 
1488   for (j=xadj[v]; j<xadj[v+1]; j++) {
1489     ii     = adjncy[j];
1490     other  = where[ii];
1491     orinfo = graph->vkrinfo+ii;
1492     onbrs  = ctrl->vnbrpool + orinfo->inbr;
1493 
1494     if (other == from) {
1495       for (k=0; k<orinfo->nnbrs; k++) {
1496         if (pmarker[onbrs[k].pid] == -1)
1497           onbrs[k].gv += vsize[v];
1498       }
1499     }
1500     else {
1501       ASSERT(pmarker[other] != -1);
1502 
1503       if (mynbrs[pmarker[other]].ned > 1) {
1504         for (k=0; k<orinfo->nnbrs; k++) {
1505           if (pmarker[onbrs[k].pid] == -1)
1506             onbrs[k].gv += vsize[v];
1507         }
1508       }
1509       else { /* There is only one connection */
1510         for (k=0; k<orinfo->nnbrs; k++) {
1511           if (pmarker[onbrs[k].pid] != -1)
1512             onbrs[k].gv -= vsize[v];
1513         }
1514       }
1515     }
1516   }
1517 
1518   for (k=0; k<myrinfo->nnbrs; k++)
1519     pmarker[mynbrs[k].pid] = -1;
1520   pmarker[from] = -1;
1521 
1522 
1523   /*======================================================================
1524    * Update the id/ed of vertex 'v'
1525    *=====================================================================*/
1526   if (myidx == -1) {
1527     myidx = myrinfo->nnbrs++;
1528     ASSERT(myidx < xadj[v+1]-xadj[v]);
1529     mynbrs[myidx].ned = 0;
1530   }
1531   myrinfo->ned += myrinfo->nid-mynbrs[myidx].ned;
1532   SWAP(myrinfo->nid, mynbrs[myidx].ned, j);
1533   if (mynbrs[myidx].ned == 0)
1534     mynbrs[myidx] = mynbrs[--myrinfo->nnbrs];
1535   else
1536     mynbrs[myidx].pid = from;
1537 
1538 
1539   /*======================================================================
1540    * Update the degrees of adjacent vertices and their volume gains
1541    *=====================================================================*/
1542   vmarker[v] = 1;
1543   modind[0]  = v;
1544   nmod       = 1;
1545   for (j=xadj[v]; j<xadj[v+1]; j++) {
1546     ii = adjncy[j];
1547     me = where[ii];
1548 
1549     if (!vmarker[ii]) {  /* The marking is done for boundary and max gv calculations */
1550       vmarker[ii] = 2;
1551       modind[nmod++] = ii;
1552     }
1553 
1554     myrinfo = graph->vkrinfo+ii;
1555     if (myrinfo->inbr == -1)
1556       myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[ii+1]-xadj[ii]+1);
1557     mynbrs = ctrl->vnbrpool + myrinfo->inbr;
1558 
1559     if (me == from) {
1560       INC_DEC(myrinfo->ned, myrinfo->nid, 1);
1561     }
1562     else if (me == to) {
1563       INC_DEC(myrinfo->nid, myrinfo->ned, 1);
1564     }
1565 
1566     /* Remove the edgeweight from the 'pid == from' entry of the vertex */
1567     if (me != from) {
1568       for (k=0; k<myrinfo->nnbrs; k++) {
1569         if (mynbrs[k].pid == from) {
1570           if (mynbrs[k].ned == 1) {
1571             mynbrs[k] = mynbrs[--myrinfo->nnbrs];
1572             vmarker[ii] = 1;  /* You do a complete .gv calculation */
1573 
1574             /* All vertices adjacent to 'ii' need to be updated */
1575             for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
1576               u      = adjncy[jj];
1577               other  = where[u];
1578               orinfo = graph->vkrinfo+u;
1579               onbrs  = ctrl->vnbrpool + orinfo->inbr;
1580 
1581               for (kk=0; kk<orinfo->nnbrs; kk++) {
1582                 if (onbrs[kk].pid == from) {
1583                   onbrs[kk].gv -= vsize[ii];
1584                   if (!vmarker[u]) { /* Need to update boundary etc */
1585                     vmarker[u]      = 2;
1586                     modind[nmod++] = u;
1587                   }
1588                   break;
1589                 }
1590               }
1591             }
1592           }
1593           else {
1594             mynbrs[k].ned--;
1595 
1596             /* Update the gv due to single 'ii' connection to 'from' */
1597             if (mynbrs[k].ned == 1) {
1598               /* find the vertex 'u' that 'ii' was connected into 'from' */
1599               for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
1600                 u     = adjncy[jj];
1601                 other = where[u];
1602 
1603                 if (other == from) {
1604                   orinfo = graph->vkrinfo+u;
1605                   onbrs  = ctrl->vnbrpool + orinfo->inbr;
1606 
1607                   /* The following is correct because domains in common
1608                      between ii and u will lead to a reduction over the
1609                      previous gain, whereas domains only in u but not in
1610                      ii, will lead to no change as opposed to the earlier
1611                      increase */
1612                   for (kk=0; kk<orinfo->nnbrs; kk++)
1613                     onbrs[kk].gv += vsize[ii];
1614 
1615                   if (!vmarker[u]) { /* Need to update boundary etc */
1616                     vmarker[u]     = 2;
1617                     modind[nmod++] = u;
1618                   }
1619                   break;
1620                 }
1621               }
1622             }
1623           }
1624           break;
1625         }
1626       }
1627     }
1628 
1629 
1630     /* Add the edgeweight to the 'pid == to' entry of the vertex */
1631     if (me != to) {
1632       for (k=0; k<myrinfo->nnbrs; k++) {
1633         if (mynbrs[k].pid == to) {
1634           mynbrs[k].ned++;
1635 
1636           /* Update the gv due to non-single 'ii' connection to 'to' */
1637           if (mynbrs[k].ned == 2) {
1638             /* find the vertex 'u' that 'ii' was connected into 'to' */
1639             for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
1640               u     = adjncy[jj];
1641               other = where[u];
1642 
1643               if (u != v && other == to) {
1644                 orinfo = graph->vkrinfo+u;
1645                 onbrs  = ctrl->vnbrpool + orinfo->inbr;
1646                 for (kk=0; kk<orinfo->nnbrs; kk++)
1647                   onbrs[kk].gv -= vsize[ii];
1648 
1649                 if (!vmarker[u]) { /* Need to update boundary etc */
1650                   vmarker[u]      = 2;
1651                   modind[nmod++] = u;
1652                 }
1653                 break;
1654               }
1655             }
1656           }
1657           break;
1658         }
1659       }
1660 
1661       if (k == myrinfo->nnbrs) {
1662         mynbrs[myrinfo->nnbrs].pid   = to;
1663         mynbrs[myrinfo->nnbrs++].ned = 1;
1664         vmarker[ii] = 1;  /* You do a complete .gv calculation */
1665 
1666         /* All vertices adjacent to 'ii' need to be updated */
1667         for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
1668           u      = adjncy[jj];
1669           other  = where[u];
1670           orinfo = graph->vkrinfo+u;
1671           onbrs  = ctrl->vnbrpool + orinfo->inbr;
1672 
1673           for (kk=0; kk<orinfo->nnbrs; kk++) {
1674             if (onbrs[kk].pid == to) {
1675               onbrs[kk].gv += vsize[ii];
1676               if (!vmarker[u]) { /* Need to update boundary etc */
1677                 vmarker[u] = 2;
1678                 modind[nmod++] = u;
1679               }
1680               break;
1681             }
1682           }
1683         }
1684       }
1685     }
1686 
1687     ASSERT(myrinfo->nnbrs <= xadj[ii+1]-xadj[ii]);
1688   }
1689 
1690 
1691   /*======================================================================
1692    * Add the contributions on the volume gain due to 'v'
1693    *=====================================================================*/
1694   myrinfo = graph->vkrinfo+v;
1695   mynbrs  = ctrl->vnbrpool + myrinfo->inbr;
1696   for (k=0; k<myrinfo->nnbrs; k++)
1697     pmarker[mynbrs[k].pid] = k;
1698   pmarker[to] = k;
1699 
1700   for (j=xadj[v]; j<xadj[v+1]; j++) {
1701     ii     = adjncy[j];
1702     other  = where[ii];
1703     orinfo = graph->vkrinfo+ii;
1704     onbrs  = ctrl->vnbrpool + orinfo->inbr;
1705 
1706     if (other == to) {
1707       for (k=0; k<orinfo->nnbrs; k++) {
1708         if (pmarker[onbrs[k].pid] == -1)
1709           onbrs[k].gv -= vsize[v];
1710       }
1711     }
1712     else {
1713       ASSERT(pmarker[other] != -1);
1714 
1715       if (mynbrs[pmarker[other]].ned > 1) {
1716         for (k=0; k<orinfo->nnbrs; k++) {
1717           if (pmarker[onbrs[k].pid] == -1)
1718             onbrs[k].gv -= vsize[v];
1719         }
1720       }
1721       else { /* There is only one connection */
1722         for (k=0; k<orinfo->nnbrs; k++) {
1723           if (pmarker[onbrs[k].pid] != -1)
1724             onbrs[k].gv += vsize[v];
1725         }
1726       }
1727     }
1728   }
1729   for (k=0; k<myrinfo->nnbrs; k++)
1730     pmarker[mynbrs[k].pid] = -1;
1731   pmarker[to] = -1;
1732 
1733 
1734   /*======================================================================
1735    * Recompute the volume information of the 'hard' nodes, and update the
1736    * max volume gain for all the modified vertices and the priority queue
1737    *=====================================================================*/
1738   for (iii=0; iii<nmod; iii++) {
1739     i  = modind[iii];
1740     me = where[i];
1741 
1742     myrinfo = graph->vkrinfo+i;
1743     mynbrs  = ctrl->vnbrpool + myrinfo->inbr;
1744 
1745     if (vmarker[i] == 1) {  /* Only complete gain updates go through */
1746       for (k=0; k<myrinfo->nnbrs; k++)
1747         mynbrs[k].gv = 0;
1748 
1749       for (j=xadj[i]; j<xadj[i+1]; j++) {
1750         ii     = adjncy[j];
1751         other  = where[ii];
1752         orinfo = graph->vkrinfo+ii;
1753         onbrs  = ctrl->vnbrpool + orinfo->inbr;
1754 
1755         for (kk=0; kk<orinfo->nnbrs; kk++)
1756           pmarker[onbrs[kk].pid] = kk;
1757         pmarker[other] = 1;
1758 
1759         if (me == other) {
1760           /* Find which domains 'i' is connected and 'ii' is not and update their gain */
1761           for (k=0; k<myrinfo->nnbrs; k++) {
1762             if (pmarker[mynbrs[k].pid] == -1)
1763               mynbrs[k].gv -= vsize[ii];
1764           }
1765         }
1766         else {
1767           ASSERT(pmarker[me] != -1);
1768 
1769           /* I'm the only connection of 'ii' in 'me' */
1770           if (onbrs[pmarker[me]].ned == 1) {
1771             /* Increase the gains for all the common domains between 'i' and 'ii' */
1772             for (k=0; k<myrinfo->nnbrs; k++) {
1773               if (pmarker[mynbrs[k].pid] != -1)
1774                 mynbrs[k].gv += vsize[ii];
1775             }
1776           }
1777           else {
1778             /* Find which domains 'i' is connected and 'ii' is not and update their gain */
1779             for (k=0; k<myrinfo->nnbrs; k++) {
1780               if (pmarker[mynbrs[k].pid] == -1)
1781                 mynbrs[k].gv -= vsize[ii];
1782             }
1783           }
1784         }
1785 
1786         for (kk=0; kk<orinfo->nnbrs; kk++)
1787           pmarker[onbrs[kk].pid] = -1;
1788         pmarker[other] = -1;
1789 
1790       }
1791     }
1792 
1793     /* Compute the overall gv for that node */
1794     myrinfo->gv = IDX_MIN;
1795     for (k=0; k<myrinfo->nnbrs; k++) {
1796       if (mynbrs[k].gv > myrinfo->gv)
1797         myrinfo->gv = mynbrs[k].gv;
1798     }
1799 
1800     /* Add the xtra gain due to id == 0 */
1801     if (myrinfo->ned > 0 && myrinfo->nid == 0)
1802       myrinfo->gv += vsize[i];
1803 
1804 
1805     /*======================================================================
1806      * Maintain a consistent boundary
1807      *=====================================================================*/
1808     if (bndtype == BNDTYPE_REFINE) {
1809       if (myrinfo->gv >= 0 && graph->bndptr[i] == -1)
1810         BNDInsert(graph->nbnd, graph->bndind, graph->bndptr, i);
1811 
1812       if (myrinfo->gv < 0 && graph->bndptr[i] != -1)
1813         BNDDelete(graph->nbnd, graph->bndind, graph->bndptr, i);
1814     }
1815     else {
1816       if (myrinfo->ned > 0 && graph->bndptr[i] == -1)
1817         BNDInsert(graph->nbnd, graph->bndind, graph->bndptr, i);
1818 
1819       if (myrinfo->ned == 0 && graph->bndptr[i] != -1)
1820         BNDDelete(graph->nbnd, graph->bndind, graph->bndptr, i);
1821     }
1822 
1823 
1824     /*======================================================================
1825      * Update the priority queue appropriately (if allowed)
1826      *=====================================================================*/
1827     if (queue != NULL) {
1828       if (vstatus[i] != VPQSTATUS_EXTRACTED) {
1829         if (graph->bndptr[i] != -1) { /* In-boundary vertex */
1830           if (vstatus[i] == VPQSTATUS_PRESENT) {
1831             ipqUpdate(queue, i, myrinfo->gv);
1832           }
1833           else {
1834             ipqInsert(queue, i, myrinfo->gv);
1835             vstatus[i] = VPQSTATUS_PRESENT;
1836             ListInsert(*r_nupd, updind, updptr, i);
1837           }
1838         }
1839         else { /* Off-boundary vertex */
1840           if (vstatus[i] == VPQSTATUS_PRESENT) {
1841             ipqDelete(queue, i);
1842             vstatus[i] = VPQSTATUS_NOTPRESENT;
1843             ListDelete(*r_nupd, updind, updptr, i);
1844           }
1845         }
1846       }
1847     }
1848 
1849     vmarker[i] = 0;
1850   }
1851 }
1852 
1853