123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- /* glpqmd.c (quotient minimum degree algorithm) */
- /***********************************************************************
- * This code is part of GLPK (GNU Linear Programming Kit).
- *
- * THIS CODE IS THE RESULT OF TRANSLATION OF THE FORTRAN SUBROUTINES
- * GENQMD, QMDRCH, QMDQT, QMDUPD, AND QMDMRG FROM THE BOOK:
- *
- * ALAN GEORGE, JOSEPH W-H LIU. COMPUTER SOLUTION OF LARGE SPARSE
- * POSITIVE DEFINITE SYSTEMS. PRENTICE-HALL, 1981.
- *
- * THE TRANSLATION HAS BEEN DONE WITH THE PERMISSION OF THE AUTHORS
- * OF THE ORIGINAL FORTRAN SUBROUTINES: ALAN GEORGE AND JOSEPH LIU,
- * UNIVERSITY OF WATERLOO, WATERLOO, ONTARIO, CANADA.
- *
- * The translation was made by Andrew Makhorin <mao@gnu.org>.
- *
- * GLPK is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GLPK is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
- ***********************************************************************/
- #include "glpqmd.h"
- /***********************************************************************
- * NAME
- *
- * genqmd - GENeral Quotient Minimum Degree algorithm
- *
- * SYNOPSIS
- *
- * #include "glpqmd.h"
- * void genqmd(int *neqns, int xadj[], int adjncy[], int perm[],
- * int invp[], int deg[], int marker[], int rchset[], int nbrhd[],
- * int qsize[], int qlink[], int *nofsub);
- *
- * PURPOSE
- *
- * This routine implements the minimum degree algorithm. It makes use
- * of the implicit representation of the elimination graph by quotient
- * graphs, and the notion of indistinguishable nodes.
- *
- * CAUTION
- *
- * The adjancy vector adjncy will be destroyed.
- *
- * INPUT PARAMETERS
- *
- * neqns - number of equations;
- * (xadj, adjncy) -
- * the adjancy structure.
- *
- * OUTPUT PARAMETERS
- *
- * perm - the minimum degree ordering;
- * invp - the inverse of perm.
- *
- * WORKING PARAMETERS
- *
- * deg - the degree vector. deg[i] is negative means node i has been
- * numbered;
- * marker - a marker vector, where marker[i] is negative means node i
- * has been merged with another nodeand thus can be ignored;
- * rchset - vector used for the reachable set;
- * nbrhd - vector used for neighborhood set;
- * qsize - vector used to store the size of indistinguishable
- * supernodes;
- * qlink - vector used to store indistinguishable nodes, i, qlink[i],
- * qlink[qlink[i]], ... are the members of the supernode
- * represented by i.
- *
- * PROGRAM SUBROUTINES
- *
- * qmdrch, qmdqt, qmdupd.
- ***********************************************************************/
- void genqmd(int *_neqns, int xadj[], int adjncy[], int perm[],
- int invp[], int deg[], int marker[], int rchset[], int nbrhd[],
- int qsize[], int qlink[], int *_nofsub)
- { int inode, ip, irch, j, mindeg, ndeg, nhdsze, node, np, num,
- nump1, nxnode, rchsze, search, thresh;
- # define neqns (*_neqns)
- # define nofsub (*_nofsub)
- /* Initialize degree vector and other working variables. */
- mindeg = neqns;
- nofsub = 0;
- for (node = 1; node <= neqns; node++)
- { perm[node] = node;
- invp[node] = node;
- marker[node] = 0;
- qsize[node] = 1;
- qlink[node] = 0;
- ndeg = xadj[node+1] - xadj[node];
- deg[node] = ndeg;
- if (ndeg < mindeg) mindeg = ndeg;
- }
- num = 0;
- /* Perform threshold search to get a node of min degree.
- Variable search point to where search should start. */
- s200: search = 1;
- thresh = mindeg;
- mindeg = neqns;
- s300: nump1 = num + 1;
- if (nump1 > search) search = nump1;
- for (j = search; j <= neqns; j++)
- { node = perm[j];
- if (marker[node] >= 0)
- { ndeg = deg[node];
- if (ndeg <= thresh) goto s500;
- if (ndeg < mindeg) mindeg = ndeg;
- }
- }
- goto s200;
- /* Node has minimum degree. Find its reachable sets by calling
- qmdrch. */
- s500: search = j;
- nofsub += deg[node];
- marker[node] = 1;
- qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, &nhdsze,
- nbrhd);
- /* Eliminate all nodes indistinguishable from node. They are given
- by node, qlink[node], ... . */
- nxnode = node;
- s600: num++;
- np = invp[nxnode];
- ip = perm[num];
- perm[np] = ip;
- invp[ip] = np;
- perm[num] = nxnode;
- invp[nxnode] = num;
- deg[nxnode] = -1;
- nxnode = qlink[nxnode];
- if (nxnode > 0) goto s600;
- if (rchsze > 0)
- { /* Update the degrees of the nodes in the reachable set and
- identify indistinguishable nodes. */
- qmdupd(xadj, adjncy, &rchsze, rchset, deg, qsize, qlink,
- marker, &rchset[rchsze+1], &nbrhd[nhdsze+1]);
- /* Reset marker value of nodes in reach set. Update threshold
- value for cyclic search. Also call qmdqt to form new
- quotient graph. */
- marker[node] = 0;
- for (irch = 1; irch <= rchsze; irch++)
- { inode = rchset[irch];
- if (marker[inode] >= 0)
- { marker[inode] = 0;
- ndeg = deg[inode];
- if (ndeg < mindeg) mindeg = ndeg;
- if (ndeg <= thresh)
- { mindeg = thresh;
- thresh = ndeg;
- search = invp[inode];
- }
- }
- }
- if (nhdsze > 0)
- qmdqt(&node, xadj, adjncy, marker, &rchsze, rchset, nbrhd);
- }
- if (num < neqns) goto s300;
- return;
- # undef neqns
- # undef nofsub
- }
- /***********************************************************************
- * NAME
- *
- * qmdrch - Quotient MD ReaCHable set
- *
- * SYNOPSIS
- *
- * #include "glpqmd.h"
- * void qmdrch(int *root, int xadj[], int adjncy[], int deg[],
- * int marker[], int *rchsze, int rchset[], int *nhdsze,
- * int nbrhd[]);
- *
- * PURPOSE
- *
- * This subroutine determines the reachable set of a node through a
- * given subset. The adjancy structure is assumed to be stored in a
- * quotient graph format.
- *
- * INPUT PARAMETERS
- *
- * root - the given node not in the subset;
- * (xadj, adjncy) -
- * the adjancy structure pair;
- * deg - the degree vector. deg[i] < 0 means the node belongs to the
- * given subset.
- *
- * OUTPUT PARAMETERS
- *
- * (rchsze, rchset) -
- * the reachable set;
- * (nhdsze, nbrhd) -
- * the neighborhood set.
- *
- * UPDATED PARAMETERS
- *
- * marker - the marker vector for reach and nbrhd sets. > 0 means the
- * node is in reach set. < 0 means the node has been merged
- * with others in the quotient or it is in nbrhd set.
- ***********************************************************************/
- void qmdrch(int *_root, int xadj[], int adjncy[], int deg[],
- int marker[], int *_rchsze, int rchset[], int *_nhdsze,
- int nbrhd[])
- { int i, istop, istrt, j, jstop, jstrt, nabor, node;
- # define root (*_root)
- # define rchsze (*_rchsze)
- # define nhdsze (*_nhdsze)
- /* Loop through the neighbors of root in the quotient graph. */
- nhdsze = 0;
- rchsze = 0;
- istrt = xadj[root];
- istop = xadj[root+1] - 1;
- if (istop < istrt) return;
- for (i = istrt; i <= istop; i++)
- { nabor = adjncy[i];
- if (nabor == 0) return;
- if (marker[nabor] == 0)
- { if (deg[nabor] >= 0)
- { /* Include nabor into the reachable set. */
- rchsze++;
- rchset[rchsze] = nabor;
- marker[nabor] = 1;
- goto s600;
- }
- /* nabor has been eliminated. Find nodes reachable from
- it. */
- marker[nabor] = -1;
- nhdsze++;
- nbrhd[nhdsze] = nabor;
- s300: jstrt = xadj[nabor];
- jstop = xadj[nabor+1] - 1;
- for (j = jstrt; j <= jstop; j++)
- { node = adjncy[j];
- nabor = - node;
- if (node < 0) goto s300;
- if (node == 0) goto s600;
- if (marker[node] == 0)
- { rchsze++;
- rchset[rchsze] = node;
- marker[node] = 1;
- }
- }
- }
- s600: ;
- }
- return;
- # undef root
- # undef rchsze
- # undef nhdsze
- }
- /***********************************************************************
- * NAME
- *
- * qmdqt - Quotient MD Quotient graph Transformation
- *
- * SYNOPSIS
- *
- * #include "glpqmd.h"
- * void qmdqt(int *root, int xadj[], int adjncy[], int marker[],
- * int *rchsze, int rchset[], int nbrhd[]);
- *
- * PURPOSE
- *
- * This subroutine performs the quotient graph transformation after a
- * node has been eliminated.
- *
- * INPUT PARAMETERS
- *
- * root - the node just eliminated. It becomes the representative of
- * the new supernode;
- * (xadj, adjncy) -
- * the adjancy structure;
- * (rchsze, rchset) -
- * the reachable set of root in the old quotient graph;
- * nbrhd - the neighborhood set which will be merged with root to form
- * the new supernode;
- * marker - the marker vector.
- *
- * UPDATED PARAMETERS
- *
- * adjncy - becomes the adjncy of the quotient graph.
- ***********************************************************************/
- void qmdqt(int *_root, int xadj[], int adjncy[], int marker[],
- int *_rchsze, int rchset[], int nbrhd[])
- { int inhd, irch, j, jstop, jstrt, link, nabor, node;
- # define root (*_root)
- # define rchsze (*_rchsze)
- irch = 0;
- inhd = 0;
- node = root;
- s100: jstrt = xadj[node];
- jstop = xadj[node+1] - 2;
- if (jstop >= jstrt)
- { /* Place reach nodes into the adjacent list of node. */
- for (j = jstrt; j <= jstop; j++)
- { irch++;
- adjncy[j] = rchset[irch];
- if (irch >= rchsze) goto s400;
- }
- }
- /* Link to other space provided by the nbrhd set. */
- link = adjncy[jstop+1];
- node = - link;
- if (link >= 0)
- { inhd++;
- node = nbrhd[inhd];
- adjncy[jstop+1] = - node;
- }
- goto s100;
- /* All reachable nodes have been saved. End the adjacent list.
- Add root to the neighborhood list of each node in the reach
- set. */
- s400: adjncy[j+1] = 0;
- for (irch = 1; irch <= rchsze; irch++)
- { node = rchset[irch];
- if (marker[node] >= 0)
- { jstrt = xadj[node];
- jstop = xadj[node+1] - 1;
- for (j = jstrt; j <= jstop; j++)
- { nabor = adjncy[j];
- if (marker[nabor] < 0)
- { adjncy[j] = root;
- goto s600;
- }
- }
- }
- s600: ;
- }
- return;
- # undef root
- # undef rchsze
- }
- /***********************************************************************
- * NAME
- *
- * qmdupd - Quotient MD UPDate
- *
- * SYNOPSIS
- *
- * #include "glpqmd.h"
- * void qmdupd(int xadj[], int adjncy[], int *nlist, int list[],
- * int deg[], int qsize[], int qlink[], int marker[], int rchset[],
- * int nbrhd[]);
- *
- * PURPOSE
- *
- * This routine performs degree update for a set of nodes in the minimum
- * degree algorithm.
- *
- * INPUT PARAMETERS
- *
- * (xadj, adjncy) -
- * the adjancy structure;
- * (nlist, list) -
- * the list of nodes whose degree has to be updated.
- *
- * UPDATED PARAMETERS
- *
- * deg - the degree vector;
- * qsize - size of indistinguishable supernodes;
- * qlink - linked list for indistinguishable nodes;
- * marker - used to mark those nodes in reach/nbrhd sets.
- *
- * WORKING PARAMETERS
- *
- * rchset - the reachable set;
- * nbrhd - the neighborhood set.
- *
- * PROGRAM SUBROUTINES
- *
- * qmdmrg.
- ***********************************************************************/
- void qmdupd(int xadj[], int adjncy[], int *_nlist, int list[],
- int deg[], int qsize[], int qlink[], int marker[], int rchset[],
- int nbrhd[])
- { int deg0, deg1, il, inhd, inode, irch, j, jstop, jstrt, mark,
- nabor, nhdsze, node, rchsze;
- # define nlist (*_nlist)
- /* Find all eliminated supernodes that are adjacent to some nodes
- in the given list. Put them into (nhdsze, nbrhd). deg0 contains
- the number of nodes in the list. */
- if (nlist <= 0) return;
- deg0 = 0;
- nhdsze = 0;
- for (il = 1; il <= nlist; il++)
- { node = list[il];
- deg0 += qsize[node];
- jstrt = xadj[node];
- jstop = xadj[node+1] - 1;
- for (j = jstrt; j <= jstop; j++)
- { nabor = adjncy[j];
- if (marker[nabor] == 0 && deg[nabor] < 0)
- { marker[nabor] = -1;
- nhdsze++;
- nbrhd[nhdsze] = nabor;
- }
- }
- }
- /* Merge indistinguishable nodes in the list by calling the
- subroutine qmdmrg. */
- if (nhdsze > 0)
- qmdmrg(xadj, adjncy, deg, qsize, qlink, marker, °0, &nhdsze,
- nbrhd, rchset, &nbrhd[nhdsze+1]);
- /* Find the new degrees of the nodes that have not been merged. */
- for (il = 1; il <= nlist; il++)
- { node = list[il];
- mark = marker[node];
- if (mark == 0 || mark == 1)
- { marker[node] = 2;
- qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset,
- &nhdsze, nbrhd);
- deg1 = deg0;
- if (rchsze > 0)
- { for (irch = 1; irch <= rchsze; irch++)
- { inode = rchset[irch];
- deg1 += qsize[inode];
- marker[inode] = 0;
- }
- }
- deg[node] = deg1 - 1;
- if (nhdsze > 0)
- { for (inhd = 1; inhd <= nhdsze; inhd++)
- { inode = nbrhd[inhd];
- marker[inode] = 0;
- }
- }
- }
- }
- return;
- # undef nlist
- }
- /***********************************************************************
- * NAME
- *
- * qmdmrg - Quotient MD MeRGe
- *
- * SYNOPSIS
- *
- * #include "qmdmrg.h"
- * void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[],
- * int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[],
- * int rchset[], int ovrlp[]);
- *
- * PURPOSE
- *
- * This routine merges indistinguishable nodes in the minimum degree
- * ordering algorithm. It also computes the new degrees of these new
- * supernodes.
- *
- * INPUT PARAMETERS
- *
- * (xadj, adjncy) -
- * the adjancy structure;
- * deg0 - the number of nodes in the given set;
- * (nhdsze, nbrhd) -
- * the set of eliminated supernodes adjacent to some nodes in
- * the set.
- *
- * UPDATED PARAMETERS
- *
- * deg - the degree vector;
- * qsize - size of indistinguishable nodes;
- * qlink - linked list for indistinguishable nodes;
- * marker - the given set is given by those nodes with marker value set
- * to 1. Those nodes with degree updated will have marker value
- * set to 2.
- *
- * WORKING PARAMETERS
- *
- * rchset - the reachable set;
- * ovrlp - temp vector to store the intersection of two reachable sets.
- ***********************************************************************/
- void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[],
- int qlink[], int marker[], int *_deg0, int *_nhdsze, int nbrhd[],
- int rchset[], int ovrlp[])
- { int deg1, head, inhd, iov, irch, j, jstop, jstrt, link, lnode,
- mark, mrgsze, nabor, node, novrlp, rchsze, root;
- # define deg0 (*_deg0)
- # define nhdsze (*_nhdsze)
- /* Initialization. */
- if (nhdsze <= 0) return;
- for (inhd = 1; inhd <= nhdsze; inhd++)
- { root = nbrhd[inhd];
- marker[root] = 0;
- }
- /* Loop through each eliminated supernode in the set
- (nhdsze, nbrhd). */
- for (inhd = 1; inhd <= nhdsze; inhd++)
- { root = nbrhd[inhd];
- marker[root] = -1;
- rchsze = 0;
- novrlp = 0;
- deg1 = 0;
- s200: jstrt = xadj[root];
- jstop = xadj[root+1] - 1;
- /* Determine the reachable set and its intersection with the
- input reachable set. */
- for (j = jstrt; j <= jstop; j++)
- { nabor = adjncy[j];
- root = - nabor;
- if (nabor < 0) goto s200;
- if (nabor == 0) break;
- mark = marker[nabor];
- if (mark == 0)
- { rchsze++;
- rchset[rchsze] = nabor;
- deg1 += qsize[nabor];
- marker[nabor] = 1;
- }
- else if (mark == 1)
- { novrlp++;
- ovrlp[novrlp] = nabor;
- marker[nabor] = 2;
- }
- }
- /* From the overlapped set, determine the nodes that can be
- merged together. */
- head = 0;
- mrgsze = 0;
- for (iov = 1; iov <= novrlp; iov++)
- { node = ovrlp[iov];
- jstrt = xadj[node];
- jstop = xadj[node+1] - 1;
- for (j = jstrt; j <= jstop; j++)
- { nabor = adjncy[j];
- if (marker[nabor] == 0)
- { marker[node] = 1;
- goto s1100;
- }
- }
- /* Node belongs to the new merged supernode. Update the
- vectors qlink and qsize. */
- mrgsze += qsize[node];
- marker[node] = -1;
- lnode = node;
- s900: link = qlink[lnode];
- if (link > 0)
- { lnode = link;
- goto s900;
- }
- qlink[lnode] = head;
- head = node;
- s1100: ;
- }
- if (head > 0)
- { qsize[head] = mrgsze;
- deg[head] = deg0 + deg1 - 1;
- marker[head] = 2;
- }
- /* Reset marker values. */
- root = nbrhd[inhd];
- marker[root] = 0;
- if (rchsze > 0)
- { for (irch = 1; irch <= rchsze; irch++)
- { node = rchset[irch];
- marker[node] = 0;
- }
- }
- }
- return;
- # undef deg0
- # undef nhdsze
- }
- /* eof */
|