/*! \file \brief Functions that deal with prunning the number of adjacent subdomains in kmetis \date Started 7/15/98 \author George \author Copyright 1997-2009, Regents of the University of Minnesota \version $Id: minconn.c 10513 2011-07-07 22:06:03Z karypis $ */ #include "metislib.h" /*************************************************************************/ /*! This function computes the subdomain graph storing the result in the pre-allocated worspace arrays */ /*************************************************************************/ void ComputeSubDomainGraph(ctrl_t *ctrl, graph_t *graph) { idx_t i, ii, j, pid, other, nparts, nvtxs, nnbrs; idx_t *xadj, *adjncy, *adjwgt, *where; idx_t *pptr, *pind; idx_t nads=0, *vadids, *vadwgts; WCOREPUSH; nvtxs = graph->nvtxs; xadj = graph->xadj; adjncy = graph->adjncy; adjwgt = graph->adjwgt; where = graph->where; nparts = ctrl->nparts; vadids = ctrl->pvec1; vadwgts = iset(nparts, 0, ctrl->pvec2); pptr = iwspacemalloc(ctrl, nparts+1); pind = iwspacemalloc(ctrl, nvtxs); iarray2csr(nvtxs, nparts, where, pptr, pind); for (pid=0; pidobjtype) { case METIS_OBJTYPE_CUT: { ckrinfo_t *rinfo; cnbr_t *nbrs; rinfo = graph->ckrinfo; for (nads=0, ii=pptr[pid]; ii 0) { nnbrs = rinfo[i].nnbrs; nbrs = ctrl->cnbrpool + rinfo[i].inbr; for (j=0; jvkrinfo; for (nads=0, ii=pptr[pid]; ii 0) { nnbrs = rinfo[i].nnbrs; nbrs = ctrl->vnbrpool + rinfo[i].inbr; for (j=0; jobjtype); } /* See if you have enough memory to store the adjacent info for that subdomain */ if (ctrl->maxnads[pid] < nads) { ctrl->maxnads[pid] = 2*nads; ctrl->adids[pid] = irealloc(ctrl->adids[pid], ctrl->maxnads[pid], "ComputeSubDomainGraph: adids[pid]"); ctrl->adwgts[pid] = irealloc(ctrl->adwgts[pid], ctrl->maxnads[pid], "ComputeSubDomainGraph: adids[pid]"); } ctrl->nads[pid] = nads; for (j=0; jadids[pid][j] = vadids[j]; ctrl->adwgts[pid][j] = vadwgts[vadids[j]]; vadwgts[vadids[j]] = 0; } } WCOREPOP; } /*************************************************************************/ /*! This function updates the weight of an edge in the subdomain graph by adding to it the value of ewgt. The update can either increase or decrease the weight of the subdomain edge based on the value of ewgt. \param u is the ID of one of the incident subdomains to the edge \param v is the ID of the other incident subdomains to the edge \param ewgt is the weight to be added to the subdomain edge \param nparts is the number of subdomains \param r_maxndoms is the maximum number of adjacent subdomains and is updated as necessary. The update is skipped if a NULL value is supplied. */ /*************************************************************************/ void UpdateEdgeSubDomainGraph(ctrl_t *ctrl, idx_t u, idx_t v, idx_t ewgt, idx_t *r_maxndoms) { idx_t i, j, nads; if (ewgt == 0) return; for (i=0; i<2; i++) { nads = ctrl->nads[u]; /* Find the edge */ for (j=0; jadids[u][j] == v) { ctrl->adwgts[u][j] += ewgt; break; } } if (j == nads) { /* Deal with the case in which the edge was not found */ ASSERT(ewgt > 0); if (ctrl->maxnads[u] == nads) { ctrl->maxnads[u] = 2*(nads+1); ctrl->adids[u] = irealloc(ctrl->adids[u], ctrl->maxnads[u], "IncreaseEdgeSubDomainGraph: adids[pid]"); ctrl->adwgts[u] = irealloc(ctrl->adwgts[u], ctrl->maxnads[u], "IncreaseEdgeSubDomainGraph: adids[pid]"); } ctrl->adids[u][nads] = v; ctrl->adwgts[u][nads] = ewgt; nads++; if (r_maxndoms != NULL && nads > *r_maxndoms) { printf("You just increased the maxndoms: %"PRIDX" %"PRIDX"\n", nads, *r_maxndoms); *r_maxndoms = nads; } } else { /* See if the updated edge becomes 0 */ ASSERT(ctrl->adwgts[u][j] >= 0); if (ctrl->adwgts[u][j] == 0) { ctrl->adids[u][j] = ctrl->adids[u][nads-1]; ctrl->adwgts[u][j] = ctrl->adwgts[u][nads-1]; nads--; if (r_maxndoms != NULL && nads+1 == *r_maxndoms) *r_maxndoms = ctrl->nads[iargmax(ctrl->nparts, ctrl->nads)]; } } ctrl->nads[u] = nads; SWAP(u, v, j); } } /*************************************************************************/ /*! This function computes the subdomain graph */ /*************************************************************************/ void EliminateSubDomainEdges(ctrl_t *ctrl, graph_t *graph) { idx_t i, ii, j, k, ncon, nparts, scheme, pid_from, pid_to, me, other, nvtxs, total, max, avg, totalout, nind=0, ncand=0, ncand2, target, target2, nadd, bestnadd=0; idx_t min, move, *cpwgt; idx_t *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *maxpwgt, *mypmat, *otherpmat, *kpmat, *ind; idx_t *nads, **adids, **adwgts; ikv_t *cand, *cand2; ipq_t queue; real_t *tpwgts, badfactor=1.4; idx_t *pptr, *pind; idx_t *vmarker=NULL, *pmarker=NULL, *modind=NULL; /* volume specific work arrays */ WCOREPUSH; nvtxs = graph->nvtxs; ncon = graph->ncon; xadj = graph->xadj; adjncy = graph->adjncy; vwgt = graph->vwgt; adjwgt = (ctrl->objtype == METIS_OBJTYPE_VOL ? NULL : graph->adjwgt); where = graph->where; pwgts = graph->pwgts; /* We assume that this is properly initialized */ nparts = ctrl->nparts; tpwgts = ctrl->tpwgts; cpwgt = iwspacemalloc(ctrl, ncon); maxpwgt = iwspacemalloc(ctrl, nparts*ncon); ind = iwspacemalloc(ctrl, nvtxs); otherpmat = iset(nparts, 0, iwspacemalloc(ctrl, nparts)); cand = ikvwspacemalloc(ctrl, nparts); cand2 = ikvwspacemalloc(ctrl, nparts); pptr = iwspacemalloc(ctrl, nparts+1); pind = iwspacemalloc(ctrl, nvtxs); iarray2csr(nvtxs, nparts, where, pptr, pind); if (ctrl->objtype == METIS_OBJTYPE_VOL) { /* Vol-refinement specific working arrays */ modind = iwspacemalloc(ctrl, nvtxs); vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts)); } /* Compute the pmat matrix and ndoms */ ComputeSubDomainGraph(ctrl, graph); nads = ctrl->nads; adids = ctrl->adids; adwgts = ctrl->adwgts; mypmat = iset(nparts, 0, ctrl->pvec1); kpmat = iset(nparts, 0, ctrl->pvec2); /* Compute the maximum allowed weight for each domain */ for (i=0; itvwgt[j]*ctrl->ubfactors[j]; } ipqInit(&queue, nparts); /* Get into the loop eliminating subdomain connections */ while (1) { total = isum(nparts, nads, 1); avg = total/nparts; max = nads[iargmax(nparts, nads)]; IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO, printf("Adjacent Subdomain Stats: Total: %3"PRIDX", " "Max: %3"PRIDX"[%zu], Avg: %3"PRIDX"\n", total, max, iargmax(nparts, nads), avg)); if (max < badfactor*avg) break; /* Add the subdomains that you will try to reduce their connectivity */ ipqReset(&queue); for (i=0; i= avg + (max-avg)/2) ipqInsert(&queue, i, nads[i]); } move = 0; while ((me = ipqGetTop(&queue)) != -1) { totalout = isum(nads[me], adwgts[me], 1); for (ncand2=0, i=0; idbglvl, METIS_DBG_CONNINFO, printf("Me: %"PRIDX", Degree: %4"PRIDX", TotalOut: %"PRIDX",\n", me, nads[me], totalout)); /* Sort the connections according to their cut */ ikvsorti(ncand2, cand2); /* Two schemes are used for eliminating subdomain edges. The first, tries to eliminate subdomain edges by moving remote groups of vertices to subdomains that 'me' is already connected to. The second, tries to eliminate subdomain edges by moving entire sets of my vertices that connect to the 'other' subdomain to a subdomain that I'm already connected to. These two schemes are applied in sequence. */ target = target2 = -1; for (scheme=0; scheme<2; scheme++) { for (min=0; min 0); } ikvsortd(ncand, cand); IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO, printf("\tMinOut: %4"PRIDX", to: %3"PRIDX", TtlWgt: %5"PRIDX"[#:%"PRIDX"]\n", mypmat[other], other, isum(ncon, cpwgt, 1), nind)); /* Go through and select the first domain that is common with 'me', and does not increase the nads[target] higher than nads[me], subject to the maxpwgt constraint. Traversal is done from the mostly connected to the least. */ for (i=0; i 0) { /* Check if balance will go off */ if (!ivecaxpylez(ncon, 1, cpwgt, pwgts+k*ncon, maxpwgt+k*ncon)) continue; /* get a dense vector out of k's connectivity */ for (j=0; j 0 && kpmat[j] == 0 && nads[j]+1 >= nads[me]) break; } /* There were no bad second level effects. See if you can find a subdomain to move to. */ if (j == nparts) { for (nadd=0, j=0; j 0 && kpmat[j] == 0) nadd++; } IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO, printf("\t\tto=%"PRIDX", nadd=%"PRIDX", %"PRIDX"\n", k, nadd, nads[k])); if (nads[k]+nadd < nads[me]) { if (target2 == -1 || nads[target2]+bestnadd > nads[k]+nadd || (nads[target2]+bestnadd == nads[k]+nadd && bestnadd > nadd)) { target2 = k; bestnadd = nadd; } } if (nadd == 0) target = k; } /* reset kpmat for the next iteration */ for (j=0; jdbglvl, METIS_DBG_CONNINFO, printf("\t\tScheme: %"PRIDX". Moving to %"PRIDX"\n", scheme, target)); move = 1; break; } } if (target != -1) break; /* A move was found. No need to try the other scheme */ } /* reset the mypmat for next iteration */ for (i=0; iobjtype) { case METIS_OBJTYPE_CUT: MoveGroupMinConnForCut(ctrl, graph, target, nind, ind); break; case METIS_OBJTYPE_VOL: MoveGroupMinConnForVol(ctrl, graph, target, nind, ind, vmarker, pmarker, modind); break; default: gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); } /* Update the csr representation of the partitioning vector */ iarray2csr(nvtxs, nparts, where, pptr, pind); } } if (move == 0) break; } ipqFree(&queue); WCOREPOP; } /*************************************************************************/ /*! This function moves a collection of vertices and updates their rinfo */ /*************************************************************************/ void MoveGroupMinConnForCut(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind, idx_t *ind) { idx_t i, ii, j, jj, k, l, nvtxs, nbnd, from, me; idx_t *xadj, *adjncy, *adjwgt, *where, *bndptr, *bndind; ckrinfo_t *myrinfo; cnbr_t *mynbrs; nvtxs = graph->nvtxs; xadj = graph->xadj; adjncy = graph->adjncy; adjwgt = graph->adjwgt; where = graph->where; bndptr = graph->bndptr; bndind = graph->bndind; nbnd = graph->nbnd; while (--nind>=0) { i = ind[nind]; from = where[i]; myrinfo = graph->ckrinfo+i; if (myrinfo->inbr == -1) { myrinfo->inbr = cnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); myrinfo->nnbrs = 0; } mynbrs = ctrl->cnbrpool + myrinfo->inbr; /* find the location of 'to' in myrinfo or create it if it is not there */ for (k=0; knnbrs; k++) { if (mynbrs[k].pid == to) break; } if (k == myrinfo->nnbrs) { ASSERT(k < xadj[i+1]-xadj[i]); mynbrs[k].pid = to; mynbrs[k].ed = 0; myrinfo->nnbrs++; } /* Update pwgts */ iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1); iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1); /* Update mincut */ graph->mincut -= mynbrs[k].ed-myrinfo->id; /* Update subdomain connectivity graph to reflect the move of 'i' */ UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->id-mynbrs[k].ed, NULL); /* Update ID/ED and BND related information for the moved vertex */ UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where, nbnd, bndptr, bndind, BNDTYPE_REFINE); /* Update the degrees of adjacent vertices */ for (j=xadj[i]; jckrinfo+ii; UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me, from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, BNDTYPE_REFINE); /* Update subdomain graph to reflect the move of 'i' for domains other than 'from' and 'to' */ if (me != from && me != to) { UpdateEdgeSubDomainGraph(ctrl, from, me, -adjwgt[j], NULL); UpdateEdgeSubDomainGraph(ctrl, to, me, adjwgt[j], NULL); } } } ASSERT(ComputeCut(graph, where) == graph->mincut); graph->nbnd = nbnd; } /*************************************************************************/ /*! This function moves a collection of vertices and updates their rinfo */ /*************************************************************************/ void MoveGroupMinConnForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind, idx_t *ind, idx_t *vmarker, idx_t *pmarker, idx_t *modind) { idx_t i, ii, j, jj, k, l, nvtxs, from, me, other, xgain, ewgt; idx_t *xadj, *vsize, *adjncy, *where; vkrinfo_t *myrinfo, *orinfo; vnbr_t *mynbrs, *onbrs; nvtxs = graph->nvtxs; xadj = graph->xadj; vsize = graph->vsize; adjncy = graph->adjncy; where = graph->where; while (--nind>=0) { i = ind[nind]; from = where[i]; myrinfo = graph->vkrinfo+i; if (myrinfo->inbr == -1) { myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); myrinfo->nnbrs = 0; } mynbrs = ctrl->vnbrpool + myrinfo->inbr; xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? vsize[i] : 0); //printf("Moving %"PRIDX" from %"PRIDX" to %"PRIDX" [vsize: %"PRIDX"] [xgain: %"PRIDX"]\n", // i, from, to, vsize[i], xgain); /* find the location of 'to' in myrinfo or create it if it is not there */ for (k=0; knnbrs; k++) { if (mynbrs[k].pid == to) break; } if (k == myrinfo->nnbrs) { //printf("Missing neighbor\n"); if (myrinfo->nid > 0) xgain -= vsize[i]; /* determine the volume gain resulting from that move */ for (j=xadj[i]; jvkrinfo+ii; onbrs = ctrl->vnbrpool + orinfo->inbr; ASSERT(other != to) //printf(" %8d %8d %3d\n", (int)ii, (int)vsize[ii], (int)other); if (from == other) { /* Same subdomain vertex: Decrease the gain if 'to' is a new neighbor. */ for (l=0; lnnbrs; l++) { if (onbrs[l].pid == to) break; } if (l == orinfo->nnbrs) xgain -= vsize[ii]; } else { /* Remote vertex: increase if 'to' is a new subdomain */ for (l=0; lnnbrs; l++) { if (onbrs[l].pid == to) break; } if (l == orinfo->nnbrs) xgain -= vsize[ii]; /* Remote vertex: decrease if i is the only connection to 'from' */ for (l=0; lnnbrs; l++) { if (onbrs[l].pid == from && onbrs[l].ned == 1) { xgain += vsize[ii]; break; } } } } graph->minvol -= xgain; graph->mincut -= -myrinfo->nid; ewgt = myrinfo->nid; } else { graph->minvol -= (xgain + mynbrs[k].gv); graph->mincut -= mynbrs[k].ned-myrinfo->nid; ewgt = myrinfo->nid-mynbrs[k].ned; } /* Update where and pwgts */ where[i] = to; iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1); iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1); /* Update subdomain connectivity graph to reflect the move of 'i' */ UpdateEdgeSubDomainGraph(ctrl, from, to, ewgt, NULL); /* Update the subdomain connectivity of the adjacent vertices */ for (j=xadj[i]; jmincut); ASSERTP(ComputeVolume(graph, where) == graph->minvol, ("%"PRIDX" %"PRIDX"\n", ComputeVolume(graph, where), graph->minvol)); } /*************************************************************************/ /*! This function computes the subdomain graph. For deubuging purposes. */ /*************************************************************************/ void PrintSubDomainGraph(graph_t *graph, idx_t nparts, idx_t *where) { idx_t i, j, k, me, nvtxs, total, max; idx_t *xadj, *adjncy, *adjwgt, *pmat; nvtxs = graph->nvtxs; xadj = graph->xadj; adjncy = graph->adjncy; adjwgt = graph->adjwgt; pmat = ismalloc(nparts*nparts, 0, "ComputeSubDomainGraph: pmat"); for (i=0; i 0) k++; } total += k; if (k > max) max = k; /* printf("%2"PRIDX" -> %2"PRIDX" ", i, k); for (j=0; j 0) printf("[%2"PRIDX" %4"PRIDX"] ", j, pmat[i*nparts+j]); } printf("\n"); */ } printf("Total adjacent subdomains: %"PRIDX", Max: %"PRIDX"\n", total, max); gk_free((void **)&pmat, LTERM); }