Cloned library METIS with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

699 lines
21 KiB

/*!
\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]; j<xadj[i+1]; j++) {
k = adjncy[j];
if (where[k] == me && !touched[k]) {
cind[last++] = k;
touched[k] = 1;
}
}
}
cptr[++ncmps] = first;
if (mustfree_ccsr)
gk_free((void **)&cptr, &cind, LTERM);
if (mustfree_where)
gk_free((void **)&where, LTERM);
gk_free((void **)&perm, &todo, &touched, LTERM);
return ncmps;
}
/*************************************************************************/
/*! This function computes a permutation of the vertices based on a
breadth-first-traversal. It can be used for re-ordering the graph
to reduce its bandwidth for better cache locality.
\param ctrl is the control structure
\param graph is the graph structure
\param perm is the array that upon completion, perm[i] will store
the ID of the vertex that corresponds to the ith vertex in the
re-ordered graph.
*/
/*************************************************************************/
void ComputeBFSOrdering(ctrl_t *ctrl, graph_t *graph, idx_t *bfsperm)
{
idx_t i, j, k, nvtxs, first, last;
idx_t *xadj, *adjncy, *perm;
WCOREPUSH;
nvtxs = graph->nvtxs;
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]; j<xadj[i+1]; j++) {
k = adjncy[j];
/* if a node has been already been visited, its perm[] will be -1 */
if (perm[k] != -1) {
/* perm[k] is the location within bfsperm of where k resides;
put in that location bfsperm[last] that we are about to
overwrite and update perm[bfsperm[last]] to reflect that. */
bfsperm[perm[k]] = bfsperm[last];
perm[bfsperm[last]] = perm[k];
bfsperm[last++] = k; /* put node at the end of the "queue" */
perm[k] = -1; /* mark node as being visited */
}
}
}
WCOREPOP;
}
/*************************************************************************/
/*! This function checks whether a graph is contiguous or not.
*/
/**************************************************************************/
idx_t IsConnected(graph_t *graph, idx_t report)
{
idx_t ncmps;
ncmps = FindPartitionInducedComponents(graph, NULL, NULL, NULL);
if (ncmps != 1 && report)
printf("The graph is not connected. It has %"PRIDX" connected components.\n", ncmps);
return (ncmps == 1);
}
/*************************************************************************/
/*! This function checks whether or not partition pid is contiguous
*/
/*************************************************************************/
idx_t IsConnectedSubdomain(ctrl_t *ctrl, graph_t *graph, idx_t pid, idx_t report)
{
idx_t i, j, k, nvtxs, first, last, nleft, ncmps, wgt;
idx_t *xadj, *adjncy, *where, *touched, *queue;
idx_t *cptr;
nvtxs = graph->nvtxs;
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<nvtxs; i++) {
if (where[i] == pid)
nleft++;
}
for (i=0; i<nvtxs; i++) {
if (where[i] == pid)
break;
}
touched[i] = 1;
queue[0] = i;
first = 0; last = 1;
cptr[0] = 0; /* This actually points to queue */
ncmps = 0;
while (first != nleft) {
if (first == last) { /* Find another starting vertex */
cptr[++ncmps] = first;
for (i=0; i<nvtxs; i++) {
if (where[i] == pid && !touched[i])
break;
}
queue[last++] = i;
touched[i] = 1;
}
i = queue[first++];
for (j=xadj[i]; j<xadj[i+1]; j++) {
k = adjncy[j];
if (where[k] == pid && !touched[k]) {
queue[last++] = k;
touched[k] = 1;
}
}
}
cptr[++ncmps] = first;
if (ncmps > 1 && report) {
printf("The graph has %"PRIDX" connected components in partition %"PRIDX":\t", ncmps, pid);
for (i=0; i<ncmps; i++) {
wgt = 0;
for (j=cptr[i]; j<cptr[i+1]; j++)
wgt += graph->vwgt[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; i<graph->nbnd; i++)
touched[graph->bndind[i]] = 1;
queue = cind;
nleft = 0;
for (i=0; i<nvtxs; i++) {
if (where[i] != 2)
nleft++;
}
for (i=0; i<nvtxs; i++) {
if (where[i] != 2)
break;
}
touched[i] = 1;
queue[0] = i;
first = 0;
last = 1;
cptr[0] = 0; /* This actually points to queue */
ncmps = 0;
while (first != nleft) {
if (first == last) { /* Find another starting vertex */
cptr[++ncmps] = first;
for (i=0; i<nvtxs; i++) {
if (!touched[i])
break;
}
queue[last++] = i;
touched[i] = 1;
}
i = queue[first++];
for (j=xadj[i]; j<xadj[i+1]; j++) {
k = adjncy[j];
if (!touched[k]) {
queue[last++] = k;
touched[k] = 1;
}
}
}
cptr[++ncmps] = first;
gk_free((void **)&touched, LTERM);
return ncmps;
}
/*************************************************************************/
/*! This function finds all the connected components induced by the
partitioning vector in graph->where 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<ncmps; i++)
pcptr[where[cind[cptr[i]]]]++;
MAKECSR(i, nparts, pcptr);
for (i=0; i<ncmps; i++)
pcind[pcptr[where[cind[cptr[i]]]]++] = i;
SHIFTCSR(i, nparts, pcptr);
/* Assign the heaviest component of each partition to its original partition */
for (ntodo=0, i=0; i<nparts; i++) {
if (pcptr[i+1]-pcptr[i] == 1)
bestcid = pcind[pcptr[i]];
else {
for (bestcid=-1, j=pcptr[i]; j<pcptr[i+1]; j++) {
cid = pcind[j];
iset(ncon, 0, cwgt);
for (ii=cptr[cid]; ii<cptr[cid+1]; ii++)
iaxpy(ncon, 1, vwgt+cind[ii]*ncon, 1, cwgt, 1);
if (bestcid == -1 || isum(ncon, bestcwgt, 1) < isum(ncon, cwgt, 1)) {
bestcid = cid;
icopy(ncon, cwgt, bestcwgt);
}
}
/* Keep track of those that need to be dealt with */
for (j=pcptr[i]; j<pcptr[i+1]; j++) {
if (pcind[j] != bestcid)
todo[ntodo++] = pcind[j];
}
}
for (j=cptr[bestcid]; j<cptr[bestcid+1]; j++) {
ASSERT(where[cind[j]] == i);
cwhere[cind[j]] = i;
}
}
while (ntodo > 0) {
oldntodo = ntodo;
for (i=0; i<ntodo; i++) {
cid = todo[i];
me = where[cind[cptr[cid]]]; /* Get the domain of this component */
/* Determine the weight of the block to be moved */
iset(ncon, 0, cwgt);
for (j=cptr[cid]; j<cptr[cid+1]; j++)
iaxpy(ncon, 1, vwgt+cind[j]*ncon, 1, cwgt, 1);
IFSET(ctrl->dbglvl, 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<cptr[cid+1]; j++) {
ii = cind[j];
for (jj=xadj[ii]; jj<xadj[ii+1]; jj++)
if (cwhere[adjncy[jj]] != -1)
cpvec[cwhere[adjncy[jj]]] += (adjwgt ? adjwgt[jj] : 1);
}
/* Put the neighbors into a cand[] array for sorting */
for (ncand=0, j=0; j<nparts; j++) {
if (cpvec[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; j<ncand; j++) {
if (cand[j].key < .5*cand[0].key)
break;
}
ncand = j;
}
/* Now among those, select the one with the best balance */
target = cand[0].val;
for (j=1; j<ncand; j++) {
if (BetterBalanceKWay(ncon, cwgt, ctrl->ubfactors,
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]; j<cptr[cid+1]; j++)
cwhere[cind[j]] = target;
todo[i] = todo[--ntodo];
}
if (oldntodo == ntodo) {
IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO, printf("Stopped at ntodo: %"PRIDX"\n", ntodo));
break;
}
}
for (i=0; i<nvtxs; i++)
ASSERT(where[i] == cwhere[i]);
}
WCOREPOP;
}
/*************************************************************************/
/*! This function moves a collection of vertices and updates their rinfo
*/
/*************************************************************************/
void MoveGroupContigForCut(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t gid,
idx_t *ptr, idx_t *ind)
{
idx_t i, ii, iii, 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;
for (iii=ptr[gid]; iii<ptr[gid+1]; iii++) {
i = ind[iii];
from = where[i];
myrinfo = graph->ckrinfo+i;
if (myrinfo->inbr == -1) {
myrinfo->inbr = cnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]);
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; k<myrinfo->nnbrs; 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]; j<xadj[i+1]; j++) {
ii = adjncy[j];
me = where[ii];
myrinfo = graph->ckrinfo+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]; iii<ptr[gid+1]; iii++) {
i = ind[iii];
from = where[i];
myrinfo = graph->vkrinfo+i;
if (myrinfo->inbr == -1) {
myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]);
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; k<myrinfo->nnbrs; 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]; j<xadj[i+1]; j++) {
ii = adjncy[j];
other = where[ii];
orinfo = graph->vkrinfo+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; l<orinfo->nnbrs; 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; l<orinfo->nnbrs; 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; l<orinfo->nnbrs; 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));
}