Path: blob/devel/elmergrid/src/metis-5.1.0/libmetis/contig.c
3206 views
/*!1\file2\brief Functions that deal with eliminating disconnected partitions34\date Started 7/15/985\author George6\author Copyright 1997-2009, Regents of the University of Minnesota7\version $Id: contig.c 10513 2011-07-07 22:06:03Z karypis $8*/910#include "metislib.h"111213/*************************************************************************/14/*! This function finds the connected components induced by the15partitioning vector.1617\param graph is the graph structure18\param where is the partitioning vector. If this is NULL, then the19entire graph is treated to belong into a single partition.20\param cptr is the ptr structure of the CSR representation of the21components. The length of this vector must be graph->nvtxs+1.22\param cind is the indices structure of the CSR representation of23the components. The length of this vector must be graph->nvtxs.2425\returns the number of components that it found.2627\note The cptr and cind parameters can be NULL, in which case only the28number of connected components is returned.29*/30/*************************************************************************/31idx_t FindPartitionInducedComponents(graph_t *graph, idx_t *where,32idx_t *cptr, idx_t *cind)33{34idx_t i, ii, j, jj, k, me=0, nvtxs, first, last, nleft, ncmps;35idx_t *xadj, *adjncy;36idx_t *touched, *perm, *todo;37idx_t mustfree_ccsr=0, mustfree_where=0;3839nvtxs = graph->nvtxs;40xadj = graph->xadj;41adjncy = graph->adjncy;4243/* Deal with NULL supplied cptr/cind vectors */44if (cptr == NULL) {45cptr = imalloc(nvtxs+1, "FindPartitionInducedComponents: cptr");46cind = imalloc(nvtxs, "FindPartitionInducedComponents: cind");47mustfree_ccsr = 1;48}4950/* Deal with NULL supplied where vector */51if (where == NULL) {52where = ismalloc(nvtxs, 0, "FindPartitionInducedComponents: where");53mustfree_where = 1;54}5556/* Allocate memory required for the BFS traversal */57perm = iincset(nvtxs, 0, imalloc(nvtxs, "FindPartitionInducedComponents: perm"));58todo = iincset(nvtxs, 0, imalloc(nvtxs, "FindPartitionInducedComponents: todo"));59touched = ismalloc(nvtxs, 0, "FindPartitionInducedComponents: touched");606162/* Find the connected componends induced by the partition */63ncmps = -1;64first = last = 0;65nleft = nvtxs;66while (nleft > 0) {67if (first == last) { /* Find another starting vertex */68cptr[++ncmps] = first;69ASSERT(touched[todo[0]] == 0);70i = todo[0];71cind[last++] = i;72touched[i] = 1;73me = where[i];74}7576i = cind[first++];77k = perm[i];78j = todo[k] = todo[--nleft];79perm[j] = k;8081for (j=xadj[i]; j<xadj[i+1]; j++) {82k = adjncy[j];83if (where[k] == me && !touched[k]) {84cind[last++] = k;85touched[k] = 1;86}87}88}89cptr[++ncmps] = first;9091if (mustfree_ccsr)92gk_free((void **)&cptr, &cind, LTERM);93if (mustfree_where)94gk_free((void **)&where, LTERM);9596gk_free((void **)&perm, &todo, &touched, LTERM);9798return ncmps;99}100101102/*************************************************************************/103/*! This function computes a permutation of the vertices based on a104breadth-first-traversal. It can be used for re-ordering the graph105to reduce its bandwidth for better cache locality.106107\param ctrl is the control structure108\param graph is the graph structure109\param perm is the array that upon completion, perm[i] will store110the ID of the vertex that corresponds to the ith vertex in the111re-ordered graph.112*/113/*************************************************************************/114void ComputeBFSOrdering(ctrl_t *ctrl, graph_t *graph, idx_t *bfsperm)115{116idx_t i, j, k, nvtxs, first, last;117idx_t *xadj, *adjncy, *perm;118119WCOREPUSH;120121nvtxs = graph->nvtxs;122xadj = graph->xadj;123adjncy = graph->adjncy;124125/* Allocate memory required for the BFS traversal */126perm = iincset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));127128iincset(nvtxs, 0, bfsperm); /* this array will also store the vertices129still to be processed */130131/* Find the connected componends induced by the partition */132first = last = 0;133while (first < nvtxs) {134if (first == last) { /* Find another starting vertex */135k = bfsperm[last];136ASSERT(perm[k] != -1);137perm[k] = -1; /* mark node as being visited */138last++;139}140141i = bfsperm[first++];142for (j=xadj[i]; j<xadj[i+1]; j++) {143k = adjncy[j];144/* if a node has been already been visited, its perm[] will be -1 */145if (perm[k] != -1) {146/* perm[k] is the location within bfsperm of where k resides;147put in that location bfsperm[last] that we are about to148overwrite and update perm[bfsperm[last]] to reflect that. */149bfsperm[perm[k]] = bfsperm[last];150perm[bfsperm[last]] = perm[k];151152bfsperm[last++] = k; /* put node at the end of the "queue" */153perm[k] = -1; /* mark node as being visited */154}155}156}157158WCOREPOP;159}160161162/*************************************************************************/163/*! This function checks whether a graph is contiguous or not.164*/165/**************************************************************************/166idx_t IsConnected(graph_t *graph, idx_t report)167{168idx_t ncmps;169170ncmps = FindPartitionInducedComponents(graph, NULL, NULL, NULL);171172if (ncmps != 1 && report)173printf("The graph is not connected. It has %"PRIDX" connected components.\n", ncmps);174175return (ncmps == 1);176}177178179/*************************************************************************/180/*! This function checks whether or not partition pid is contigous181*/182/*************************************************************************/183idx_t IsConnectedSubdomain(ctrl_t *ctrl, graph_t *graph, idx_t pid, idx_t report)184{185idx_t i, j, k, nvtxs, first, last, nleft, ncmps, wgt;186idx_t *xadj, *adjncy, *where, *touched, *queue;187idx_t *cptr;188189nvtxs = graph->nvtxs;190xadj = graph->xadj;191adjncy = graph->adjncy;192where = graph->where;193194touched = ismalloc(nvtxs, 0, "IsConnected: touched");195queue = imalloc(nvtxs, "IsConnected: queue");196cptr = imalloc(nvtxs+1, "IsConnected: cptr");197198nleft = 0;199for (i=0; i<nvtxs; i++) {200if (where[i] == pid)201nleft++;202}203204for (i=0; i<nvtxs; i++) {205if (where[i] == pid)206break;207}208209touched[i] = 1;210queue[0] = i;211first = 0; last = 1;212213cptr[0] = 0; /* This actually points to queue */214ncmps = 0;215while (first != nleft) {216if (first == last) { /* Find another starting vertex */217cptr[++ncmps] = first;218for (i=0; i<nvtxs; i++) {219if (where[i] == pid && !touched[i])220break;221}222queue[last++] = i;223touched[i] = 1;224}225226i = queue[first++];227for (j=xadj[i]; j<xadj[i+1]; j++) {228k = adjncy[j];229if (where[k] == pid && !touched[k]) {230queue[last++] = k;231touched[k] = 1;232}233}234}235cptr[++ncmps] = first;236237if (ncmps > 1 && report) {238printf("The graph has %"PRIDX" connected components in partition %"PRIDX":\t", ncmps, pid);239for (i=0; i<ncmps; i++) {240wgt = 0;241for (j=cptr[i]; j<cptr[i+1]; j++)242wgt += graph->vwgt[queue[j]];243printf("[%5"PRIDX" %5"PRIDX"] ", cptr[i+1]-cptr[i], wgt);244/*245if (cptr[i+1]-cptr[i] == 1)246printf("[%"PRIDX" %"PRIDX"] ", queue[cptr[i]], xadj[queue[cptr[i]]+1]-xadj[queue[cptr[i]]]);247*/248}249printf("\n");250}251252gk_free((void **)&touched, &queue, &cptr, LTERM);253254return (ncmps == 1 ? 1 : 0);255}256257258/*************************************************************************/259/*! This function identifies the number of connected components in a graph260that result after removing the vertices that belong to the vertex261separator (i.e., graph->where[i] == 2).262The connected component memberships are returned in the CSR-style263pair of arrays cptr, cind.264*/265/**************************************************************************/266idx_t FindSepInducedComponents(ctrl_t *ctrl, graph_t *graph, idx_t *cptr,267idx_t *cind)268{269idx_t i, j, k, nvtxs, first, last, nleft, ncmps, wgt;270idx_t *xadj, *adjncy, *where, *touched, *queue;271272nvtxs = graph->nvtxs;273xadj = graph->xadj;274adjncy = graph->adjncy;275where = graph->where;276277touched = ismalloc(nvtxs, 0, "IsConnected: queue");278279for (i=0; i<graph->nbnd; i++)280touched[graph->bndind[i]] = 1;281282queue = cind;283284nleft = 0;285for (i=0; i<nvtxs; i++) {286if (where[i] != 2)287nleft++;288}289290for (i=0; i<nvtxs; i++) {291if (where[i] != 2)292break;293}294295touched[i] = 1;296queue[0] = i;297first = 0;298last = 1;299cptr[0] = 0; /* This actually points to queue */300ncmps = 0;301302while (first != nleft) {303if (first == last) { /* Find another starting vertex */304cptr[++ncmps] = first;305for (i=0; i<nvtxs; i++) {306if (!touched[i])307break;308}309queue[last++] = i;310touched[i] = 1;311}312313i = queue[first++];314for (j=xadj[i]; j<xadj[i+1]; j++) {315k = adjncy[j];316if (!touched[k]) {317queue[last++] = k;318touched[k] = 1;319}320}321}322cptr[++ncmps] = first;323324gk_free((void **)&touched, LTERM);325326return ncmps;327}328329330/*************************************************************************/331/*! This function finds all the connected components induced by the332partitioning vector in graph->where and tries to push them around to333remove some of them. */334/*************************************************************************/335void EliminateComponents(ctrl_t *ctrl, graph_t *graph)336{337idx_t i, ii, j, jj, k, me, nparts, nvtxs, ncon, ncmps, other,338ncand, target;339idx_t *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts;340idx_t *cptr, *cind, *cpvec, *pcptr, *pcind, *cwhere;341idx_t cid, bestcid, *cwgt, *bestcwgt;342idx_t ntodo, oldntodo, *todo;343rkv_t *cand;344real_t *tpwgts;345idx_t *vmarker=NULL, *pmarker=NULL, *modind=NULL; /* volume specific work arrays */346347WCOREPUSH;348349nvtxs = graph->nvtxs;350ncon = graph->ncon;351xadj = graph->xadj;352adjncy = graph->adjncy;353vwgt = graph->vwgt;354adjwgt = (ctrl->objtype == METIS_OBJTYPE_VOL ? NULL : graph->adjwgt);355356where = graph->where;357pwgts = graph->pwgts;358359nparts = ctrl->nparts;360tpwgts = ctrl->tpwgts;361362cptr = iwspacemalloc(ctrl, nvtxs+1);363cind = iwspacemalloc(ctrl, nvtxs);364365ncmps = FindPartitionInducedComponents(graph, where, cptr, cind);366367IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO,368printf("I found %"PRIDX" components, for this %"PRIDX"-way partition\n",369ncmps, nparts));370371/* There are more components than partitions */372if (ncmps > nparts) {373cwgt = iwspacemalloc(ctrl, ncon);374bestcwgt = iwspacemalloc(ctrl, ncon);375cpvec = iwspacemalloc(ctrl, nparts);376pcptr = iset(nparts+1, 0, iwspacemalloc(ctrl, nparts+1));377pcind = iwspacemalloc(ctrl, ncmps);378cwhere = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs));379todo = iwspacemalloc(ctrl, ncmps);380cand = (rkv_t *)wspacemalloc(ctrl, nparts*sizeof(rkv_t));381382if (ctrl->objtype == METIS_OBJTYPE_VOL) {383/* Vol-refinement specific working arrays */384modind = iwspacemalloc(ctrl, nvtxs);385vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));386pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts));387}388389390/* Get a CSR representation of the components-2-partitions mapping */391for (i=0; i<ncmps; i++)392pcptr[where[cind[cptr[i]]]]++;393MAKECSR(i, nparts, pcptr);394for (i=0; i<ncmps; i++)395pcind[pcptr[where[cind[cptr[i]]]]++] = i;396SHIFTCSR(i, nparts, pcptr);397398/* Assign the heaviest component of each partition to its original partition */399for (ntodo=0, i=0; i<nparts; i++) {400if (pcptr[i+1]-pcptr[i] == 1)401bestcid = pcind[pcptr[i]];402else {403for (bestcid=-1, j=pcptr[i]; j<pcptr[i+1]; j++) {404cid = pcind[j];405iset(ncon, 0, cwgt);406for (ii=cptr[cid]; ii<cptr[cid+1]; ii++)407iaxpy(ncon, 1, vwgt+cind[ii]*ncon, 1, cwgt, 1);408if (bestcid == -1 || isum(ncon, bestcwgt, 1) < isum(ncon, cwgt, 1)) {409bestcid = cid;410icopy(ncon, cwgt, bestcwgt);411}412}413/* Keep track of those that need to be dealt with */414for (j=pcptr[i]; j<pcptr[i+1]; j++) {415if (pcind[j] != bestcid)416todo[ntodo++] = pcind[j];417}418}419420for (j=cptr[bestcid]; j<cptr[bestcid+1]; j++) {421ASSERT(where[cind[j]] == i);422cwhere[cind[j]] = i;423}424}425426427while (ntodo > 0) {428oldntodo = ntodo;429for (i=0; i<ntodo; i++) {430cid = todo[i];431me = where[cind[cptr[cid]]]; /* Get the domain of this component */432433/* Determine the weight of the block to be moved */434iset(ncon, 0, cwgt);435for (j=cptr[cid]; j<cptr[cid+1]; j++)436iaxpy(ncon, 1, vwgt+cind[j]*ncon, 1, cwgt, 1);437438IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO,439printf("Trying to move %"PRIDX" [%"PRIDX"] from %"PRIDX"\n",440cid, isum(ncon, cwgt, 1), me));441442/* Determine the connectivity */443iset(nparts, 0, cpvec);444for (j=cptr[cid]; j<cptr[cid+1]; j++) {445ii = cind[j];446for (jj=xadj[ii]; jj<xadj[ii+1]; jj++)447if (cwhere[adjncy[jj]] != -1)448cpvec[cwhere[adjncy[jj]]] += (adjwgt ? adjwgt[jj] : 1);449}450451/* Put the neighbors into a cand[] array for sorting */452for (ncand=0, j=0; j<nparts; j++) {453if (cpvec[j] > 0) {454cand[ncand].key = cpvec[j];455cand[ncand++].val = j;456}457}458if (ncand == 0)459continue;460461rkvsortd(ncand, cand);462463/* Limit the moves to only the top candidates, which are defined as464those with connectivity at least 50% of the best.465This applies only when ncon=1, as for multi-constraint, balancing466will be hard. */467if (ncon == 1) {468for (j=1; j<ncand; j++) {469if (cand[j].key < .5*cand[0].key)470break;471}472ncand = j;473}474475/* Now among those, select the one with the best balance */476target = cand[0].val;477for (j=1; j<ncand; j++) {478if (BetterBalanceKWay(ncon, cwgt, ctrl->ubfactors,4791, pwgts+target*ncon, ctrl->pijbm+target*ncon,4801, pwgts+cand[j].val*ncon, ctrl->pijbm+cand[j].val*ncon))481target = cand[j].val;482}483484IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO,485printf("\tMoving it to %"PRIDX" [%"PRIDX"] [%"PRIDX"]\n", target, cpvec[target], ncand));486487/* Note that as a result of a previous movement, a connected component may488now will like to stay to its original partition */489if (target != me) {490switch (ctrl->objtype) {491case METIS_OBJTYPE_CUT:492MoveGroupContigForCut(ctrl, graph, target, cid, cptr, cind);493break;494495case METIS_OBJTYPE_VOL:496MoveGroupContigForVol(ctrl, graph, target, cid, cptr, cind,497vmarker, pmarker, modind);498break;499500default:501gk_errexit(SIGERR, "Unknown objtype %d\n", ctrl->objtype);502}503}504505/* Update the cwhere vector */506for (j=cptr[cid]; j<cptr[cid+1]; j++)507cwhere[cind[j]] = target;508509todo[i] = todo[--ntodo];510}511if (oldntodo == ntodo) {512IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO, printf("Stopped at ntodo: %"PRIDX"\n", ntodo));513break;514}515}516517for (i=0; i<nvtxs; i++)518ASSERT(where[i] == cwhere[i]);519520}521522WCOREPOP;523}524525526/*************************************************************************/527/*! This function moves a collection of vertices and updates their rinfo528*/529/*************************************************************************/530void MoveGroupContigForCut(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t gid,531idx_t *ptr, idx_t *ind)532{533idx_t i, ii, iii, j, jj, k, l, nvtxs, nbnd, from, me;534idx_t *xadj, *adjncy, *adjwgt, *where, *bndptr, *bndind;535ckrinfo_t *myrinfo;536cnbr_t *mynbrs;537538nvtxs = graph->nvtxs;539xadj = graph->xadj;540adjncy = graph->adjncy;541adjwgt = graph->adjwgt;542543where = graph->where;544bndptr = graph->bndptr;545bndind = graph->bndind;546547nbnd = graph->nbnd;548549for (iii=ptr[gid]; iii<ptr[gid+1]; iii++) {550i = ind[iii];551from = where[i];552553myrinfo = graph->ckrinfo+i;554if (myrinfo->inbr == -1) {555myrinfo->inbr = cnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1);556myrinfo->nnbrs = 0;557}558mynbrs = ctrl->cnbrpool + myrinfo->inbr;559560/* find the location of 'to' in myrinfo or create it if it is not there */561for (k=0; k<myrinfo->nnbrs; k++) {562if (mynbrs[k].pid == to)563break;564}565if (k == myrinfo->nnbrs) {566mynbrs[k].pid = to;567mynbrs[k].ed = 0;568myrinfo->nnbrs++;569}570571graph->mincut -= mynbrs[k].ed-myrinfo->id;572573/* Update ID/ED and BND related information for the moved vertex */574iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1);575iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1);576UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where, nbnd,577bndptr, bndind, BNDTYPE_REFINE);578579/* Update the degrees of adjacent vertices */580for (j=xadj[i]; j<xadj[i+1]; j++) {581ii = adjncy[j];582me = where[ii];583myrinfo = graph->ckrinfo+ii;584585UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me,586from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, BNDTYPE_REFINE);587}588589ASSERT(CheckRInfo(ctrl, graph->ckrinfo+i));590}591592graph->nbnd = nbnd;593}594595596/*************************************************************************/597/*! This function moves a collection of vertices and updates their rinfo598*/599/*************************************************************************/600void MoveGroupContigForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t gid,601idx_t *ptr, idx_t *ind, idx_t *vmarker, idx_t *pmarker,602idx_t *modind)603{604idx_t i, ii, iii, j, jj, k, l, nvtxs, from, me, other, xgain;605idx_t *xadj, *vsize, *adjncy, *where;606vkrinfo_t *myrinfo, *orinfo;607vnbr_t *mynbrs, *onbrs;608609nvtxs = graph->nvtxs;610xadj = graph->xadj;611vsize = graph->vsize;612adjncy = graph->adjncy;613where = graph->where;614615for (iii=ptr[gid]; iii<ptr[gid+1]; iii++) {616i = ind[iii];617from = where[i];618619myrinfo = graph->vkrinfo+i;620if (myrinfo->inbr == -1) {621myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1);622myrinfo->nnbrs = 0;623}624mynbrs = ctrl->vnbrpool + myrinfo->inbr;625626xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? vsize[i] : 0);627628/* find the location of 'to' in myrinfo or create it if it is not there */629for (k=0; k<myrinfo->nnbrs; k++) {630if (mynbrs[k].pid == to)631break;632}633if (k == myrinfo->nnbrs) {634if (myrinfo->nid > 0)635xgain -= vsize[i];636637/* determine the volume gain resulting from that move */638for (j=xadj[i]; j<xadj[i+1]; j++) {639ii = adjncy[j];640other = where[ii];641orinfo = graph->vkrinfo+ii;642onbrs = ctrl->vnbrpool + orinfo->inbr;643ASSERT(other != to)644645if (from == other) {646/* Same subdomain vertex: Decrease the gain if 'to' is a new neighbor. */647for (l=0; l<orinfo->nnbrs; l++) {648if (onbrs[l].pid == to)649break;650}651if (l == orinfo->nnbrs)652xgain -= vsize[ii];653}654else {655/* Remote vertex: increase if 'to' is a new subdomain */656for (l=0; l<orinfo->nnbrs; l++) {657if (onbrs[l].pid == to)658break;659}660if (l == orinfo->nnbrs)661xgain -= vsize[ii];662663/* Remote vertex: decrease if i is the only connection to 'from' */664for (l=0; l<orinfo->nnbrs; l++) {665if (onbrs[l].pid == from && onbrs[l].ned == 1) {666xgain += vsize[ii];667break;668}669}670}671}672graph->minvol -= xgain;673graph->mincut -= -myrinfo->nid;674}675else {676graph->minvol -= (xgain + mynbrs[k].gv);677graph->mincut -= mynbrs[k].ned-myrinfo->nid;678}679680681/* Update where and pwgts */682where[i] = to;683iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1);684iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1);685686/* Update the id/ed/gains/bnd of potentially affected nodes */687KWayVolUpdate(ctrl, graph, i, from, to, NULL, NULL, NULL, NULL,688NULL, BNDTYPE_REFINE, vmarker, pmarker, modind);689690/*CheckKWayVolPartitionParams(ctrl, graph);*/691}692693ASSERT(ComputeCut(graph, where) == graph->mincut);694ASSERTP(ComputeVolume(graph, where) == graph->minvol,695("%"PRIDX" %"PRIDX"\n", ComputeVolume(graph, where), graph->minvol));696697}698699700701