glpqmd.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /* glpqmd.c (quotient minimum degree algorithm) */
  2. /***********************************************************************
  3. * This code is part of GLPK (GNU Linear Programming Kit).
  4. *
  5. * THIS CODE IS THE RESULT OF TRANSLATION OF THE FORTRAN SUBROUTINES
  6. * GENQMD, QMDRCH, QMDQT, QMDUPD, AND QMDMRG FROM THE BOOK:
  7. *
  8. * ALAN GEORGE, JOSEPH W-H LIU. COMPUTER SOLUTION OF LARGE SPARSE
  9. * POSITIVE DEFINITE SYSTEMS. PRENTICE-HALL, 1981.
  10. *
  11. * THE TRANSLATION HAS BEEN DONE WITH THE PERMISSION OF THE AUTHORS
  12. * OF THE ORIGINAL FORTRAN SUBROUTINES: ALAN GEORGE AND JOSEPH LIU,
  13. * UNIVERSITY OF WATERLOO, WATERLOO, ONTARIO, CANADA.
  14. *
  15. * The translation was made by Andrew Makhorin <mao@gnu.org>.
  16. *
  17. * GLPK is free software: you can redistribute it and/or modify it
  18. * under the terms of the GNU General Public License as published by
  19. * the Free Software Foundation, either version 3 of the License, or
  20. * (at your option) any later version.
  21. *
  22. * GLPK is distributed in the hope that it will be useful, but WITHOUT
  23. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  24. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  25. * License for more details.
  26. *
  27. * You should have received a copy of the GNU General Public License
  28. * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
  29. ***********************************************************************/
  30. #include "glpqmd.h"
  31. /***********************************************************************
  32. * NAME
  33. *
  34. * genqmd - GENeral Quotient Minimum Degree algorithm
  35. *
  36. * SYNOPSIS
  37. *
  38. * #include "glpqmd.h"
  39. * void genqmd(int *neqns, int xadj[], int adjncy[], int perm[],
  40. * int invp[], int deg[], int marker[], int rchset[], int nbrhd[],
  41. * int qsize[], int qlink[], int *nofsub);
  42. *
  43. * PURPOSE
  44. *
  45. * This routine implements the minimum degree algorithm. It makes use
  46. * of the implicit representation of the elimination graph by quotient
  47. * graphs, and the notion of indistinguishable nodes.
  48. *
  49. * CAUTION
  50. *
  51. * The adjancy vector adjncy will be destroyed.
  52. *
  53. * INPUT PARAMETERS
  54. *
  55. * neqns - number of equations;
  56. * (xadj, adjncy) -
  57. * the adjancy structure.
  58. *
  59. * OUTPUT PARAMETERS
  60. *
  61. * perm - the minimum degree ordering;
  62. * invp - the inverse of perm.
  63. *
  64. * WORKING PARAMETERS
  65. *
  66. * deg - the degree vector. deg[i] is negative means node i has been
  67. * numbered;
  68. * marker - a marker vector, where marker[i] is negative means node i
  69. * has been merged with another nodeand thus can be ignored;
  70. * rchset - vector used for the reachable set;
  71. * nbrhd - vector used for neighborhood set;
  72. * qsize - vector used to store the size of indistinguishable
  73. * supernodes;
  74. * qlink - vector used to store indistinguishable nodes, i, qlink[i],
  75. * qlink[qlink[i]], ... are the members of the supernode
  76. * represented by i.
  77. *
  78. * PROGRAM SUBROUTINES
  79. *
  80. * qmdrch, qmdqt, qmdupd.
  81. ***********************************************************************/
  82. void genqmd(int *_neqns, int xadj[], int adjncy[], int perm[],
  83. int invp[], int deg[], int marker[], int rchset[], int nbrhd[],
  84. int qsize[], int qlink[], int *_nofsub)
  85. { int inode, ip, irch, j, mindeg, ndeg, nhdsze, node, np, num,
  86. nump1, nxnode, rchsze, search, thresh;
  87. # define neqns (*_neqns)
  88. # define nofsub (*_nofsub)
  89. /* Initialize degree vector and other working variables. */
  90. mindeg = neqns;
  91. nofsub = 0;
  92. for (node = 1; node <= neqns; node++)
  93. { perm[node] = node;
  94. invp[node] = node;
  95. marker[node] = 0;
  96. qsize[node] = 1;
  97. qlink[node] = 0;
  98. ndeg = xadj[node+1] - xadj[node];
  99. deg[node] = ndeg;
  100. if (ndeg < mindeg) mindeg = ndeg;
  101. }
  102. num = 0;
  103. /* Perform threshold search to get a node of min degree.
  104. Variable search point to where search should start. */
  105. s200: search = 1;
  106. thresh = mindeg;
  107. mindeg = neqns;
  108. s300: nump1 = num + 1;
  109. if (nump1 > search) search = nump1;
  110. for (j = search; j <= neqns; j++)
  111. { node = perm[j];
  112. if (marker[node] >= 0)
  113. { ndeg = deg[node];
  114. if (ndeg <= thresh) goto s500;
  115. if (ndeg < mindeg) mindeg = ndeg;
  116. }
  117. }
  118. goto s200;
  119. /* Node has minimum degree. Find its reachable sets by calling
  120. qmdrch. */
  121. s500: search = j;
  122. nofsub += deg[node];
  123. marker[node] = 1;
  124. qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset, &nhdsze,
  125. nbrhd);
  126. /* Eliminate all nodes indistinguishable from node. They are given
  127. by node, qlink[node], ... . */
  128. nxnode = node;
  129. s600: num++;
  130. np = invp[nxnode];
  131. ip = perm[num];
  132. perm[np] = ip;
  133. invp[ip] = np;
  134. perm[num] = nxnode;
  135. invp[nxnode] = num;
  136. deg[nxnode] = -1;
  137. nxnode = qlink[nxnode];
  138. if (nxnode > 0) goto s600;
  139. if (rchsze > 0)
  140. { /* Update the degrees of the nodes in the reachable set and
  141. identify indistinguishable nodes. */
  142. qmdupd(xadj, adjncy, &rchsze, rchset, deg, qsize, qlink,
  143. marker, &rchset[rchsze+1], &nbrhd[nhdsze+1]);
  144. /* Reset marker value of nodes in reach set. Update threshold
  145. value for cyclic search. Also call qmdqt to form new
  146. quotient graph. */
  147. marker[node] = 0;
  148. for (irch = 1; irch <= rchsze; irch++)
  149. { inode = rchset[irch];
  150. if (marker[inode] >= 0)
  151. { marker[inode] = 0;
  152. ndeg = deg[inode];
  153. if (ndeg < mindeg) mindeg = ndeg;
  154. if (ndeg <= thresh)
  155. { mindeg = thresh;
  156. thresh = ndeg;
  157. search = invp[inode];
  158. }
  159. }
  160. }
  161. if (nhdsze > 0)
  162. qmdqt(&node, xadj, adjncy, marker, &rchsze, rchset, nbrhd);
  163. }
  164. if (num < neqns) goto s300;
  165. return;
  166. # undef neqns
  167. # undef nofsub
  168. }
  169. /***********************************************************************
  170. * NAME
  171. *
  172. * qmdrch - Quotient MD ReaCHable set
  173. *
  174. * SYNOPSIS
  175. *
  176. * #include "glpqmd.h"
  177. * void qmdrch(int *root, int xadj[], int adjncy[], int deg[],
  178. * int marker[], int *rchsze, int rchset[], int *nhdsze,
  179. * int nbrhd[]);
  180. *
  181. * PURPOSE
  182. *
  183. * This subroutine determines the reachable set of a node through a
  184. * given subset. The adjancy structure is assumed to be stored in a
  185. * quotient graph format.
  186. *
  187. * INPUT PARAMETERS
  188. *
  189. * root - the given node not in the subset;
  190. * (xadj, adjncy) -
  191. * the adjancy structure pair;
  192. * deg - the degree vector. deg[i] < 0 means the node belongs to the
  193. * given subset.
  194. *
  195. * OUTPUT PARAMETERS
  196. *
  197. * (rchsze, rchset) -
  198. * the reachable set;
  199. * (nhdsze, nbrhd) -
  200. * the neighborhood set.
  201. *
  202. * UPDATED PARAMETERS
  203. *
  204. * marker - the marker vector for reach and nbrhd sets. > 0 means the
  205. * node is in reach set. < 0 means the node has been merged
  206. * with others in the quotient or it is in nbrhd set.
  207. ***********************************************************************/
  208. void qmdrch(int *_root, int xadj[], int adjncy[], int deg[],
  209. int marker[], int *_rchsze, int rchset[], int *_nhdsze,
  210. int nbrhd[])
  211. { int i, istop, istrt, j, jstop, jstrt, nabor, node;
  212. # define root (*_root)
  213. # define rchsze (*_rchsze)
  214. # define nhdsze (*_nhdsze)
  215. /* Loop through the neighbors of root in the quotient graph. */
  216. nhdsze = 0;
  217. rchsze = 0;
  218. istrt = xadj[root];
  219. istop = xadj[root+1] - 1;
  220. if (istop < istrt) return;
  221. for (i = istrt; i <= istop; i++)
  222. { nabor = adjncy[i];
  223. if (nabor == 0) return;
  224. if (marker[nabor] == 0)
  225. { if (deg[nabor] >= 0)
  226. { /* Include nabor into the reachable set. */
  227. rchsze++;
  228. rchset[rchsze] = nabor;
  229. marker[nabor] = 1;
  230. goto s600;
  231. }
  232. /* nabor has been eliminated. Find nodes reachable from
  233. it. */
  234. marker[nabor] = -1;
  235. nhdsze++;
  236. nbrhd[nhdsze] = nabor;
  237. s300: jstrt = xadj[nabor];
  238. jstop = xadj[nabor+1] - 1;
  239. for (j = jstrt; j <= jstop; j++)
  240. { node = adjncy[j];
  241. nabor = - node;
  242. if (node < 0) goto s300;
  243. if (node == 0) goto s600;
  244. if (marker[node] == 0)
  245. { rchsze++;
  246. rchset[rchsze] = node;
  247. marker[node] = 1;
  248. }
  249. }
  250. }
  251. s600: ;
  252. }
  253. return;
  254. # undef root
  255. # undef rchsze
  256. # undef nhdsze
  257. }
  258. /***********************************************************************
  259. * NAME
  260. *
  261. * qmdqt - Quotient MD Quotient graph Transformation
  262. *
  263. * SYNOPSIS
  264. *
  265. * #include "glpqmd.h"
  266. * void qmdqt(int *root, int xadj[], int adjncy[], int marker[],
  267. * int *rchsze, int rchset[], int nbrhd[]);
  268. *
  269. * PURPOSE
  270. *
  271. * This subroutine performs the quotient graph transformation after a
  272. * node has been eliminated.
  273. *
  274. * INPUT PARAMETERS
  275. *
  276. * root - the node just eliminated. It becomes the representative of
  277. * the new supernode;
  278. * (xadj, adjncy) -
  279. * the adjancy structure;
  280. * (rchsze, rchset) -
  281. * the reachable set of root in the old quotient graph;
  282. * nbrhd - the neighborhood set which will be merged with root to form
  283. * the new supernode;
  284. * marker - the marker vector.
  285. *
  286. * UPDATED PARAMETERS
  287. *
  288. * adjncy - becomes the adjncy of the quotient graph.
  289. ***********************************************************************/
  290. void qmdqt(int *_root, int xadj[], int adjncy[], int marker[],
  291. int *_rchsze, int rchset[], int nbrhd[])
  292. { int inhd, irch, j, jstop, jstrt, link, nabor, node;
  293. # define root (*_root)
  294. # define rchsze (*_rchsze)
  295. irch = 0;
  296. inhd = 0;
  297. node = root;
  298. s100: jstrt = xadj[node];
  299. jstop = xadj[node+1] - 2;
  300. if (jstop >= jstrt)
  301. { /* Place reach nodes into the adjacent list of node. */
  302. for (j = jstrt; j <= jstop; j++)
  303. { irch++;
  304. adjncy[j] = rchset[irch];
  305. if (irch >= rchsze) goto s400;
  306. }
  307. }
  308. /* Link to other space provided by the nbrhd set. */
  309. link = adjncy[jstop+1];
  310. node = - link;
  311. if (link >= 0)
  312. { inhd++;
  313. node = nbrhd[inhd];
  314. adjncy[jstop+1] = - node;
  315. }
  316. goto s100;
  317. /* All reachable nodes have been saved. End the adjacent list.
  318. Add root to the neighborhood list of each node in the reach
  319. set. */
  320. s400: adjncy[j+1] = 0;
  321. for (irch = 1; irch <= rchsze; irch++)
  322. { node = rchset[irch];
  323. if (marker[node] >= 0)
  324. { jstrt = xadj[node];
  325. jstop = xadj[node+1] - 1;
  326. for (j = jstrt; j <= jstop; j++)
  327. { nabor = adjncy[j];
  328. if (marker[nabor] < 0)
  329. { adjncy[j] = root;
  330. goto s600;
  331. }
  332. }
  333. }
  334. s600: ;
  335. }
  336. return;
  337. # undef root
  338. # undef rchsze
  339. }
  340. /***********************************************************************
  341. * NAME
  342. *
  343. * qmdupd - Quotient MD UPDate
  344. *
  345. * SYNOPSIS
  346. *
  347. * #include "glpqmd.h"
  348. * void qmdupd(int xadj[], int adjncy[], int *nlist, int list[],
  349. * int deg[], int qsize[], int qlink[], int marker[], int rchset[],
  350. * int nbrhd[]);
  351. *
  352. * PURPOSE
  353. *
  354. * This routine performs degree update for a set of nodes in the minimum
  355. * degree algorithm.
  356. *
  357. * INPUT PARAMETERS
  358. *
  359. * (xadj, adjncy) -
  360. * the adjancy structure;
  361. * (nlist, list) -
  362. * the list of nodes whose degree has to be updated.
  363. *
  364. * UPDATED PARAMETERS
  365. *
  366. * deg - the degree vector;
  367. * qsize - size of indistinguishable supernodes;
  368. * qlink - linked list for indistinguishable nodes;
  369. * marker - used to mark those nodes in reach/nbrhd sets.
  370. *
  371. * WORKING PARAMETERS
  372. *
  373. * rchset - the reachable set;
  374. * nbrhd - the neighborhood set.
  375. *
  376. * PROGRAM SUBROUTINES
  377. *
  378. * qmdmrg.
  379. ***********************************************************************/
  380. void qmdupd(int xadj[], int adjncy[], int *_nlist, int list[],
  381. int deg[], int qsize[], int qlink[], int marker[], int rchset[],
  382. int nbrhd[])
  383. { int deg0, deg1, il, inhd, inode, irch, j, jstop, jstrt, mark,
  384. nabor, nhdsze, node, rchsze;
  385. # define nlist (*_nlist)
  386. /* Find all eliminated supernodes that are adjacent to some nodes
  387. in the given list. Put them into (nhdsze, nbrhd). deg0 contains
  388. the number of nodes in the list. */
  389. if (nlist <= 0) return;
  390. deg0 = 0;
  391. nhdsze = 0;
  392. for (il = 1; il <= nlist; il++)
  393. { node = list[il];
  394. deg0 += qsize[node];
  395. jstrt = xadj[node];
  396. jstop = xadj[node+1] - 1;
  397. for (j = jstrt; j <= jstop; j++)
  398. { nabor = adjncy[j];
  399. if (marker[nabor] == 0 && deg[nabor] < 0)
  400. { marker[nabor] = -1;
  401. nhdsze++;
  402. nbrhd[nhdsze] = nabor;
  403. }
  404. }
  405. }
  406. /* Merge indistinguishable nodes in the list by calling the
  407. subroutine qmdmrg. */
  408. if (nhdsze > 0)
  409. qmdmrg(xadj, adjncy, deg, qsize, qlink, marker, &deg0, &nhdsze,
  410. nbrhd, rchset, &nbrhd[nhdsze+1]);
  411. /* Find the new degrees of the nodes that have not been merged. */
  412. for (il = 1; il <= nlist; il++)
  413. { node = list[il];
  414. mark = marker[node];
  415. if (mark == 0 || mark == 1)
  416. { marker[node] = 2;
  417. qmdrch(&node, xadj, adjncy, deg, marker, &rchsze, rchset,
  418. &nhdsze, nbrhd);
  419. deg1 = deg0;
  420. if (rchsze > 0)
  421. { for (irch = 1; irch <= rchsze; irch++)
  422. { inode = rchset[irch];
  423. deg1 += qsize[inode];
  424. marker[inode] = 0;
  425. }
  426. }
  427. deg[node] = deg1 - 1;
  428. if (nhdsze > 0)
  429. { for (inhd = 1; inhd <= nhdsze; inhd++)
  430. { inode = nbrhd[inhd];
  431. marker[inode] = 0;
  432. }
  433. }
  434. }
  435. }
  436. return;
  437. # undef nlist
  438. }
  439. /***********************************************************************
  440. * NAME
  441. *
  442. * qmdmrg - Quotient MD MeRGe
  443. *
  444. * SYNOPSIS
  445. *
  446. * #include "qmdmrg.h"
  447. * void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[],
  448. * int qlink[], int marker[], int *deg0, int *nhdsze, int nbrhd[],
  449. * int rchset[], int ovrlp[]);
  450. *
  451. * PURPOSE
  452. *
  453. * This routine merges indistinguishable nodes in the minimum degree
  454. * ordering algorithm. It also computes the new degrees of these new
  455. * supernodes.
  456. *
  457. * INPUT PARAMETERS
  458. *
  459. * (xadj, adjncy) -
  460. * the adjancy structure;
  461. * deg0 - the number of nodes in the given set;
  462. * (nhdsze, nbrhd) -
  463. * the set of eliminated supernodes adjacent to some nodes in
  464. * the set.
  465. *
  466. * UPDATED PARAMETERS
  467. *
  468. * deg - the degree vector;
  469. * qsize - size of indistinguishable nodes;
  470. * qlink - linked list for indistinguishable nodes;
  471. * marker - the given set is given by those nodes with marker value set
  472. * to 1. Those nodes with degree updated will have marker value
  473. * set to 2.
  474. *
  475. * WORKING PARAMETERS
  476. *
  477. * rchset - the reachable set;
  478. * ovrlp - temp vector to store the intersection of two reachable sets.
  479. ***********************************************************************/
  480. void qmdmrg(int xadj[], int adjncy[], int deg[], int qsize[],
  481. int qlink[], int marker[], int *_deg0, int *_nhdsze, int nbrhd[],
  482. int rchset[], int ovrlp[])
  483. { int deg1, head, inhd, iov, irch, j, jstop, jstrt, link, lnode,
  484. mark, mrgsze, nabor, node, novrlp, rchsze, root;
  485. # define deg0 (*_deg0)
  486. # define nhdsze (*_nhdsze)
  487. /* Initialization. */
  488. if (nhdsze <= 0) return;
  489. for (inhd = 1; inhd <= nhdsze; inhd++)
  490. { root = nbrhd[inhd];
  491. marker[root] = 0;
  492. }
  493. /* Loop through each eliminated supernode in the set
  494. (nhdsze, nbrhd). */
  495. for (inhd = 1; inhd <= nhdsze; inhd++)
  496. { root = nbrhd[inhd];
  497. marker[root] = -1;
  498. rchsze = 0;
  499. novrlp = 0;
  500. deg1 = 0;
  501. s200: jstrt = xadj[root];
  502. jstop = xadj[root+1] - 1;
  503. /* Determine the reachable set and its intersection with the
  504. input reachable set. */
  505. for (j = jstrt; j <= jstop; j++)
  506. { nabor = adjncy[j];
  507. root = - nabor;
  508. if (nabor < 0) goto s200;
  509. if (nabor == 0) break;
  510. mark = marker[nabor];
  511. if (mark == 0)
  512. { rchsze++;
  513. rchset[rchsze] = nabor;
  514. deg1 += qsize[nabor];
  515. marker[nabor] = 1;
  516. }
  517. else if (mark == 1)
  518. { novrlp++;
  519. ovrlp[novrlp] = nabor;
  520. marker[nabor] = 2;
  521. }
  522. }
  523. /* From the overlapped set, determine the nodes that can be
  524. merged together. */
  525. head = 0;
  526. mrgsze = 0;
  527. for (iov = 1; iov <= novrlp; iov++)
  528. { node = ovrlp[iov];
  529. jstrt = xadj[node];
  530. jstop = xadj[node+1] - 1;
  531. for (j = jstrt; j <= jstop; j++)
  532. { nabor = adjncy[j];
  533. if (marker[nabor] == 0)
  534. { marker[node] = 1;
  535. goto s1100;
  536. }
  537. }
  538. /* Node belongs to the new merged supernode. Update the
  539. vectors qlink and qsize. */
  540. mrgsze += qsize[node];
  541. marker[node] = -1;
  542. lnode = node;
  543. s900: link = qlink[lnode];
  544. if (link > 0)
  545. { lnode = link;
  546. goto s900;
  547. }
  548. qlink[lnode] = head;
  549. head = node;
  550. s1100: ;
  551. }
  552. if (head > 0)
  553. { qsize[head] = mrgsze;
  554. deg[head] = deg0 + deg1 - 1;
  555. marker[head] = 2;
  556. }
  557. /* Reset marker values. */
  558. root = nbrhd[inhd];
  559. marker[root] = 0;
  560. if (rchsze > 0)
  561. { for (irch = 1; irch <= rchsze; irch++)
  562. { node = rchset[irch];
  563. marker[node] = 0;
  564. }
  565. }
  566. }
  567. return;
  568. # undef deg0
  569. # undef nhdsze
  570. }
  571. /* eof */