rev |
line source |
alpar@9
|
1 /* glpnet02.c (permutations to block triangular form) */
|
alpar@9
|
2
|
alpar@9
|
3 /***********************************************************************
|
alpar@9
|
4 * This code is part of GLPK (GNU Linear Programming Kit).
|
alpar@9
|
5 *
|
alpar@9
|
6 * This code is the result of translation of the Fortran subroutines
|
alpar@9
|
7 * MC13D and MC13E associated with the following paper:
|
alpar@9
|
8 *
|
alpar@9
|
9 * I.S.Duff, J.K.Reid, Algorithm 529: Permutations to block triangular
|
alpar@9
|
10 * form, ACM Trans. on Math. Softw. 4 (1978), 189-192.
|
alpar@9
|
11 *
|
alpar@9
|
12 * Use of ACM Algorithms is subject to the ACM Software Copyright and
|
alpar@9
|
13 * License Agreement. See <http://www.acm.org/publications/policies>.
|
alpar@9
|
14 *
|
alpar@9
|
15 * The translation was made by Andrew Makhorin <mao@gnu.org>.
|
alpar@9
|
16 *
|
alpar@9
|
17 * GLPK is free software: you can redistribute it and/or modify it
|
alpar@9
|
18 * under the terms of the GNU General Public License as published by
|
alpar@9
|
19 * the Free Software Foundation, either version 3 of the License, or
|
alpar@9
|
20 * (at your option) any later version.
|
alpar@9
|
21 *
|
alpar@9
|
22 * GLPK is distributed in the hope that it will be useful, but WITHOUT
|
alpar@9
|
23 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
alpar@9
|
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
alpar@9
|
25 * License for more details.
|
alpar@9
|
26 *
|
alpar@9
|
27 * You should have received a copy of the GNU General Public License
|
alpar@9
|
28 * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
|
alpar@9
|
29 ***********************************************************************/
|
alpar@9
|
30
|
alpar@9
|
31 #include "glpnet.h"
|
alpar@9
|
32
|
alpar@9
|
33 /***********************************************************************
|
alpar@9
|
34 * NAME
|
alpar@9
|
35 *
|
alpar@9
|
36 * mc13d - permutations to block triangular form
|
alpar@9
|
37 *
|
alpar@9
|
38 * SYNOPSIS
|
alpar@9
|
39 *
|
alpar@9
|
40 * #include "glpnet.h"
|
alpar@9
|
41 * int mc13d(int n, const int icn[], const int ip[], const int lenr[],
|
alpar@9
|
42 * int ior[], int ib[], int lowl[], int numb[], int prev[]);
|
alpar@9
|
43 *
|
alpar@9
|
44 * DESCRIPTION
|
alpar@9
|
45 *
|
alpar@9
|
46 * Given the column numbers of the nonzeros in each row of the sparse
|
alpar@9
|
47 * matrix, the routine mc13d finds a symmetric permutation that makes
|
alpar@9
|
48 * the matrix block lower triangular.
|
alpar@9
|
49 *
|
alpar@9
|
50 * INPUT PARAMETERS
|
alpar@9
|
51 *
|
alpar@9
|
52 * n order of the matrix.
|
alpar@9
|
53 *
|
alpar@9
|
54 * icn array containing the column indices of the non-zeros. Those
|
alpar@9
|
55 * belonging to a single row must be contiguous but the ordering
|
alpar@9
|
56 * of column indices within each row is unimportant and wasted
|
alpar@9
|
57 * space between rows is permitted.
|
alpar@9
|
58 *
|
alpar@9
|
59 * ip ip[i], i = 1,2,...,n, is the position in array icn of the
|
alpar@9
|
60 * first column index of a non-zero in row i.
|
alpar@9
|
61 *
|
alpar@9
|
62 * lenr lenr[i], i = 1,2,...,n, is the number of non-zeros in row i.
|
alpar@9
|
63 *
|
alpar@9
|
64 * OUTPUT PARAMETERS
|
alpar@9
|
65 *
|
alpar@9
|
66 * ior ior[i], i = 1,2,...,n, gives the position on the original
|
alpar@9
|
67 * ordering of the row or column which is in position i in the
|
alpar@9
|
68 * permuted form.
|
alpar@9
|
69 *
|
alpar@9
|
70 * ib ib[i], i = 1,2,...,num, is the row number in the permuted
|
alpar@9
|
71 * matrix of the beginning of block i, 1 <= num <= n.
|
alpar@9
|
72 *
|
alpar@9
|
73 * WORKING ARRAYS
|
alpar@9
|
74 *
|
alpar@9
|
75 * arp working array of length [1+n], where arp[0] is not used.
|
alpar@9
|
76 * arp[i] is one less than the number of unsearched edges leaving
|
alpar@9
|
77 * node i. At the end of the algorithm it is set to a permutation
|
alpar@9
|
78 * which puts the matrix in block lower triangular form.
|
alpar@9
|
79 *
|
alpar@9
|
80 * ib working array of length [1+n], where ib[0] is not used.
|
alpar@9
|
81 * ib[i] is the position in the ordering of the start of the ith
|
alpar@9
|
82 * block. ib[n+1-i] holds the node number of the ith node on the
|
alpar@9
|
83 * stack.
|
alpar@9
|
84 *
|
alpar@9
|
85 * lowl working array of length [1+n], where lowl[0] is not used.
|
alpar@9
|
86 * lowl[i] is the smallest stack position of any node to which a
|
alpar@9
|
87 * path from node i has been found. It is set to n+1 when node i
|
alpar@9
|
88 * is removed from the stack.
|
alpar@9
|
89 *
|
alpar@9
|
90 * numb working array of length [1+n], where numb[0] is not used.
|
alpar@9
|
91 * numb[i] is the position of node i in the stack if it is on it,
|
alpar@9
|
92 * is the permuted order of node i for those nodes whose final
|
alpar@9
|
93 * position has been found and is otherwise zero.
|
alpar@9
|
94 *
|
alpar@9
|
95 * prev working array of length [1+n], where prev[0] is not used.
|
alpar@9
|
96 * prev[i] is the node at the end of the path when node i was
|
alpar@9
|
97 * placed on the stack.
|
alpar@9
|
98 *
|
alpar@9
|
99 * RETURNS
|
alpar@9
|
100 *
|
alpar@9
|
101 * The routine mc13d returns num, the number of blocks found. */
|
alpar@9
|
102
|
alpar@9
|
103 int mc13d(int n, const int icn[], const int ip[], const int lenr[],
|
alpar@9
|
104 int ior[], int ib[], int lowl[], int numb[], int prev[])
|
alpar@9
|
105 { int *arp = ior;
|
alpar@9
|
106 int dummy, i, i1, i2, icnt, ii, isn, ist, ist1, iv, iw, j, lcnt,
|
alpar@9
|
107 nnm1, num, stp;
|
alpar@9
|
108 /* icnt is the number of nodes whose positions in final ordering
|
alpar@9
|
109 have been found. */
|
alpar@9
|
110 icnt = 0;
|
alpar@9
|
111 /* num is the number of blocks that have been found. */
|
alpar@9
|
112 num = 0;
|
alpar@9
|
113 nnm1 = n + n - 1;
|
alpar@9
|
114 /* Initialization of arrays. */
|
alpar@9
|
115 for (j = 1; j <= n; j++)
|
alpar@9
|
116 { numb[j] = 0;
|
alpar@9
|
117 arp[j] = lenr[j] - 1;
|
alpar@9
|
118 }
|
alpar@9
|
119 for (isn = 1; isn <= n; isn++)
|
alpar@9
|
120 { /* Look for a starting node. */
|
alpar@9
|
121 if (numb[isn] != 0) continue;
|
alpar@9
|
122 iv = isn;
|
alpar@9
|
123 /* ist is the number of nodes on the stack ... it is the stack
|
alpar@9
|
124 pointer. */
|
alpar@9
|
125 ist = 1;
|
alpar@9
|
126 /* Put node iv at beginning of stack. */
|
alpar@9
|
127 lowl[iv] = numb[iv] = 1;
|
alpar@9
|
128 ib[n] = iv;
|
alpar@9
|
129 /* The body of this loop puts a new node on the stack or
|
alpar@9
|
130 backtracks. */
|
alpar@9
|
131 for (dummy = 1; dummy <= nnm1; dummy++)
|
alpar@9
|
132 { i1 = arp[iv];
|
alpar@9
|
133 /* Have all edges leaving node iv been searched? */
|
alpar@9
|
134 if (i1 >= 0)
|
alpar@9
|
135 { i2 = ip[iv] + lenr[iv] - 1;
|
alpar@9
|
136 i1 = i2 - i1;
|
alpar@9
|
137 /* Look at edges leaving node iv until one enters a new
|
alpar@9
|
138 node or all edges are exhausted. */
|
alpar@9
|
139 for (ii = i1; ii <= i2; ii++)
|
alpar@9
|
140 { iw = icn[ii];
|
alpar@9
|
141 /* Has node iw been on stack already? */
|
alpar@9
|
142 if (numb[iw] == 0) goto L70;
|
alpar@9
|
143 /* Update value of lowl[iv] if necessary. */
|
alpar@9
|
144 if (lowl[iw] < lowl[iv]) lowl[iv] = lowl[iw];
|
alpar@9
|
145 }
|
alpar@9
|
146 /* There are no more edges leaving node iv. */
|
alpar@9
|
147 arp[iv] = -1;
|
alpar@9
|
148 }
|
alpar@9
|
149 /* Is node iv the root of a block? */
|
alpar@9
|
150 if (lowl[iv] < numb[iv]) goto L60;
|
alpar@9
|
151 /* Order nodes in a block. */
|
alpar@9
|
152 num++;
|
alpar@9
|
153 ist1 = n + 1 - ist;
|
alpar@9
|
154 lcnt = icnt + 1;
|
alpar@9
|
155 /* Peel block off the top of the stack starting at the top
|
alpar@9
|
156 and working down to the root of the block. */
|
alpar@9
|
157 for (stp = ist1; stp <= n; stp++)
|
alpar@9
|
158 { iw = ib[stp];
|
alpar@9
|
159 lowl[iw] = n + 1;
|
alpar@9
|
160 numb[iw] = ++icnt;
|
alpar@9
|
161 if (iw == iv) break;
|
alpar@9
|
162 }
|
alpar@9
|
163 ist = n - stp;
|
alpar@9
|
164 ib[num] = lcnt;
|
alpar@9
|
165 /* Are there any nodes left on the stack? */
|
alpar@9
|
166 if (ist != 0) goto L60;
|
alpar@9
|
167 /* Have all the nodes been ordered? */
|
alpar@9
|
168 if (icnt < n) break;
|
alpar@9
|
169 goto L100;
|
alpar@9
|
170 L60: /* Backtrack to previous node on path. */
|
alpar@9
|
171 iw = iv;
|
alpar@9
|
172 iv = prev[iv];
|
alpar@9
|
173 /* Update value of lowl[iv] if necessary. */
|
alpar@9
|
174 if (lowl[iw] < lowl[iv]) lowl[iv] = lowl[iw];
|
alpar@9
|
175 continue;
|
alpar@9
|
176 L70: /* Put new node on the stack. */
|
alpar@9
|
177 arp[iv] = i2 - ii - 1;
|
alpar@9
|
178 prev[iw] = iv;
|
alpar@9
|
179 iv = iw;
|
alpar@9
|
180 lowl[iv] = numb[iv] = ++ist;
|
alpar@9
|
181 ib[n+1-ist] = iv;
|
alpar@9
|
182 }
|
alpar@9
|
183 }
|
alpar@9
|
184 L100: /* Put permutation in the required form. */
|
alpar@9
|
185 for (i = 1; i <= n; i++)
|
alpar@9
|
186 arp[numb[i]] = i;
|
alpar@9
|
187 return num;
|
alpar@9
|
188 }
|
alpar@9
|
189
|
alpar@9
|
190 /**********************************************************************/
|
alpar@9
|
191
|
alpar@9
|
192 #if 0
|
alpar@9
|
193 #include "glplib.h"
|
alpar@9
|
194
|
alpar@9
|
195 void test(int n, int ipp);
|
alpar@9
|
196
|
alpar@9
|
197 int main(void)
|
alpar@9
|
198 { /* test program for routine mc13d */
|
alpar@9
|
199 test( 1, 0);
|
alpar@9
|
200 test( 2, 1);
|
alpar@9
|
201 test( 2, 2);
|
alpar@9
|
202 test( 3, 3);
|
alpar@9
|
203 test( 4, 4);
|
alpar@9
|
204 test( 5, 10);
|
alpar@9
|
205 test(10, 10);
|
alpar@9
|
206 test(10, 20);
|
alpar@9
|
207 test(20, 20);
|
alpar@9
|
208 test(20, 50);
|
alpar@9
|
209 test(50, 50);
|
alpar@9
|
210 test(50, 200);
|
alpar@9
|
211 return 0;
|
alpar@9
|
212 }
|
alpar@9
|
213
|
alpar@9
|
214 void fa01bs(int max, int *nrand);
|
alpar@9
|
215
|
alpar@9
|
216 void setup(int n, char a[1+50][1+50], int ip[], int icn[], int lenr[]);
|
alpar@9
|
217
|
alpar@9
|
218 void test(int n, int ipp)
|
alpar@9
|
219 { int ip[1+50], icn[1+1000], ior[1+50], ib[1+51], iw[1+150],
|
alpar@9
|
220 lenr[1+50];
|
alpar@9
|
221 char a[1+50][1+50], hold[1+100];
|
alpar@9
|
222 int i, ii, iblock, ij, index, j, jblock, jj, k9, num;
|
alpar@9
|
223 xprintf("\n\n\nMatrix is of order %d and has %d off-diagonal non-"
|
alpar@9
|
224 "zeros\n", n, ipp);
|
alpar@9
|
225 for (j = 1; j <= n; j++)
|
alpar@9
|
226 { for (i = 1; i <= n; i++)
|
alpar@9
|
227 a[i][j] = 0;
|
alpar@9
|
228 a[j][j] = 1;
|
alpar@9
|
229 }
|
alpar@9
|
230 for (k9 = 1; k9 <= ipp; k9++)
|
alpar@9
|
231 { /* these statements should be replaced by calls to your
|
alpar@9
|
232 favorite random number generator to place two pseudo-random
|
alpar@9
|
233 numbers between 1 and n in the variables i and j */
|
alpar@9
|
234 for (;;)
|
alpar@9
|
235 { fa01bs(n, &i);
|
alpar@9
|
236 fa01bs(n, &j);
|
alpar@9
|
237 if (!a[i][j]) break;
|
alpar@9
|
238 }
|
alpar@9
|
239 a[i][j] = 1;
|
alpar@9
|
240 }
|
alpar@9
|
241 /* setup converts matrix a[i,j] to required sparsity-oriented
|
alpar@9
|
242 storage format */
|
alpar@9
|
243 setup(n, a, ip, icn, lenr);
|
alpar@9
|
244 num = mc13d(n, icn, ip, lenr, ior, ib, &iw[0], &iw[n], &iw[n+n]);
|
alpar@9
|
245 /* output reordered matrix with blocking to improve clarity */
|
alpar@9
|
246 xprintf("\nThe reordered matrix which has %d block%s is of the fo"
|
alpar@9
|
247 "rm\n", num, num == 1 ? "" : "s");
|
alpar@9
|
248 ib[num+1] = n + 1;
|
alpar@9
|
249 index = 100;
|
alpar@9
|
250 iblock = 1;
|
alpar@9
|
251 for (i = 1; i <= n; i++)
|
alpar@9
|
252 { for (ij = 1; ij <= index; ij++)
|
alpar@9
|
253 hold[ij] = ' ';
|
alpar@9
|
254 if (i == ib[iblock])
|
alpar@9
|
255 { xprintf("\n");
|
alpar@9
|
256 iblock++;
|
alpar@9
|
257 }
|
alpar@9
|
258 jblock = 1;
|
alpar@9
|
259 index = 0;
|
alpar@9
|
260 for (j = 1; j <= n; j++)
|
alpar@9
|
261 { if (j == ib[jblock])
|
alpar@9
|
262 { hold[++index] = ' ';
|
alpar@9
|
263 jblock++;
|
alpar@9
|
264 }
|
alpar@9
|
265 ii = ior[i];
|
alpar@9
|
266 jj = ior[j];
|
alpar@9
|
267 hold[++index] = (char)(a[ii][jj] ? 'X' : '0');
|
alpar@9
|
268 }
|
alpar@9
|
269 xprintf("%.*s\n", index, &hold[1]);
|
alpar@9
|
270 }
|
alpar@9
|
271 xprintf("\nThe starting point for each block is given by\n");
|
alpar@9
|
272 for (i = 1; i <= num; i++)
|
alpar@9
|
273 { if ((i - 1) % 12 == 0) xprintf("\n");
|
alpar@9
|
274 xprintf(" %4d", ib[i]);
|
alpar@9
|
275 }
|
alpar@9
|
276 xprintf("\n");
|
alpar@9
|
277 return;
|
alpar@9
|
278 }
|
alpar@9
|
279
|
alpar@9
|
280 void setup(int n, char a[1+50][1+50], int ip[], int icn[], int lenr[])
|
alpar@9
|
281 { int i, j, ind;
|
alpar@9
|
282 for (i = 1; i <= n; i++)
|
alpar@9
|
283 lenr[i] = 0;
|
alpar@9
|
284 ind = 1;
|
alpar@9
|
285 for (i = 1; i <= n; i++)
|
alpar@9
|
286 { ip[i] = ind;
|
alpar@9
|
287 for (j = 1; j <= n; j++)
|
alpar@9
|
288 { if (a[i][j])
|
alpar@9
|
289 { lenr[i]++;
|
alpar@9
|
290 icn[ind++] = j;
|
alpar@9
|
291 }
|
alpar@9
|
292 }
|
alpar@9
|
293 }
|
alpar@9
|
294 return;
|
alpar@9
|
295 }
|
alpar@9
|
296
|
alpar@9
|
297 double g = 1431655765.0;
|
alpar@9
|
298
|
alpar@9
|
299 double fa01as(int i)
|
alpar@9
|
300 { /* random number generator */
|
alpar@9
|
301 g = fmod(g * 9228907.0, 4294967296.0);
|
alpar@9
|
302 if (i >= 0)
|
alpar@9
|
303 return g / 4294967296.0;
|
alpar@9
|
304 else
|
alpar@9
|
305 return 2.0 * g / 4294967296.0 - 1.0;
|
alpar@9
|
306 }
|
alpar@9
|
307
|
alpar@9
|
308 void fa01bs(int max, int *nrand)
|
alpar@9
|
309 { *nrand = (int)(fa01as(1) * (double)max) + 1;
|
alpar@9
|
310 return;
|
alpar@9
|
311 }
|
alpar@9
|
312 #endif
|
alpar@9
|
313
|
alpar@9
|
314 /* eof */
|