/* * 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; icompress) { /* Uncompress the ordering */ /* construct perm from iperm */ for (i=0; idbglvl, 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; invtxs, 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; idbglvl&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; invtxs; 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; inseps; 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; imincut < 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; ixadj; 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; iinbnd; ii++) { i = bndind[ii]; for (j=xadj[i]; jnvtxs = 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; iinbnd; ii++) { i = bndind[ii]; for (j=xadj[i]; jxadj; 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]; iinvtxs = 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; ilabel; firstvtx = lastvtx-nvtxs; for (i=0; i