/*! \file \brief Functions that deal with eliminating disconnected partitions \date Started 7/15/98 \author George \author Copyright 1997-2009, Regents of the University of Minnesota \version $Id: contig.c 10513 2011-07-07 22:06:03Z karypis $ */ #include "metislib.h" /*************************************************************************/ /*! This function finds the connected components induced by the partitioning vector. \param graph is the graph structure \param where is the partitioning vector. If this is NULL, then the entire graph is treated to belong into a single partition. \param cptr is the ptr structure of the CSR representation of the components. The length of this vector must be graph->nvtxs+1. \param cind is the indices structure of the CSR representation of the components. The length of this vector must be graph->nvtxs. \returns the number of components that it found. \note The cptr and cind parameters can be NULL, in which case only the number of connected components is returned. */ /*************************************************************************/ idx_t FindPartitionInducedComponents(graph_t *graph, idx_t *where, idx_t *cptr, idx_t *cind) { idx_t i, ii, j, jj, k, me=0, nvtxs, first, last, nleft, ncmps; idx_t *xadj, *adjncy; idx_t *touched, *perm, *todo; idx_t mustfree_ccsr=0, mustfree_where=0; nvtxs = graph->nvtxs; xadj = graph->xadj; adjncy = graph->adjncy; /* Deal with NULL supplied cptr/cind vectors */ if (cptr == NULL) { cptr = imalloc(nvtxs+1, "FindPartitionInducedComponents: cptr"); cind = imalloc(nvtxs, "FindPartitionInducedComponents: cind"); mustfree_ccsr = 1; } /* Deal with NULL supplied where vector */ if (where == NULL) { where = ismalloc(nvtxs, 0, "FindPartitionInducedComponents: where"); mustfree_where = 1; } /* Allocate memory required for the BFS traversal */ perm = iincset(nvtxs, 0, imalloc(nvtxs, "FindPartitionInducedComponents: perm")); todo = iincset(nvtxs, 0, imalloc(nvtxs, "FindPartitionInducedComponents: todo")); touched = ismalloc(nvtxs, 0, "FindPartitionInducedComponents: touched"); /* Find the connected componends induced by the partition */ ncmps = -1; first = last = 0; nleft = nvtxs; while (nleft > 0) { if (first == last) { /* Find another starting vertex */ cptr[++ncmps] = first; ASSERT(touched[todo[0]] == 0); i = todo[0]; cind[last++] = i; touched[i] = 1; me = where[i]; } i = cind[first++]; k = perm[i]; j = todo[k] = todo[--nleft]; perm[j] = k; for (j=xadj[i]; jnvtxs; xadj = graph->xadj; adjncy = graph->adjncy; /* Allocate memory required for the BFS traversal */ perm = iincset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); iincset(nvtxs, 0, bfsperm); /* this array will also store the vertices still to be processed */ /* Find the connected componends induced by the partition */ first = last = 0; while (first < nvtxs) { if (first == last) { /* Find another starting vertex */ k = bfsperm[last]; ASSERT(perm[k] != -1); perm[k] = -1; /* mark node as being visited */ last++; } i = bfsperm[first++]; for (j=xadj[i]; jnvtxs; xadj = graph->xadj; adjncy = graph->adjncy; where = graph->where; touched = ismalloc(nvtxs, 0, "IsConnected: touched"); queue = imalloc(nvtxs, "IsConnected: queue"); cptr = imalloc(nvtxs+1, "IsConnected: cptr"); nleft = 0; for (i=0; i 1 && report) { printf("The graph has %"PRIDX" connected components in partition %"PRIDX":\t", ncmps, pid); for (i=0; ivwgt[queue[j]]; printf("[%5"PRIDX" %5"PRIDX"] ", cptr[i+1]-cptr[i], wgt); /* if (cptr[i+1]-cptr[i] == 1) printf("[%"PRIDX" %"PRIDX"] ", queue[cptr[i]], xadj[queue[cptr[i]]+1]-xadj[queue[cptr[i]]]); */ } printf("\n"); } gk_free((void **)&touched, &queue, &cptr, LTERM); return (ncmps == 1 ? 1 : 0); } /*************************************************************************/ /*! This function identifies the number of connected components in a graph that result after removing the vertices that belong to the vertex separator (i.e., graph->where[i] == 2). The connected component memberships are returned in the CSR-style pair of arrays cptr, cind. */ /**************************************************************************/ idx_t FindSepInducedComponents(ctrl_t *ctrl, graph_t *graph, idx_t *cptr, idx_t *cind) { idx_t i, j, k, nvtxs, first, last, nleft, ncmps, wgt; idx_t *xadj, *adjncy, *where, *touched, *queue; nvtxs = graph->nvtxs; xadj = graph->xadj; adjncy = graph->adjncy; where = graph->where; touched = ismalloc(nvtxs, 0, "IsConnected: queue"); for (i=0; inbnd; i++) touched[graph->bndind[i]] = 1; queue = cind; nleft = 0; for (i=0; iwhere and tries to push them around to remove some of them. */ /*************************************************************************/ void EliminateComponents(ctrl_t *ctrl, graph_t *graph) { idx_t i, ii, j, jj, k, me, nparts, nvtxs, ncon, ncmps, other, ncand, target; idx_t *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts; idx_t *cptr, *cind, *cpvec, *pcptr, *pcind, *cwhere; idx_t cid, bestcid, *cwgt, *bestcwgt; idx_t ntodo, oldntodo, *todo; rkv_t *cand; real_t *tpwgts; 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; nparts = ctrl->nparts; tpwgts = ctrl->tpwgts; cptr = iwspacemalloc(ctrl, nvtxs+1); cind = iwspacemalloc(ctrl, nvtxs); ncmps = FindPartitionInducedComponents(graph, where, cptr, cind); IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO, printf("I found %"PRIDX" components, for this %"PRIDX"-way partition\n", ncmps, nparts)); /* There are more components than partitions */ if (ncmps > nparts) { cwgt = iwspacemalloc(ctrl, ncon); bestcwgt = iwspacemalloc(ctrl, ncon); cpvec = iwspacemalloc(ctrl, nparts); pcptr = iset(nparts+1, 0, iwspacemalloc(ctrl, nparts+1)); pcind = iwspacemalloc(ctrl, ncmps); cwhere = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); todo = iwspacemalloc(ctrl, ncmps); cand = (rkv_t *)wspacemalloc(ctrl, nparts*sizeof(rkv_t)); 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)); } /* Get a CSR representation of the components-2-partitions mapping */ for (i=0; i 0) { oldntodo = ntodo; for (i=0; idbglvl, METIS_DBG_CONTIGINFO, printf("Trying to move %"PRIDX" [%"PRIDX"] from %"PRIDX"\n", cid, isum(ncon, cwgt, 1), me)); /* Determine the connectivity */ iset(nparts, 0, cpvec); for (j=cptr[cid]; j 0) { cand[ncand].key = cpvec[j]; cand[ncand++].val = j; } } if (ncand == 0) continue; rkvsortd(ncand, cand); /* Limit the moves to only the top candidates, which are defined as those with connectivity at least 50% of the best. This applies only when ncon=1, as for multi-constraint, balancing will be hard. */ if (ncon == 1) { for (j=1; jubfactors, 1, pwgts+target*ncon, ctrl->pijbm+target*ncon, 1, pwgts+cand[j].val*ncon, ctrl->pijbm+cand[j].val*ncon)) target = cand[j].val; } IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO, printf("\tMoving it to %"PRIDX" [%"PRIDX"] [%"PRIDX"]\n", target, cpvec[target], ncand)); /* Note that as a result of a previous movement, a connected component may now will like to stay to its original partition */ if (target != me) { switch (ctrl->objtype) { case METIS_OBJTYPE_CUT: MoveGroupContigForCut(ctrl, graph, target, cid, cptr, cind); break; case METIS_OBJTYPE_VOL: MoveGroupContigForVol(ctrl, graph, target, cid, cptr, cind, vmarker, pmarker, modind); break; default: gk_errexit(SIGERR, "Unknown objtype %d\n", ctrl->objtype); } } /* Update the cwhere vector */ for (j=cptr[cid]; jdbglvl, METIS_DBG_CONTIGINFO, printf("Stopped at ntodo: %"PRIDX"\n", ntodo)); break; } } for (i=0; invtxs; xadj = graph->xadj; adjncy = graph->adjncy; adjwgt = graph->adjwgt; where = graph->where; bndptr = graph->bndptr; bndind = graph->bndind; nbnd = graph->nbnd; for (iii=ptr[gid]; iiickrinfo+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) { mynbrs[k].pid = to; mynbrs[k].ed = 0; myrinfo->nnbrs++; } graph->mincut -= mynbrs[k].ed-myrinfo->id; /* Update ID/ED and BND related information for the moved vertex */ 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); 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); } ASSERT(CheckRInfo(ctrl, graph->ckrinfo+i)); } graph->nbnd = nbnd; } /*************************************************************************/ /*! This function moves a collection of vertices and updates their rinfo */ /*************************************************************************/ void MoveGroupContigForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t gid, idx_t *ptr, idx_t *ind, idx_t *vmarker, idx_t *pmarker, idx_t *modind) { idx_t i, ii, iii, j, jj, k, l, nvtxs, from, me, other, xgain; 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; for (iii=ptr[gid]; iiivkrinfo+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); /* 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) { 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) 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; } else { graph->minvol -= (xgain + mynbrs[k].gv); graph->mincut -= mynbrs[k].ned-myrinfo->nid; } /* 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 the id/ed/gains/bnd of potentially affected nodes */ KWayVolUpdate(ctrl, graph, i, from, to, NULL, NULL, NULL, NULL, NULL, BNDTYPE_REFINE, vmarker, pmarker, modind); /*CheckKWayVolPartitionParams(ctrl, graph);*/ } ASSERT(ComputeCut(graph, where) == graph->mincut); ASSERTP(ComputeVolume(graph, where) == graph->minvol, ("%"PRIDX" %"PRIDX"\n", ComputeVolume(graph, where), graph->minvol)); }