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.
 
 
 

701 lines
20 KiB

/*
* Copyright 1997, Regents of the University of Minnesota
*
* ometis.c
*
* This file contains the top level routines for the multilevel recursive
* bisection algorithm PMETIS.
*
* Started 7/24/97
* George
*
* $Id: ometis.c 10513 2011-07-07 22:06:03Z karypis $
*
*/
#include "metislib.h"
/*************************************************************************/
/*! This function is the entry point for the multilevel nested dissection
ordering code. At each bisection, a node-separator is computed using
a node-based refinement approach.
\param nvtxs is the number of vertices in the graph.
\param xadj is of length nvtxs+1 marking the start of the adjancy
list of each vertex in adjncy.
\param adjncy stores the adjacency lists of the vertices. The adjnacy
list of a vertex should not contain the vertex itself.
\param vwgt is an array of size nvtxs storing the weight of each
vertex. If vwgt is NULL, then the vertices are considered
to have unit weight.
\param numflag is either 0 or 1 indicating that the numbering of
the vertices starts from 0 or 1, respectively.
\param options is an array of size METIS_NOPTIONS used to pass
various options impacting the of the algorithm. A NULL
value indicates use of default options.
\param perm is an array of size nvtxs such that if A and A' are
the original and permuted matrices, then A'[i] = A[perm[i]].
\param iperm is an array of size nvtxs such that if A and A' are
the original and permuted matrices, then A[i] = A'[iperm[i]].
*/
/*************************************************************************/
int METIS_NodeND(idx_t *nvtxs, idx_t *xadj, idx_t *adjncy, idx_t *vwgt,
idx_t *options, idx_t *perm, idx_t *iperm)
{
int sigrval=0, renumber=0;
idx_t i, ii, j, l, nnvtxs=0;
graph_t *graph=NULL;
ctrl_t *ctrl;
idx_t *cptr, *cind, *piperm;
int numflag = 0;
/* set up malloc cleaning code and signal catchers */
if (!gk_malloc_init())
return METIS_ERROR_MEMORY;
gk_sigtrap();
if ((sigrval = gk_sigcatch()) != 0)
goto SIGTHROW;
/* set up the run time parameters */
ctrl = SetupCtrl(METIS_OP_OMETIS, options, 1, 3, NULL, NULL);
if (!ctrl) {
gk_siguntrap();
return METIS_ERROR_INPUT;
}
/* if required, change the numbering to 0 */
if (ctrl->numflag == 1) {
Change2CNumbering(*nvtxs, xadj, adjncy);
renumber = 1;
}
IFSET(ctrl->dbglvl, METIS_DBG_TIME, InitTimers(ctrl));
IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->TotalTmr));
/* prune the dense columns */
if (ctrl->pfactor > 0.0) {
piperm = imalloc(*nvtxs, "OMETIS: piperm");
graph = PruneGraph(ctrl, *nvtxs, xadj, adjncy, vwgt, piperm, ctrl->pfactor);
if (graph == NULL) {
/* if there was no prunning, cleanup the pfactor */
gk_free((void **)&piperm, LTERM);
ctrl->pfactor = 0.0;
}
else {
nnvtxs = graph->nvtxs;
ctrl->compress = 0; /* disable compression if prunning took place */
}
}
/* compress the graph; note that compression only happens if not prunning
has taken place. */
if (ctrl->compress) {
cptr = imalloc(*nvtxs+1, "OMETIS: cptr");
cind = imalloc(*nvtxs, "OMETIS: cind");
graph = CompressGraph(ctrl, *nvtxs, xadj, adjncy, vwgt, cptr, cind);
if (graph == NULL) {
/* if there was no compression, cleanup the compress flag */
gk_free((void **)&cptr, &cind, LTERM);
ctrl->compress = 0;
}
else {
nnvtxs = graph->nvtxs;
ctrl->cfactor = 1.0*(*nvtxs)/nnvtxs;
if (ctrl->cfactor > 1.5 && ctrl->nseps == 1)
ctrl->nseps = 2;
//ctrl->nseps = (idx_t)(ctrl->cfactor*ctrl->nseps);
}
}
/* if no prunning and no compression, setup the graph in the normal way. */
if (ctrl->pfactor == 0.0 && ctrl->compress == 0)
graph = SetupGraph(ctrl, *nvtxs, 1, xadj, adjncy, vwgt, NULL, NULL);
ASSERT(CheckGraph(graph, ctrl->numflag, 1));
/* allocate workspace memory */
AllocateWorkSpace(ctrl, graph);
/* do the nested dissection ordering */
if (ctrl->ccorder)
MlevelNestedDissectionCC(ctrl, graph, iperm, graph->nvtxs);
else
MlevelNestedDissection(ctrl, graph, iperm, graph->nvtxs);
if (ctrl->pfactor > 0.0) { /* Order any prunned vertices */
icopy(nnvtxs, iperm, perm); /* Use perm as an auxiliary array */
for (i=0; i<nnvtxs; i++)
iperm[piperm[i]] = perm[i];
for (i=nnvtxs; i<*nvtxs; i++)
iperm[piperm[i]] = i;
gk_free((void **)&piperm, LTERM);
}
else if (ctrl->compress) { /* Uncompress the ordering */
/* construct perm from iperm */
for (i=0; i<nnvtxs; i++)
perm[iperm[i]] = i;
for (l=ii=0; ii<nnvtxs; ii++) {
i = perm[ii];
for (j=cptr[i]; j<cptr[i+1]; j++)
iperm[cind[j]] = l++;
}
gk_free((void **)&cptr, &cind, LTERM);
}
for (i=0; i<*nvtxs; i++)
perm[iperm[i]] = i;
IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->TotalTmr));
IFSET(ctrl->dbglvl, METIS_DBG_TIME, PrintTimers(ctrl));
/* clean up */
FreeCtrl(&ctrl);
SIGTHROW:
/* if required, change the numbering back to 1 */
if (renumber)
Change2FNumberingOrder(*nvtxs, xadj, adjncy, perm, iperm);
gk_siguntrap();
gk_malloc_cleanup(0);
return metis_rcode(sigrval);
}
/*************************************************************************/
/*! This is the driver for the recursive tri-section of a graph into the
left, separator, and right partitions. The graphs correspond to the
left and right parts are further tri-sected in a recursive fashion.
The nodes in the separator are ordered at the end of the left & right
nodes.
*/
/*************************************************************************/
void MlevelNestedDissection(ctrl_t *ctrl, graph_t *graph, idx_t *order,
idx_t lastvtx)
{
idx_t i, j, nvtxs, nbnd;
idx_t *label, *bndind;
graph_t *lgraph, *rgraph;
nvtxs = graph->nvtxs;
MlevelNodeBisectionMultiple(ctrl, graph);
IFSET(ctrl->dbglvl, METIS_DBG_SEPINFO,
printf("Nvtxs: %6"PRIDX", [%6"PRIDX" %6"PRIDX" %6"PRIDX"]\n",
graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]));
/* Order the nodes in the separator */
nbnd = graph->nbnd;
bndind = graph->bndind;
label = graph->label;
for (i=0; i<nbnd; i++)
order[label[bndind[i]]] = --lastvtx;
SplitGraphOrder(ctrl, graph, &lgraph, &rgraph);
/* Free the memory of the top level graph */
FreeGraph(&graph);
/* Recurse on lgraph first, as its lastvtx depends on rgraph->nvtxs, which
will not be defined upon return from MlevelNestedDissection. */
if (lgraph->nvtxs > MMDSWITCH && lgraph->nedges > 0)
MlevelNestedDissection(ctrl, lgraph, order, lastvtx-rgraph->nvtxs);
else {
MMDOrder(ctrl, lgraph, order, lastvtx-rgraph->nvtxs);
FreeGraph(&lgraph);
}
if (rgraph->nvtxs > MMDSWITCH && rgraph->nedges > 0)
MlevelNestedDissection(ctrl, rgraph, order, lastvtx);
else {
MMDOrder(ctrl, rgraph, order, lastvtx);
FreeGraph(&rgraph);
}
}
/*************************************************************************/
/*! This routine is similar to its non 'CC' counterpart. The difference is
that after each tri-section, the connected components of the original
graph that result after removing the separator vertises are ordered
independently (i.e., this may lead to more than just the left and
the right subgraphs).
*/
/*************************************************************************/
void MlevelNestedDissectionCC(ctrl_t *ctrl, graph_t *graph, idx_t *order,
idx_t lastvtx)
{
idx_t i, j, nvtxs, nbnd, ncmps, rnvtxs, snvtxs;
idx_t *label, *bndind;
idx_t *cptr, *cind;
graph_t **sgraphs;
nvtxs = graph->nvtxs;
MlevelNodeBisectionMultiple(ctrl, graph);
IFSET(ctrl->dbglvl, METIS_DBG_SEPINFO,
printf("Nvtxs: %6"PRIDX", [%6"PRIDX" %6"PRIDX" %6"PRIDX"]\n",
graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]));
/* Order the nodes in the separator */
nbnd = graph->nbnd;
bndind = graph->bndind;
label = graph->label;
for (i=0; i<nbnd; i++)
order[label[bndind[i]]] = --lastvtx;
WCOREPUSH;
cptr = iwspacemalloc(ctrl, nvtxs+1);
cind = iwspacemalloc(ctrl, nvtxs);
ncmps = FindSepInducedComponents(ctrl, graph, cptr, cind);
if (ctrl->dbglvl&METIS_DBG_INFO) {
if (ncmps > 2)
printf(" Bisection resulted in %"PRIDX" connected components\n", ncmps);
}
sgraphs = SplitGraphOrderCC(ctrl, graph, ncmps, cptr, cind);
WCOREPOP;
/* Free the memory of the top level graph */
FreeGraph(&graph);
/* Go and process the subgraphs */
for (rnvtxs=i=0; i<ncmps; i++) {
/* Save the number of vertices in sgraphs[i] because sgraphs[i] is freed
inside MlevelNestedDissectionCC, and as such it will be undefined. */
snvtxs = sgraphs[i]->nvtxs;
if (sgraphs[i]->nvtxs > MMDSWITCH && sgraphs[i]->nedges > 0) {
MlevelNestedDissectionCC(ctrl, sgraphs[i], order, lastvtx-rnvtxs);
}
else {
MMDOrder(ctrl, sgraphs[i], order, lastvtx-rnvtxs);
FreeGraph(&sgraphs[i]);
}
rnvtxs += snvtxs;
}
gk_free((void **)&sgraphs, LTERM);
}
/*************************************************************************/
/*! This function performs multilevel node bisection (i.e., tri-section).
It performs multiple bisections and selects the best. */
/*************************************************************************/
void MlevelNodeBisectionMultiple(ctrl_t *ctrl, graph_t *graph)
{
idx_t i, mincut;
idx_t *bestwhere;
/* if the graph is small, just find a single vertex separator */
if (ctrl->nseps == 1 || graph->nvtxs < (ctrl->compress ? 1000 : 2000)) {
MlevelNodeBisectionL2(ctrl, graph, LARGENIPARTS);
return;
}
WCOREPUSH;
bestwhere = iwspacemalloc(ctrl, graph->nvtxs);
mincut = graph->tvwgt[0];
for (i=0; i<ctrl->nseps; i++) {
MlevelNodeBisectionL2(ctrl, graph, LARGENIPARTS);
if (i == 0 || graph->mincut < mincut) {
mincut = graph->mincut;
if (i < ctrl->nseps-1)
icopy(graph->nvtxs, graph->where, bestwhere);
}
if (mincut == 0)
break;
if (i < ctrl->nseps-1)
FreeRData(graph);
}
if (mincut != graph->mincut) {
icopy(graph->nvtxs, bestwhere, graph->where);
Compute2WayNodePartitionParams(ctrl, graph);
}
WCOREPOP;
}
/*************************************************************************/
/*! This function performs multilevel node bisection (i.e., tri-section).
It performs multiple bisections and selects the best. */
/*************************************************************************/
void MlevelNodeBisectionL2(ctrl_t *ctrl, graph_t *graph, idx_t niparts)
{
idx_t i, mincut, nruns=5;
graph_t *cgraph;
idx_t *bestwhere;
/* if the graph is small, just find a single vertex separator */
if (graph->nvtxs < 5000) {
MlevelNodeBisectionL1(ctrl, graph, niparts);
return;
}
WCOREPUSH;
ctrl->CoarsenTo = gk_max(100, graph->nvtxs/30);
cgraph = CoarsenGraphNlevels(ctrl, graph, 4);
bestwhere = iwspacemalloc(ctrl, cgraph->nvtxs);
mincut = graph->tvwgt[0];
for (i=0; i<nruns; i++) {
MlevelNodeBisectionL1(ctrl, cgraph, 0.7*niparts);
if (i == 0 || cgraph->mincut < mincut) {
mincut = cgraph->mincut;
if (i < nruns-1)
icopy(cgraph->nvtxs, cgraph->where, bestwhere);
}
if (mincut == 0)
break;
if (i < nruns-1)
FreeRData(cgraph);
}
if (mincut != cgraph->mincut)
icopy(cgraph->nvtxs, bestwhere, cgraph->where);
WCOREPOP;
Refine2WayNode(ctrl, graph, cgraph);
}
/*************************************************************************/
/*! The top-level routine of the actual multilevel node bisection */
/*************************************************************************/
void MlevelNodeBisectionL1(ctrl_t *ctrl, graph_t *graph, idx_t niparts)
{
graph_t *cgraph;
ctrl->CoarsenTo = graph->nvtxs/8;
if (ctrl->CoarsenTo > 100)
ctrl->CoarsenTo = 100;
else if (ctrl->CoarsenTo < 40)
ctrl->CoarsenTo = 40;
cgraph = CoarsenGraph(ctrl, graph);
niparts = gk_max(1, (cgraph->nvtxs <= ctrl->CoarsenTo ? niparts/2: niparts));
/*niparts = (cgraph->nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);*/
InitSeparator(ctrl, cgraph, niparts);
Refine2WayNode(ctrl, graph, cgraph);
}
/*************************************************************************/
/*! This function takes a graph and a tri-section (left, right, separator)
and splits it into two graphs.
This function relies on the fact that adjwgt is all equal to 1.
*/
/*************************************************************************/
void SplitGraphOrder(ctrl_t *ctrl, graph_t *graph, graph_t **r_lgraph,
graph_t **r_rgraph)
{
idx_t i, ii, j, k, l, istart, iend, mypart, nvtxs, snvtxs[3], snedges[3];
idx_t *xadj, *vwgt, *adjncy, *adjwgt, *label, *where, *bndptr, *bndind;
idx_t *sxadj[2], *svwgt[2], *sadjncy[2], *sadjwgt[2], *slabel[2];
idx_t *rename;
idx_t *auxadjncy;
graph_t *lgraph, *rgraph;
WCOREPUSH;
IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->SplitTmr));
nvtxs = graph->nvtxs;
xadj = graph->xadj;
vwgt = graph->vwgt;
adjncy = graph->adjncy;
adjwgt = graph->adjwgt;
label = graph->label;
where = graph->where;
bndptr = graph->bndptr;
bndind = graph->bndind;
ASSERT(bndptr != NULL);
rename = iwspacemalloc(ctrl, nvtxs);
snvtxs[0] = snvtxs[1] = snvtxs[2] = snedges[0] = snedges[1] = snedges[2] = 0;
for (i=0; i<nvtxs; i++) {
k = where[i];
rename[i] = snvtxs[k]++;
snedges[k] += xadj[i+1]-xadj[i];
}
lgraph = SetupSplitGraph(graph, snvtxs[0], snedges[0]);
sxadj[0] = lgraph->xadj;
svwgt[0] = lgraph->vwgt;
sadjncy[0] = lgraph->adjncy;
sadjwgt[0] = lgraph->adjwgt;
slabel[0] = lgraph->label;
rgraph = SetupSplitGraph(graph, snvtxs[1], snedges[1]);
sxadj[1] = rgraph->xadj;
svwgt[1] = rgraph->vwgt;
sadjncy[1] = rgraph->adjncy;
sadjwgt[1] = rgraph->adjwgt;
slabel[1] = rgraph->label;
/* Go and use bndptr to also mark the boundary nodes in the two partitions */
for (ii=0; ii<graph->nbnd; ii++) {
i = bndind[ii];
for (j=xadj[i]; j<xadj[i+1]; j++)
bndptr[adjncy[j]] = 1;
}
snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0;
sxadj[0][0] = sxadj[1][0] = 0;
for (i=0; i<nvtxs; i++) {
if ((mypart = where[i]) == 2)
continue;
istart = xadj[i];
iend = xadj[i+1];
if (bndptr[i] == -1) { /* This is an interior vertex */
auxadjncy = sadjncy[mypart] + snedges[mypart] - istart;
for(j=istart; j<iend; j++)
auxadjncy[j] = adjncy[j];
snedges[mypart] += iend-istart;
}
else {
auxadjncy = sadjncy[mypart];
l = snedges[mypart];
for (j=istart; j<iend; j++) {
k = adjncy[j];
if (where[k] == mypart)
auxadjncy[l++] = k;
}
snedges[mypart] = l;
}
svwgt[mypart][snvtxs[mypart]] = vwgt[i];
slabel[mypart][snvtxs[mypart]] = label[i];
sxadj[mypart][++snvtxs[mypart]] = snedges[mypart];
}
for (mypart=0; mypart<2; mypart++) {
iend = snedges[mypart];
iset(iend, 1, sadjwgt[mypart]);
auxadjncy = sadjncy[mypart];
for (i=0; i<iend; i++)
auxadjncy[i] = rename[auxadjncy[i]];
}
lgraph->nvtxs = snvtxs[0];
lgraph->nedges = snedges[0];
rgraph->nvtxs = snvtxs[1];
rgraph->nedges = snedges[1];
SetupGraph_tvwgt(lgraph);
SetupGraph_tvwgt(rgraph);
IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->SplitTmr));
*r_lgraph = lgraph;
*r_rgraph = rgraph;
WCOREPOP;
}
/*************************************************************************/
/*! This function takes a graph and generates a set of graphs, each of
which is a connected component in the original graph.
This function relies on the fact that adjwgt is all equal to 1.
\param ctrl stores run state info.
\param graph is the graph to be split.
\param ncmps is the number of connected components.
\param cptr is an array of size ncmps+1 that marks the start and end
locations of the vertices in cind that make up the respective
components (i.e., cptr, cind is in CSR format).
\param cind is an array of size equal to the number of vertices in
the original graph and stores the vertices that belong to each
connected component.
\returns an array of subgraphs corresponding to the extracted subgraphs.
*/
/*************************************************************************/
graph_t **SplitGraphOrderCC(ctrl_t *ctrl, graph_t *graph, idx_t ncmps,
idx_t *cptr, idx_t *cind)
{
idx_t i, ii, iii, j, k, l, istart, iend, mypart, nvtxs, snvtxs, snedges;
idx_t *xadj, *vwgt, *adjncy, *adjwgt, *label, *where, *bndptr, *bndind;
idx_t *sxadj, *svwgt, *sadjncy, *sadjwgt, *slabel;
idx_t *rename;
idx_t *auxadjncy;
graph_t **sgraphs;
WCOREPUSH;
IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->SplitTmr));
nvtxs = graph->nvtxs;
xadj = graph->xadj;
vwgt = graph->vwgt;
adjncy = graph->adjncy;
adjwgt = graph->adjwgt;
label = graph->label;
where = graph->where;
bndptr = graph->bndptr;
bndind = graph->bndind;
ASSERT(bndptr != NULL);
/* Go and use bndptr to also mark the boundary nodes in the two partitions */
for (ii=0; ii<graph->nbnd; ii++) {
i = bndind[ii];
for (j=xadj[i]; j<xadj[i+1]; j++)
bndptr[adjncy[j]] = 1;
}
rename = iwspacemalloc(ctrl, nvtxs);
sgraphs = (graph_t **)gk_malloc(sizeof(graph_t *)*ncmps, "SplitGraphOrderCC: sgraphs");
/* Go and split the graph a component at a time */
for (iii=0; iii<ncmps; iii++) {
irandArrayPermute(cptr[iii+1]-cptr[iii], cind+cptr[iii], cptr[iii+1]-cptr[iii], 0);
snvtxs = snedges = 0;
for (j=cptr[iii]; j<cptr[iii+1]; j++) {
i = cind[j];
rename[i] = snvtxs++;
snedges += xadj[i+1]-xadj[i];
}
sgraphs[iii] = SetupSplitGraph(graph, snvtxs, snedges);
sxadj = sgraphs[iii]->xadj;
svwgt = sgraphs[iii]->vwgt;
sadjncy = sgraphs[iii]->adjncy;
sadjwgt = sgraphs[iii]->adjwgt;
slabel = sgraphs[iii]->label;
snvtxs = snedges = sxadj[0] = 0;
for (ii=cptr[iii]; ii<cptr[iii+1]; ii++) {
i = cind[ii];
istart = xadj[i];
iend = xadj[i+1];
if (bndptr[i] == -1) { /* This is an interior vertex */
auxadjncy = sadjncy + snedges - istart;
for(j=istart; j<iend; j++)
auxadjncy[j] = adjncy[j];
snedges += iend-istart;
}
else {
l = snedges;
for (j=istart; j<iend; j++) {
k = adjncy[j];
if (where[k] != 2)
sadjncy[l++] = k;
}
snedges = l;
}
svwgt[snvtxs] = vwgt[i];
slabel[snvtxs] = label[i];
sxadj[++snvtxs] = snedges;
}
iset(snedges, 1, sadjwgt);
for (i=0; i<snedges; i++)
sadjncy[i] = rename[sadjncy[i]];
sgraphs[iii]->nvtxs = snvtxs;
sgraphs[iii]->nedges = snedges;
SetupGraph_tvwgt(sgraphs[iii]);
}
IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->SplitTmr));
WCOREPOP;
return sgraphs;
}
/*************************************************************************/
/*! This function uses MMD to order the graph. The vertices are numbered
from lastvtx downwards. */
/*************************************************************************/
void MMDOrder(ctrl_t *ctrl, graph_t *graph, idx_t *order, idx_t lastvtx)
{
idx_t i, j, k, nvtxs, nofsub, firstvtx;
idx_t *xadj, *adjncy, *label;
idx_t *perm, *iperm, *head, *qsize, *list, *marker;
WCOREPUSH;
nvtxs = graph->nvtxs;
xadj = graph->xadj;
adjncy = graph->adjncy;
/* Relabel the vertices so that it starts from 1 */
k = xadj[nvtxs];
for (i=0; i<k; i++)
adjncy[i]++;
for (i=0; i<nvtxs+1; i++)
xadj[i]++;
perm = iwspacemalloc(ctrl, nvtxs+5);
iperm = iwspacemalloc(ctrl, nvtxs+5);
head = iwspacemalloc(ctrl, nvtxs+5);
qsize = iwspacemalloc(ctrl, nvtxs+5);
list = iwspacemalloc(ctrl, nvtxs+5);
marker = iwspacemalloc(ctrl, nvtxs+5);
genmmd(nvtxs, xadj, adjncy, iperm, perm, 1, head, qsize, list, marker, IDX_MAX, &nofsub);
label = graph->label;
firstvtx = lastvtx-nvtxs;
for (i=0; i<nvtxs; i++)
order[label[i]] = firstvtx+iperm[i]-1;
/* Relabel the vertices so that it starts from 0 */
for (i=0; i<nvtxs+1; i++)
xadj[i]--;
k = xadj[nvtxs];
for (i=0; i<k; i++)
adjncy[i]--;
WCOREPOP;
}