rev |
line source |
alpar@9
|
1 /* glpnet01.c (permutations for zero-free diagonal) */
|
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 * MC21A and MC21B associated with the following paper:
|
alpar@9
|
8 *
|
alpar@9
|
9 * I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM
|
alpar@9
|
10 * Trans. on Math. Softw. 7 (1981), 387-390.
|
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 * mc21a - permutations for zero-free diagonal
|
alpar@9
|
37 *
|
alpar@9
|
38 * SYNOPSIS
|
alpar@9
|
39 *
|
alpar@9
|
40 * #include "glpnet.h"
|
alpar@9
|
41 * int mc21a(int n, const int icn[], const int ip[], const int lenr[],
|
alpar@9
|
42 * int iperm[], int pr[], int arp[], int cv[], int out[]);
|
alpar@9
|
43 *
|
alpar@9
|
44 * DESCRIPTION
|
alpar@9
|
45 *
|
alpar@9
|
46 * Given the pattern of nonzeros of a sparse matrix, the routine mc21a
|
alpar@9
|
47 * attempts to find a permutation of its rows that makes the matrix have
|
alpar@9
|
48 * no zeros on its diagonal.
|
alpar@9
|
49 *
|
alpar@9
|
50 * INPUT PARAMETERS
|
alpar@9
|
51 *
|
alpar@9
|
52 * n order of 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 PARAMETER
|
alpar@9
|
65 *
|
alpar@9
|
66 * iperm contains permutation to make diagonal have the smallest
|
alpar@9
|
67 * number of zeros on it. Elements (iperm[i], i), i = 1,2,...,n,
|
alpar@9
|
68 * are non-zero at the end of the algorithm unless the matrix is
|
alpar@9
|
69 * structurally singular. In this case, (iperm[i], i) will be
|
alpar@9
|
70 * zero for n - numnz entries.
|
alpar@9
|
71 *
|
alpar@9
|
72 * WORKING ARRAYS
|
alpar@9
|
73 *
|
alpar@9
|
74 * pr working array of length [1+n], where pr[0] is not used.
|
alpar@9
|
75 * pr[i] is the previous row to i in the depth first search.
|
alpar@9
|
76 *
|
alpar@9
|
77 * arp working array of length [1+n], where arp[0] is not used.
|
alpar@9
|
78 * arp[i] is one less than the number of non-zeros in row i which
|
alpar@9
|
79 * have not been scanned when looking for a cheap assignment.
|
alpar@9
|
80 *
|
alpar@9
|
81 * cv working array of length [1+n], where cv[0] is not used.
|
alpar@9
|
82 * cv[i] is the most recent row extension at which column i was
|
alpar@9
|
83 * visited.
|
alpar@9
|
84 *
|
alpar@9
|
85 * out working array of length [1+n], where out[0] is not used.
|
alpar@9
|
86 * out[i] is one less than the number of non-zeros in row i
|
alpar@9
|
87 * which have not been scanned during one pass through the main
|
alpar@9
|
88 * loop.
|
alpar@9
|
89 *
|
alpar@9
|
90 * RETURNS
|
alpar@9
|
91 *
|
alpar@9
|
92 * The routine mc21a returns numnz, the number of non-zeros on diagonal
|
alpar@9
|
93 * of permuted matrix. */
|
alpar@9
|
94
|
alpar@9
|
95 int mc21a(int n, const int icn[], const int ip[], const int lenr[],
|
alpar@9
|
96 int iperm[], int pr[], int arp[], int cv[], int out[])
|
alpar@9
|
97 { int i, ii, in1, in2, j, j1, jord, k, kk, numnz;
|
alpar@9
|
98 /* Initialization of arrays. */
|
alpar@9
|
99 for (i = 1; i <= n; i++)
|
alpar@9
|
100 { arp[i] = lenr[i] - 1;
|
alpar@9
|
101 cv[i] = iperm[i] = 0;
|
alpar@9
|
102 }
|
alpar@9
|
103 numnz = 0;
|
alpar@9
|
104 /* Main loop. */
|
alpar@9
|
105 /* Each pass round this loop either results in a new assignment
|
alpar@9
|
106 or gives a row with no assignment. */
|
alpar@9
|
107 for (jord = 1; jord <= n; jord++)
|
alpar@9
|
108 { j = jord;
|
alpar@9
|
109 pr[j] = -1;
|
alpar@9
|
110 for (k = 1; k <= jord; k++)
|
alpar@9
|
111 { /* Look for a cheap assignment. */
|
alpar@9
|
112 in1 = arp[j];
|
alpar@9
|
113 if (in1 >= 0)
|
alpar@9
|
114 { in2 = ip[j] + lenr[j] - 1;
|
alpar@9
|
115 in1 = in2 - in1;
|
alpar@9
|
116 for (ii = in1; ii <= in2; ii++)
|
alpar@9
|
117 { i = icn[ii];
|
alpar@9
|
118 if (iperm[i] == 0) goto L110;
|
alpar@9
|
119 }
|
alpar@9
|
120 /* No cheap assignment in row. */
|
alpar@9
|
121 arp[j] = -1;
|
alpar@9
|
122 }
|
alpar@9
|
123 /* Begin looking for assignment chain starting with row j.*/
|
alpar@9
|
124 out[j] = lenr[j] - 1;
|
alpar@9
|
125 /* Inner loop. Extends chain by one or backtracks. */
|
alpar@9
|
126 for (kk = 1; kk <= jord; kk++)
|
alpar@9
|
127 { in1 = out[j];
|
alpar@9
|
128 if (in1 >= 0)
|
alpar@9
|
129 { in2 = ip[j] + lenr[j] - 1;
|
alpar@9
|
130 in1 = in2 - in1;
|
alpar@9
|
131 /* Forward scan. */
|
alpar@9
|
132 for (ii = in1; ii <= in2; ii++)
|
alpar@9
|
133 { i = icn[ii];
|
alpar@9
|
134 if (cv[i] != jord)
|
alpar@9
|
135 { /* Column i has not yet been accessed during
|
alpar@9
|
136 this pass. */
|
alpar@9
|
137 j1 = j;
|
alpar@9
|
138 j = iperm[i];
|
alpar@9
|
139 cv[i] = jord;
|
alpar@9
|
140 pr[j] = j1;
|
alpar@9
|
141 out[j1] = in2 - ii - 1;
|
alpar@9
|
142 goto L100;
|
alpar@9
|
143 }
|
alpar@9
|
144 }
|
alpar@9
|
145 }
|
alpar@9
|
146 /* Backtracking step. */
|
alpar@9
|
147 j = pr[j];
|
alpar@9
|
148 if (j == -1) goto L130;
|
alpar@9
|
149 }
|
alpar@9
|
150 L100: ;
|
alpar@9
|
151 }
|
alpar@9
|
152 L110: /* New assignment is made. */
|
alpar@9
|
153 iperm[i] = j;
|
alpar@9
|
154 arp[j] = in2 - ii - 1;
|
alpar@9
|
155 numnz++;
|
alpar@9
|
156 for (k = 1; k <= jord; k++)
|
alpar@9
|
157 { j = pr[j];
|
alpar@9
|
158 if (j == -1) break;
|
alpar@9
|
159 ii = ip[j] + lenr[j] - out[j] - 2;
|
alpar@9
|
160 i = icn[ii];
|
alpar@9
|
161 iperm[i] = j;
|
alpar@9
|
162 }
|
alpar@9
|
163 L130: ;
|
alpar@9
|
164 }
|
alpar@9
|
165 /* If matrix is structurally singular, we now complete the
|
alpar@9
|
166 permutation iperm. */
|
alpar@9
|
167 if (numnz < n)
|
alpar@9
|
168 { for (i = 1; i <= n; i++)
|
alpar@9
|
169 arp[i] = 0;
|
alpar@9
|
170 k = 0;
|
alpar@9
|
171 for (i = 1; i <= n; i++)
|
alpar@9
|
172 { if (iperm[i] == 0)
|
alpar@9
|
173 out[++k] = i;
|
alpar@9
|
174 else
|
alpar@9
|
175 arp[iperm[i]] = i;
|
alpar@9
|
176 }
|
alpar@9
|
177 k = 0;
|
alpar@9
|
178 for (i = 1; i <= n; i++)
|
alpar@9
|
179 { if (arp[i] == 0)
|
alpar@9
|
180 iperm[out[++k]] = i;
|
alpar@9
|
181 }
|
alpar@9
|
182 }
|
alpar@9
|
183 return numnz;
|
alpar@9
|
184 }
|
alpar@9
|
185
|
alpar@9
|
186 /**********************************************************************/
|
alpar@9
|
187
|
alpar@9
|
188 #if 0
|
alpar@9
|
189 #include "glplib.h"
|
alpar@9
|
190
|
alpar@9
|
191 int sing;
|
alpar@9
|
192
|
alpar@9
|
193 void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum,
|
alpar@9
|
194 int iw[]);
|
alpar@9
|
195
|
alpar@9
|
196 void fa01bs(int max, int *nrand);
|
alpar@9
|
197
|
alpar@9
|
198 int main(void)
|
alpar@9
|
199 { /* test program for the routine mc21a */
|
alpar@9
|
200 /* these runs on random matrices cause all possible statements in
|
alpar@9
|
201 mc21a to be executed */
|
alpar@9
|
202 int i, iold, j, j1, j2, jj, knum, l, licn, n, nov4, num, numnz;
|
alpar@9
|
203 int ip[1+21], icn[1+1000], iperm[1+20], lenr[1+20], iw1[1+80];
|
alpar@9
|
204 licn = 1000;
|
alpar@9
|
205 /* run on random matrices of orders 1 through 20 */
|
alpar@9
|
206 for (n = 1; n <= 20; n++)
|
alpar@9
|
207 { nov4 = n / 4;
|
alpar@9
|
208 if (nov4 < 1) nov4 = 1;
|
alpar@9
|
209 L10: fa01bs(nov4, &l);
|
alpar@9
|
210 knum = l * n;
|
alpar@9
|
211 /* knum is requested number of non-zeros in random matrix */
|
alpar@9
|
212 if (knum > licn) goto L10;
|
alpar@9
|
213 /* if sing is false, matrix is guaranteed structurally
|
alpar@9
|
214 non-singular */
|
alpar@9
|
215 sing = ((n / 2) * 2 == n);
|
alpar@9
|
216 /* call to subroutine to generate random matrix */
|
alpar@9
|
217 ranmat(n, n, icn, ip, n+1, &knum, iw1);
|
alpar@9
|
218 /* knum is now actual number of non-zeros in random matrix */
|
alpar@9
|
219 if (knum > licn) goto L10;
|
alpar@9
|
220 xprintf("n = %2d; nz = %4d; sing = %d\n", n, knum, sing);
|
alpar@9
|
221 /* set up array of row lengths */
|
alpar@9
|
222 for (i = 1; i <= n; i++)
|
alpar@9
|
223 lenr[i] = ip[i+1] - ip[i];
|
alpar@9
|
224 /* call to mc21a */
|
alpar@9
|
225 numnz = mc21a(n, icn, ip, lenr, iperm, &iw1[0], &iw1[n],
|
alpar@9
|
226 &iw1[n+n], &iw1[n+n+n]);
|
alpar@9
|
227 /* testing to see if there are numnz non-zeros on the diagonal
|
alpar@9
|
228 of the permuted matrix. */
|
alpar@9
|
229 num = 0;
|
alpar@9
|
230 for (i = 1; i <= n; i++)
|
alpar@9
|
231 { iold = iperm[i];
|
alpar@9
|
232 j1 = ip[iold];
|
alpar@9
|
233 j2 = j1 + lenr[iold] - 1;
|
alpar@9
|
234 if (j2 < j1) continue;
|
alpar@9
|
235 for (jj = j1; jj <= j2; jj++)
|
alpar@9
|
236 { j = icn[jj];
|
alpar@9
|
237 if (j == i)
|
alpar@9
|
238 { num++;
|
alpar@9
|
239 break;
|
alpar@9
|
240 }
|
alpar@9
|
241 }
|
alpar@9
|
242 }
|
alpar@9
|
243 if (num != numnz)
|
alpar@9
|
244 xprintf("Failure in mc21a, numnz = %d instead of %d\n",
|
alpar@9
|
245 numnz, num);
|
alpar@9
|
246 }
|
alpar@9
|
247 return 0;
|
alpar@9
|
248 }
|
alpar@9
|
249
|
alpar@9
|
250 void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum,
|
alpar@9
|
251 int iw[])
|
alpar@9
|
252 { /* subroutine to generate random matrix */
|
alpar@9
|
253 int i, ii, inum, j, lrow, matnum;
|
alpar@9
|
254 inum = (*knum / n) * 2;
|
alpar@9
|
255 if (inum > n-1) inum = n-1;
|
alpar@9
|
256 matnum = 1;
|
alpar@9
|
257 /* each pass through this loop generates a row of the matrix */
|
alpar@9
|
258 for (j = 1; j <= m; j++)
|
alpar@9
|
259 { iptr[j] = matnum;
|
alpar@9
|
260 if (!(sing || j > n))
|
alpar@9
|
261 icn[matnum++] = j;
|
alpar@9
|
262 if (n == 1) continue;
|
alpar@9
|
263 for (i = 1; i <= n; i++) iw[i] = 0;
|
alpar@9
|
264 if (!sing) iw[j] = 1;
|
alpar@9
|
265 fa01bs(inum, &lrow);
|
alpar@9
|
266 lrow--;
|
alpar@9
|
267 if (lrow == 0) continue;
|
alpar@9
|
268 /* lrow off-diagonal non-zeros in row j of the matrix */
|
alpar@9
|
269 for (ii = 1; ii <= lrow; ii++)
|
alpar@9
|
270 { for (;;)
|
alpar@9
|
271 { fa01bs(n, &i);
|
alpar@9
|
272 if (iw[i] != 1) break;
|
alpar@9
|
273 }
|
alpar@9
|
274 iw[i] = 1;
|
alpar@9
|
275 icn[matnum++] = i;
|
alpar@9
|
276 }
|
alpar@9
|
277 }
|
alpar@9
|
278 for (i = m+1; i <= nnnp1; i++)
|
alpar@9
|
279 iptr[i] = matnum;
|
alpar@9
|
280 *knum = matnum - 1;
|
alpar@9
|
281 return;
|
alpar@9
|
282 }
|
alpar@9
|
283
|
alpar@9
|
284 double g = 1431655765.0;
|
alpar@9
|
285
|
alpar@9
|
286 double fa01as(int i)
|
alpar@9
|
287 { /* random number generator */
|
alpar@9
|
288 g = fmod(g * 9228907.0, 4294967296.0);
|
alpar@9
|
289 if (i >= 0)
|
alpar@9
|
290 return g / 4294967296.0;
|
alpar@9
|
291 else
|
alpar@9
|
292 return 2.0 * g / 4294967296.0 - 1.0;
|
alpar@9
|
293 }
|
alpar@9
|
294
|
alpar@9
|
295 void fa01bs(int max, int *nrand)
|
alpar@9
|
296 { *nrand = (int)(fa01as(1) * (double)max) + 1;
|
alpar@9
|
297 return;
|
alpar@9
|
298 }
|
alpar@9
|
299 #endif
|
alpar@9
|
300
|
alpar@9
|
301 /* eof */
|