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.
584 lines
18 KiB
584 lines
18 KiB
/* qmd.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 "qmd.h"
|
|
|
|
/***********************************************************************
|
|
* NAME
|
|
*
|
|
* genqmd - GENeral Quotient Minimum Degree algorithm
|
|
*
|
|
* SYNOPSIS
|
|
*
|
|
* #include "qmd.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 "qmd.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 "qmd.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 "qmd.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 "qmd.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 */
|