Path: blob/devel/elmergrid/src/metis-5.1.0/libmetis/mmd.c
3206 views
/*1* mmd.c2*3* **************************************************************4* The following C function was developed from a FORTRAN subroutine5* in SPARSPAK written by Eleanor Chu, Alan George, Joseph Liu6* and Esmond Ng.7*8* The FORTRAN-to-C transformation and modifications such as dynamic9* memory allocation and deallocation were performed by Chunguang10* Sun.11* **************************************************************12*13* Taken from SMMS, George 12/13/9414*15* The meaning of invperm, and perm vectors is different from that16* in genqmd_ of SparsPak17*18* $Id: mmd.c 5993 2009-01-07 02:09:57Z karypis $19*/2021#include "metislib.h"222324/*************************************************************************25* genmmd -- multiple minimum external degree26* purpose -- this routine implements the minimum degree27* algorithm. it makes use of the implicit representation28* of elimination graphs by quotient graphs, and the notion29* of indistinguishable nodes. It also implements the modifications30* by multiple elimination and minimum external degree.31* Caution -- the adjacency vector adjncy will be destroyed.32* Input parameters --33* neqns -- number of equations.34* (xadj, adjncy) -- the adjacency structure.35* delta -- tolerance value for multiple elimination.36* maxint -- maximum machine representable (short) integer37* (any smaller estimate will do) for marking nodes.38* Output parameters --39* perm -- the minimum degree ordering.40* invp -- the inverse of perm.41* *ncsub -- an upper bound on the number of nonzero subscripts42* for the compressed storage scheme.43* Working parameters --44* head -- vector for head of degree lists.45* invp -- used temporarily for degree forward link.46* perm -- used temporarily for degree backward link.47* qsize -- vector for size of supernodes.48* list -- vector for temporary linked lists.49* marker -- a temporary marker vector.50* Subroutines used -- mmdelm, mmdint, mmdnum, mmdupd.51**************************************************************************/52void genmmd(idx_t neqns, idx_t *xadj, idx_t *adjncy, idx_t *invp, idx_t *perm,53idx_t delta, idx_t *head, idx_t *qsize, idx_t *list, idx_t *marker,54idx_t maxint, idx_t *ncsub)55{56idx_t ehead, i, mdeg, mdlmt, mdeg_node, nextmd, num, tag;5758if (neqns <= 0)59return;6061/* Adjust from C to Fortran */62xadj--; adjncy--; invp--; perm--; head--; qsize--; list--; marker--;6364/* initialization for the minimum degree algorithm. */65*ncsub = 0;66mmdint(neqns, xadj, adjncy, head, invp, perm, qsize, list, marker);6768/* 'num' counts the number of ordered nodes plus 1. */69num = 1;7071/* eliminate all isolated nodes. */72nextmd = head[1];73while (nextmd > 0) {74mdeg_node = nextmd;75nextmd = invp[mdeg_node];76marker[mdeg_node] = maxint;77invp[mdeg_node] = -num;78num = num + 1;79}8081/* search for node of the minimum degree. 'mdeg' is the current */82/* minimum degree; 'tag' is used to facilitate marking nodes. */83if (num > neqns)84goto n1000;85tag = 1;86head[1] = 0;87mdeg = 2;8889/* infinite loop here ! */90while (1) {91while (head[mdeg] <= 0)92mdeg++;9394/* use value of 'delta' to set up 'mdlmt', which governs */95/* when a degree update is to be performed. */96mdlmt = mdeg + delta;97ehead = 0;9899n500:100mdeg_node = head[mdeg];101while (mdeg_node <= 0) {102mdeg++;103104if (mdeg > mdlmt)105goto n900;106mdeg_node = head[mdeg];107};108109/* remove 'mdeg_node' from the degree structure. */110nextmd = invp[mdeg_node];111head[mdeg] = nextmd;112if (nextmd > 0)113perm[nextmd] = -mdeg;114invp[mdeg_node] = -num;115*ncsub += mdeg + qsize[mdeg_node] - 2;116if ((num+qsize[mdeg_node]) > neqns)117goto n1000;118119/* eliminate 'mdeg_node' and perform quotient graph */120/* transformation. reset 'tag' value if necessary. */121tag++;122if (tag >= maxint) {123tag = 1;124for (i = 1; i <= neqns; i++)125if (marker[i] < maxint)126marker[i] = 0;127};128129mmdelm(mdeg_node, xadj, adjncy, head, invp, perm, qsize, list, marker, maxint, tag);130131num += qsize[mdeg_node];132list[mdeg_node] = ehead;133ehead = mdeg_node;134if (delta >= 0)135goto n500;136137n900:138/* update degrees of the nodes involved in the */139/* minimum degree nodes elimination. */140if (num > neqns)141goto n1000;142mmdupd( ehead, neqns, xadj, adjncy, delta, &mdeg, head, invp, perm, qsize, list, marker, maxint, &tag);143}; /* end of -- while ( 1 ) -- */144145n1000:146mmdnum( neqns, perm, invp, qsize );147148/* Adjust from Fortran back to C*/149xadj++; adjncy++; invp++; perm++; head++; qsize++; list++; marker++;150}151152153/**************************************************************************154* mmdelm ...... multiple minimum degree elimination155* Purpose -- This routine eliminates the node mdeg_node of minimum degree156* from the adjacency structure, which is stored in the quotient157* graph format. It also transforms the quotient graph representation158* of the elimination graph.159* Input parameters --160* mdeg_node -- node of minimum degree.161* maxint -- estimate of maximum representable (short) integer.162* tag -- tag value.163* Updated parameters --164* (xadj, adjncy) -- updated adjacency structure.165* (head, forward, backward) -- degree doubly linked structure.166* qsize -- size of supernode.167* marker -- marker vector.168* list -- temporary linked list of eliminated nabors.169***************************************************************************/170void mmdelm(idx_t mdeg_node, idx_t *xadj, idx_t *adjncy, idx_t *head, idx_t *forward,171idx_t *backward, idx_t *qsize, idx_t *list, idx_t *marker, idx_t maxint, idx_t tag)172{173idx_t element, i, istop, istart, j,174jstop, jstart, link,175nabor, node, npv, nqnbrs, nxnode,176pvnode, rlmt, rloc, rnode, xqnbr;177178/* find the reachable set of 'mdeg_node' and */179/* place it in the data structure. */180marker[mdeg_node] = tag;181istart = xadj[mdeg_node];182istop = xadj[mdeg_node+1] - 1;183184/* 'element' points to the beginning of the list of */185/* eliminated nabors of 'mdeg_node', and 'rloc' gives the */186/* storage location for the next reachable node. */187element = 0;188rloc = istart;189rlmt = istop;190for ( i = istart; i <= istop; i++ ) {191nabor = adjncy[i];192if ( nabor == 0 ) break;193if ( marker[nabor] < tag ) {194marker[nabor] = tag;195if ( forward[nabor] < 0 ) {196list[nabor] = element;197element = nabor;198} else {199adjncy[rloc] = nabor;200rloc++;201};202}; /* end of -- if -- */203}; /* end of -- for -- */204205/* merge with reachable nodes from generalized elements. */206while ( element > 0 ) {207adjncy[rlmt] = -element;208link = element;209210n400:211jstart = xadj[link];212jstop = xadj[link+1] - 1;213for ( j = jstart; j <= jstop; j++ ) {214node = adjncy[j];215link = -node;216if ( node < 0 ) goto n400;217if ( node == 0 ) break;218if ((marker[node]<tag)&&(forward[node]>=0)) {219marker[node] = tag;220/*use storage from eliminated nodes if necessary.*/221while ( rloc >= rlmt ) {222link = -adjncy[rlmt];223rloc = xadj[link];224rlmt = xadj[link+1] - 1;225};226adjncy[rloc] = node;227rloc++;228};229}; /* end of -- for ( j = jstart; -- */230element = list[element];231}; /* end of -- while ( element > 0 ) -- */232if ( rloc <= rlmt ) adjncy[rloc] = 0;233/* for each node in the reachable set, do the following. */234link = mdeg_node;235236n1100:237istart = xadj[link];238istop = xadj[link+1] - 1;239for ( i = istart; i <= istop; i++ ) {240rnode = adjncy[i];241link = -rnode;242if ( rnode < 0 ) goto n1100;243if ( rnode == 0 ) return;244245/* 'rnode' is in the degree list structure. */246pvnode = backward[rnode];247if (( pvnode != 0 ) && ( pvnode != (-maxint) )) {248/* then remove 'rnode' from the structure. */249nxnode = forward[rnode];250if ( nxnode > 0 ) backward[nxnode] = pvnode;251if ( pvnode > 0 ) forward[pvnode] = nxnode;252npv = -pvnode;253if ( pvnode < 0 ) head[npv] = nxnode;254};255256/* purge inactive quotient nabors of 'rnode'. */257jstart = xadj[rnode];258jstop = xadj[rnode+1] - 1;259xqnbr = jstart;260for ( j = jstart; j <= jstop; j++ ) {261nabor = adjncy[j];262if ( nabor == 0 ) break;263if ( marker[nabor] < tag ) {264adjncy[xqnbr] = nabor;265xqnbr++;266};267};268269/* no active nabor after the purging. */270nqnbrs = xqnbr - jstart;271if ( nqnbrs <= 0 ) {272/* merge 'rnode' with 'mdeg_node'. */273qsize[mdeg_node] += qsize[rnode];274qsize[rnode] = 0;275marker[rnode] = maxint;276forward[rnode] = -mdeg_node;277backward[rnode] = -maxint;278} else {279/* flag 'rnode' for degree update, and */280/* add 'mdeg_node' as a nabor of 'rnode'. */281forward[rnode] = nqnbrs + 1;282backward[rnode] = 0;283adjncy[xqnbr] = mdeg_node;284xqnbr++;285if ( xqnbr <= jstop ) adjncy[xqnbr] = 0;286};287}; /* end of -- for ( i = istart; -- */288return;289}290291/***************************************************************************292* mmdint ---- mult minimum degree initialization293* purpose -- this routine performs initialization for the294* multiple elimination version of the minimum degree algorithm.295* input parameters --296* neqns -- number of equations.297* (xadj, adjncy) -- adjacency structure.298* output parameters --299* (head, dfrow, backward) -- degree doubly linked structure.300* qsize -- size of supernode ( initialized to one).301* list -- linked list.302* marker -- marker vector.303****************************************************************************/304idx_t mmdint(idx_t neqns, idx_t *xadj, idx_t *adjncy, idx_t *head, idx_t *forward,305idx_t *backward, idx_t *qsize, idx_t *list, idx_t *marker)306{307idx_t fnode, ndeg, node;308309for ( node = 1; node <= neqns; node++ ) {310head[node] = 0;311qsize[node] = 1;312marker[node] = 0;313list[node] = 0;314};315316/* initialize the degree doubly linked lists. */317for ( node = 1; node <= neqns; node++ ) {318ndeg = xadj[node+1] - xadj[node]/* + 1*/; /* george */319if (ndeg == 0)320ndeg = 1;321fnode = head[ndeg];322forward[node] = fnode;323head[ndeg] = node;324if ( fnode > 0 ) backward[fnode] = node;325backward[node] = -ndeg;326};327return 0;328}329330/****************************************************************************331* mmdnum --- multi minimum degree numbering332* purpose -- this routine performs the final step in producing333* the permutation and inverse permutation vectors in the334* multiple elimination version of the minimum degree335* ordering algorithm.336* input parameters --337* neqns -- number of equations.338* qsize -- size of supernodes at elimination.339* updated parameters --340* invp -- inverse permutation vector. on input,341* if qsize[node] = 0, then node has been merged342* into the node -invp[node]; otherwise,343* -invp[node] is its inverse labelling.344* output parameters --345* perm -- the permutation vector.346****************************************************************************/347void mmdnum(idx_t neqns, idx_t *perm, idx_t *invp, idx_t *qsize)348{349idx_t father, nextf, node, nqsize, num, root;350351for ( node = 1; node <= neqns; node++ ) {352nqsize = qsize[node];353if ( nqsize <= 0 ) perm[node] = invp[node];354if ( nqsize > 0 ) perm[node] = -invp[node];355};356357/* for each node which has been merged, do the following. */358for ( node = 1; node <= neqns; node++ ) {359if ( perm[node] <= 0 ) {360361/* trace the merged tree until one which has not */362/* been merged, call it root. */363father = node;364while ( perm[father] <= 0 )365father = - perm[father];366367/* number node after root. */368root = father;369num = perm[root] + 1;370invp[node] = -num;371perm[root] = num;372373/* shorten the merged tree. */374father = node;375nextf = - perm[father];376while ( nextf > 0 ) {377perm[father] = -root;378father = nextf;379nextf = -perm[father];380};381}; /* end of -- if ( perm[node] <= 0 ) -- */382}; /* end of -- for ( node = 1; -- */383384/* ready to compute perm. */385for ( node = 1; node <= neqns; node++ ) {386num = -invp[node];387invp[node] = num;388perm[num] = node;389};390return;391}392393/****************************************************************************394* mmdupd ---- multiple minimum degree update395* purpose -- this routine updates the degrees of nodes after a396* multiple elimination step.397* input parameters --398* ehead -- the beginning of the list of eliminated nodes399* (i.e., newly formed elements).400* neqns -- number of equations.401* (xadj, adjncy) -- adjacency structure.402* delta -- tolerance value for multiple elimination.403* maxint -- maximum machine representable (short) integer.404* updated parameters --405* mdeg -- new minimum degree after degree update.406* (head, forward, backward) -- degree doubly linked structure.407* qsize -- size of supernode.408* list -- marker vector for degree update.409* *tag -- tag value.410****************************************************************************/411void mmdupd(idx_t ehead, idx_t neqns, idx_t *xadj, idx_t *adjncy, idx_t delta, idx_t *mdeg,412idx_t *head, idx_t *forward, idx_t *backward, idx_t *qsize, idx_t *list,413idx_t *marker, idx_t maxint, idx_t *tag)414{415idx_t deg, deg0, element, enode, fnode, i, iq2, istop,416istart, j, jstop, jstart, link, mdeg0, mtag, nabor,417node, q2head, qxhead;418419mdeg0 = *mdeg + delta;420element = ehead;421422n100:423if ( element <= 0 ) return;424425/* for each of the newly formed element, do the following. */426/* reset tag value if necessary. */427mtag = *tag + mdeg0;428if ( mtag >= maxint ) {429*tag = 1;430for ( i = 1; i <= neqns; i++ )431if ( marker[i] < maxint ) marker[i] = 0;432mtag = *tag + mdeg0;433};434435/* create two linked lists from nodes associated with 'element': */436/* one with two nabors (q2head) in the adjacency structure, and the*/437/* other with more than two nabors (qxhead). also compute 'deg0',*/438/* number of nodes in this element. */439q2head = 0;440qxhead = 0;441deg0 = 0;442link =element;443444n400:445istart = xadj[link];446istop = xadj[link+1] - 1;447for ( i = istart; i <= istop; i++ ) {448enode = adjncy[i];449link = -enode;450if ( enode < 0 ) goto n400;451if ( enode == 0 ) break;452if ( qsize[enode] != 0 ) {453deg0 += qsize[enode];454marker[enode] = mtag;455456/*'enode' requires a degree update*/457if ( backward[enode] == 0 ) {458/* place either in qxhead or q2head list. */459if ( forward[enode] != 2 ) {460list[enode] = qxhead;461qxhead = enode;462} else {463list[enode] = q2head;464q2head = enode;465};466};467}; /* enf of -- if ( qsize[enode] != 0 ) -- */468}; /* end of -- for ( i = istart; -- */469470/* for each node in q2 list, do the following. */471enode = q2head;472iq2 = 1;473474n900:475if ( enode <= 0 ) goto n1500;476if ( backward[enode] != 0 ) goto n2200;477(*tag)++;478deg = deg0;479480/* identify the other adjacent element nabor. */481istart = xadj[enode];482nabor = adjncy[istart];483if ( nabor == element ) nabor = adjncy[istart+1];484link = nabor;485if ( forward[nabor] >= 0 ) {486/* nabor is uneliminated, increase degree count. */487deg += qsize[nabor];488goto n2100;489};490491/* the nabor is eliminated. for each node in the 2nd element */492/* do the following. */493n1000:494istart = xadj[link];495istop = xadj[link+1] - 1;496for ( i = istart; i <= istop; i++ ) {497node = adjncy[i];498link = -node;499if ( node != enode ) {500if ( node < 0 ) goto n1000;501if ( node == 0 ) goto n2100;502if ( qsize[node] != 0 ) {503if ( marker[node] < *tag ) {504/* 'node' is not yet considered. */505marker[node] = *tag;506deg += qsize[node];507} else {508if ( backward[node] == 0 ) {509if ( forward[node] == 2 ) {510/* 'node' is indistinguishable from 'enode'.*/511/* merge them into a new supernode. */512qsize[enode] += qsize[node];513qsize[node] = 0;514marker[node] = maxint;515forward[node] = -enode;516backward[node] = -maxint;517} else {518/* 'node' is outmacthed by 'enode' */519if (backward[node]==0) backward[node] = -maxint;520};521}; /* end of -- if ( backward[node] == 0 ) -- */522}; /* end of -- if ( marker[node] < *tag ) -- */523}; /* end of -- if ( qsize[node] != 0 ) -- */524}; /* end of -- if ( node != enode ) -- */525}; /* end of -- for ( i = istart; -- */526goto n2100;527528n1500:529/* for each 'enode' in the 'qx' list, do the following. */530enode = qxhead;531iq2 = 0;532533n1600: if ( enode <= 0 ) goto n2300;534if ( backward[enode] != 0 ) goto n2200;535(*tag)++;536deg = deg0;537538/*for each unmarked nabor of 'enode', do the following.*/539istart = xadj[enode];540istop = xadj[enode+1] - 1;541for ( i = istart; i <= istop; i++ ) {542nabor = adjncy[i];543if ( nabor == 0 ) break;544if ( marker[nabor] < *tag ) {545marker[nabor] = *tag;546link = nabor;547if ( forward[nabor] >= 0 )548/*if uneliminated, include it in deg count.*/549deg += qsize[nabor];550else {551n1700:552/* if eliminated, include unmarked nodes in this*/553/* element into the degree count. */554jstart = xadj[link];555jstop = xadj[link+1] - 1;556for ( j = jstart; j <= jstop; j++ ) {557node = adjncy[j];558link = -node;559if ( node < 0 ) goto n1700;560if ( node == 0 ) break;561if ( marker[node] < *tag ) {562marker[node] = *tag;563deg += qsize[node];564};565}; /* end of -- for ( j = jstart; -- */566}; /* end of -- if ( forward[nabor] >= 0 ) -- */567}; /* end of -- if ( marker[nabor] < *tag ) -- */568}; /* end of -- for ( i = istart; -- */569570n2100:571/* update external degree of 'enode' in degree structure, */572/* and '*mdeg' if necessary. */573deg = deg - qsize[enode] + 1;574fnode = head[deg];575forward[enode] = fnode;576backward[enode] = -deg;577if ( fnode > 0 ) backward[fnode] = enode;578head[deg] = enode;579if ( deg < *mdeg ) *mdeg = deg;580581n2200:582/* get next enode in current element. */583enode = list[enode];584if ( iq2 == 1 ) goto n900;585goto n1600;586587n2300:588/* get next element in the list. */589*tag = mtag;590element = list[element];591goto n100;592}593594595