1 /* glpqmd.c (quotient minimum degree algorithm) */
3 /***********************************************************************
4 * This code is part of GLPK (GNU Linear Programming Kit).
6 * THIS CODE IS THE RESULT OF TRANSLATION OF THE FORTRAN SUBROUTINES
7 * GENQMD, QMDRCH, QMDQT, QMDUPD, AND QMDMRG FROM THE BOOK:
9 * ALAN GEORGE, JOSEPH W-H LIU. COMPUTER SOLUTION OF LARGE SPARSE
10 * POSITIVE DEFINITE SYSTEMS. PRENTICE-HALL, 1981.
12 * THE TRANSLATION HAS BEEN DONE WITH THE PERMISSION OF THE AUTHORS
13 * OF THE ORIGINAL FORTRAN SUBROUTINES: ALAN GEORGE AND JOSEPH LIU,
14 * UNIVERSITY OF WATERLOO, WATERLOO, ONTARIO, CANADA.
16 * The translation was made by Andrew Makhorin <mao@gnu.org>.
18 * GLPK is free software: you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
23 * GLPK is distributed in the hope that it will be useful, but WITHOUT
24 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
26 * License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
30 ***********************************************************************/
34 /***********************************************************************
37 * genqmd - GENeral Quotient Minimum Degree algorithm
42 * void genqmd(int *neqns, int xadj[], int adjncy[], int perm[],
43 * int invp[], int deg[], int marker[], int rchset[], int nbrhd[],
44 * int qsize[], int qlink[], int *nofsub);
48 * This routine implements the minimum degree algorithm. It makes use
49 * of the implicit representation of the elimination graph by quotient
50 * graphs, and the notion of indistinguishable nodes.
54 * The adjancy vector adjncy will be destroyed.
58 * neqns - number of equations;
60 * the adjancy structure.
64 * perm - the minimum degree ordering;
65 * invp - the inverse of perm.
69 * deg - the degree vector. deg[i] is negative means node i has been
71 * marker - a marker vector, where marker[i] is negative means node i
72 * has been merged with another nodeand thus can be ignored;
73 * rchset - vector used for the reachable set;
74 * nbrhd - vector used for neighborhood set;
75 * qsize - vector used to store the size of indistinguishable
77 * qlink - vector used to store indistinguishable nodes, i, qlink[i],
78 * qlink[qlink[i]], ... are the members of the supernode
83 * qmdrch, qmdqt, qmdupd.
84 ***********************************************************************/
86 void genqmd(int *_neqns, int xadj[], int adjncy[], int perm[],
87 int invp[], int deg[], int marker[], int rchset[], int nbrhd[],
88 int qsize[], int qlink[], int *_nofsub)
89 { int inode, ip, irch, j, mindeg, ndeg, nhdsze, node, np, num,
90 nump1, nxnode, rchsze, search, thresh;
91 # define neqns (*_neqns)
92 # define nofsub (*_nofsub)
93 /* Initialize degree vector and other working variables. */
96 for (node = 1; node <= neqns; node++)
102 ndeg = xadj[node+1] - xadj[node];
104 if (ndeg < mindeg) mindeg = ndeg;
107 /* Perform threshold search to get a node of min degree.
108 Variable search point to where search should start. */
112 s300: nump1 = num + 1;
113 if (nump1 > search) search = nump1;
114 for (j = search; j <= neqns; j++)
116 if (marker[node] >= 0)
118 if (ndeg <= thresh) goto s500;
119 if (ndeg < mindeg) mindeg = ndeg;
123 /* Node has minimum degree. Find its reachable sets by calling
128 qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, &nhdsze,
130 /* Eliminate all nodes indistinguishable from node. They are given
131 by node, qlink[node], ... . */
141 nxnode = qlink[nxnode];
142 if (nxnode > 0) goto s600;
144 { /* Update the degrees of the nodes in the reachable set and
145 identify indistinguishable nodes. */
146 qmdupd(xadj, adjncy, &rchsze, rchset, deg, qsize, qlink,
147 marker, &rchset[rchsze+1], &nbrhd[nhdsze+1]);
148 /* Reset marker value of nodes in reach set. Update threshold
149 value for cyclic search. Also call qmdqt to form new
152 for (irch = 1; irch <= rchsze; irch++)
153 { inode = rchset[irch];
154 if (marker[inode] >= 0)
157 if (ndeg < mindeg) mindeg = ndeg;
161 search = invp[inode];
166 qmdqt(&node, xadj, adjncy, marker, &rchsze, rchset, nbrhd);
168 if (num < neqns) goto s300;
174 /***********************************************************************
177 * qmdrch - Quotient MD ReaCHable set
181 * #include "glpqmd.h"
182 * void qmdrch(int *root, int xadj[], int adjncy[], int deg[],
183 * int marker[], int *rchsze, int rchset[], int *nhdsze,
188 * This subroutine determines the reachable set of a node through a
189 * given subset. The adjancy structure is assumed to be stored in a
190 * quotient graph format.
194 * root - the given node not in the subset;
196 * the adjancy structure pair;
197 * deg - the degree vector. deg[i] < 0 means the node belongs to the
205 * the neighborhood set.
209 * marker - the marker vector for reach and nbrhd sets. > 0 means the
210 * node is in reach set. < 0 means the node has been merged
211 * with others in the quotient or it is in nbrhd set.
212 ***********************************************************************/
214 void qmdrch(int *_root, int xadj[], int adjncy[], int deg[],
215 int marker[], int *_rchsze, int rchset[], int *_nhdsze,
217 { int i, istop, istrt, j, jstop, jstrt, nabor, node;
218 # define root (*_root)
219 # define rchsze (*_rchsze)
220 # define nhdsze (*_nhdsze)
221 /* Loop through the neighbors of root in the quotient graph. */
225 istop = xadj[root+1] - 1;
226 if (istop < istrt) return;
227 for (i = istrt; i <= istop; i++)
229 if (nabor == 0) return;
230 if (marker[nabor] == 0)
231 { if (deg[nabor] >= 0)
232 { /* Include nabor into the reachable set. */
234 rchset[rchsze] = nabor;
238 /* nabor has been eliminated. Find nodes reachable from
242 nbrhd[nhdsze] = nabor;
243 s300: jstrt = xadj[nabor];
244 jstop = xadj[nabor+1] - 1;
245 for (j = jstrt; j <= jstop; j++)
248 if (node < 0) goto s300;
249 if (node == 0) goto s600;
250 if (marker[node] == 0)
252 rchset[rchsze] = node;
265 /***********************************************************************
268 * qmdqt - Quotient MD Quotient graph Transformation
272 * #include "glpqmd.h"
273 * void qmdqt(int *root, int xadj[], int adjncy[], int marker[],
274 * int *rchsze, int rchset[], int nbrhd[]);
278 * This subroutine performs the quotient graph transformation after a
279 * node has been eliminated.
283 * root - the node just eliminated. It becomes the representative of
286 * the adjancy structure;
288 * the reachable set of root in the old quotient graph;
289 * nbrhd - the neighborhood set which will be merged with root to form
291 * marker - the marker vector.
295 * adjncy - becomes the adjncy of the quotient graph.
296 ***********************************************************************/
298 void qmdqt(int *_root, int xadj[], int adjncy[], int marker[],
299 int *_rchsze, int rchset[], int nbrhd[])
300 { int inhd, irch, j, jstop, jstrt, link, nabor, node;
301 # define root (*_root)
302 # define rchsze (*_rchsze)
306 s100: jstrt = xadj[node];
307 jstop = xadj[node+1] - 2;
309 { /* Place reach nodes into the adjacent list of node. */
310 for (j = jstrt; j <= jstop; j++)
312 adjncy[j] = rchset[irch];
313 if (irch >= rchsze) goto s400;
316 /* Link to other space provided by the nbrhd set. */
317 link = adjncy[jstop+1];
322 adjncy[jstop+1] = - node;
325 /* All reachable nodes have been saved. End the adjacent list.
326 Add root to the neighborhood list of each node in the reach
328 s400: adjncy[j+1] = 0;
329 for (irch = 1; irch <= rchsze; irch++)
330 { node = rchset[irch];
331 if (marker[node] >= 0)
332 { jstrt = xadj[node];
333 jstop = xadj[node+1] - 1;
334 for (j = jstrt; j <= jstop; j++)
336 if (marker[nabor] < 0)
349 /***********************************************************************
352 * qmdupd - Quotient MD UPDate
356 * #include "glpqmd.h"
357 * void qmdupd(int xadj[], int adjncy[], int *nlist, int list[],
358 * int deg[], int qsize[], int qlink[], int marker[], int rchset[],
363 * This routine performs degree update for a set of nodes in the minimum
369 * the adjancy structure;
371 * the list of nodes whose degree has to be updated.
375 * deg - the degree vector;
376 * qsize - size of indistinguishable supernodes;
377 * qlink - linked list for indistinguishable nodes;
378 * marker - used to mark those nodes in reach/nbrhd sets.
382 * rchset - the reachable set;
383 * nbrhd - the neighborhood set.
385 * PROGRAM SUBROUTINES
388 ***********************************************************************/
390 void qmdupd(int xadj[], int adjncy[], int *_nlist, int list[],
391 int deg[], int qsize[], int qlink[], int marker[], int rchset[],
393 { int deg0, deg1, il, inhd, inode, irch, j, jstop, jstrt, mark,
394 nabor, nhdsze, node, rchsze;
395 # define nlist (*_nlist)
396 /* Find all eliminated supernodes that are adjacent to some nodes
397 in the given list. Put them into (nhdsze, nbrhd). deg0 contains
398 the number of nodes in the list. */
399 if (nlist <= 0) return;
402 for (il = 1; il <= nlist; il++)
406 jstop = xadj[node+1] - 1;
407 for (j = jstrt; j <= jstop; j++)
409 if (marker[nabor] == 0 && deg[nabor] < 0)
410 { marker[nabor] = -1;
412 nbrhd[nhdsze] = nabor;
416 /* Merge indistinguishable nodes in the list by calling the
417 subroutine qmdmrg. */
419 qmdmrg(xadj, adjncy, deg, qsize, qlink, marker, °0, &nhdsze,
420 nbrhd, rchset, &nbrhd[nhdsze+1]);
421 /* Find the new degrees of the nodes that have not been merged. */
422 for (il = 1; il <= nlist; il++)
425 if (mark == 0 || mark == 1)
427 qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset,
431 { for (irch = 1; irch <= rchsze; irch++)
432 { inode = rchset[irch];
433 deg1 += qsize[inode];
437 deg[node] = deg1 - 1;
439 { for (inhd = 1; inhd <= nhdsze; inhd++)
440 { inode = nbrhd[inhd];
450 /***********************************************************************
453 * qmdmrg - Quotient MD MeRGe
457 * #include "qmdmrg.h"
458 * void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[],
459 * int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[],
460 * int rchset[], int ovrlp[]);
464 * This routine merges indistinguishable nodes in the minimum degree
465 * ordering algorithm. It also computes the new degrees of these new
471 * the adjancy structure;
472 * deg0 - the number of nodes in the given set;
474 * the set of eliminated supernodes adjacent to some nodes in
479 * deg - the degree vector;
480 * qsize - size of indistinguishable nodes;
481 * qlink - linked list for indistinguishable nodes;
482 * marker - the given set is given by those nodes with marker value set
483 * to 1. Those nodes with degree updated will have marker value
488 * rchset - the reachable set;
489 * ovrlp - temp vector to store the intersection of two reachable sets.
490 ***********************************************************************/
492 void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[],
493 int qlink[], int marker[], int *_deg0, int *_nhdsze, int nbrhd[],
494 int rchset[], int ovrlp[])
495 { int deg1, head, inhd, iov, irch, j, jstop, jstrt, link, lnode,
496 mark, mrgsze, nabor, node, novrlp, rchsze, root;
497 # define deg0 (*_deg0)
498 # define nhdsze (*_nhdsze)
499 /* Initialization. */
500 if (nhdsze <= 0) return;
501 for (inhd = 1; inhd <= nhdsze; inhd++)
502 { root = nbrhd[inhd];
505 /* Loop through each eliminated supernode in the set
507 for (inhd = 1; inhd <= nhdsze; inhd++)
508 { root = nbrhd[inhd];
513 s200: jstrt = xadj[root];
514 jstop = xadj[root+1] - 1;
515 /* Determine the reachable set and its intersection with the
516 input reachable set. */
517 for (j = jstrt; j <= jstop; j++)
520 if (nabor < 0) goto s200;
521 if (nabor == 0) break;
522 mark = marker[nabor];
525 rchset[rchsze] = nabor;
526 deg1 += qsize[nabor];
531 ovrlp[novrlp] = nabor;
535 /* From the overlapped set, determine the nodes that can be
539 for (iov = 1; iov <= novrlp; iov++)
542 jstop = xadj[node+1] - 1;
543 for (j = jstrt; j <= jstop; j++)
545 if (marker[nabor] == 0)
550 /* Node belongs to the new merged supernode. Update the
551 vectors qlink and qsize. */
552 mrgsze += qsize[node];
555 s900: link = qlink[lnode];
565 { qsize[head] = mrgsze;
566 deg[head] = deg0 + deg1 - 1;
569 /* Reset marker values. */
573 { for (irch = 1; irch <= rchsze; irch++)
574 { node = rchset[irch];