Amesos Package Browser (Single Doxygen Collection)  Development
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
amesos_btf_l_strongcomp.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /* === BTF_STRONGCOMP ======================================================= */
3 /* ========================================================================== */
4 
5 /* Finds the strongly connected components of a graph, or equivalently, permutes
6  * the matrix into upper block triangular form. See btf.h for more details.
7  * Input matrix and Q are not checked on input.
8  *
9  * Copyright (c) 2004-2007. Tim Davis, University of Florida,
10  * with support from Sandia National Laboratories. All Rights Reserved.
11  */
12 
13 /* This file should make the long int version of BTF */
14 #define DLONG 1
15 
16 #include "amesos_btf_decl.h"
17 #include "amesos_btf_internal.h"
18 
19 #define UNVISITED (-2) /* Flag [j] = UNVISITED if node j not visited yet */
20 #define UNASSIGNED (-1) /* Flag [j] = UNASSIGNED if node j has been visited,
21  * but not yet assigned to a strongly-connected
22  * component (aka block). Flag [j] = k (k in the
23  * range 0 to nblocks-1) if node j has been visited
24  * (and completed, with its postwork done) and
25  * assigned to component k. */
26 
27 /* This file contains two versions of the depth-first-search, a recursive one
28  * and a non-recursive one. By default, the non-recursive one is used. */
29 
30 #ifndef RECURSIVE
31 
32 /* ========================================================================== */
33 /* === dfs: non-recursive version (default) ================================= */
34 /* ========================================================================== */
35 
36 /* Perform a depth-first-search of a graph, stored in an adjacency-list form.
37  * The row indices of column j (equivalently, the out-adjacency list of node j)
38  * are stored in Ai [Ap[j] ... Ap[j+1]-1]. Self-edge (diagonal entries) are
39  * ignored. Ap[0] must be zero, and thus nz = Ap[n] is the number of entries
40  * in the matrix (or edges in the graph). The row indices in each column need
41  * not be in any particular order. If an input column permutation is given,
42  * node j (in the permuted matrix A*Q) is located in
43  * Ai [Ap[Q[j]] ... Ap[Q[j]+1]-1]. This Q can be the same as the Match array
44  * output from the maxtrans routine, for a square matrix that is structurally
45  * full rank.
46  *
47  * The algorithm is from the paper by Robert E. Tarjan, "Depth-first search and
48  * linear graph algorithms," SIAM Journal on Computing, vol. 1, no. 2,
49  * pp. 146-160, 1972. The time taken by strongcomp is O(nnz(A)).
50  *
51  * See also MC13A/B in the Harwell subroutine library (Iain S. Duff and John
52  * K. Reid, "Algorithm 529: permutations to block triangular form," ACM Trans.
53  * on Mathematical Software, vol. 4, no. 2, pp. 189-192, 1978, and "An
54  * implementation of Tarjan's algorithm for the block triangular form of a
55  * matrix," same journal, pp. 137-147. This code is implements the same
56  * algorithm as MC13A/B, except that the data structures are very different.
57  * Also, unlike MC13A/B, the output permutation preserves the natural ordering
58  * within each block.
59  */
60 
61 static void amesos_dfs
62 (
63  /* inputs, not modified on output: */
64  Int j, /* start the DFS at node j */
65  Int Ap [ ], /* size n+1, column pointers for the matrix A */
66  Int Ai [ ], /* row indices, size nz = Ap [n] */
67  Int Q [ ], /* input column permutation */
68 
69  /* inputs, modified on output (each array is of size n): */
70  Int Time [ ], /* Time [j] = "time" that node j was first visited */
71  Int Flag [ ], /* Flag [j]: see above */
72  Int Low [ ], /* Low [j]: see definition below */
73  Int *p_nblocks, /* number of blocks (aka strongly-connected-comp.)*/
74  Int *p_timestamp, /* current "time" */
75 
76  /* workspace, not defined on input or output: */
77  Int Cstack [ ], /* size n, output stack to hold nodes of components */
78  Int Jstack [ ], /* size n, stack for the variable j */
79  Int Pstack [ ] /* size n, stack for the variable p */
80 )
81 {
82  /* ---------------------------------------------------------------------- */
83  /* local variables, and initializations */
84  /* ---------------------------------------------------------------------- */
85 
86  /* local variables, but "global" to all DFS levels: */
87  Int chead ; /* top of Cstack */
88  Int jhead ; /* top of Jstack and Pstack */
89 
90  /* variables that are purely local to any one DFS level: */
91  Int i ; /* edge (j,i) considered; i can be next node to traverse */
92  Int parent ; /* parent of node j in the DFS tree */
93  Int pend ; /* one past the end of the adjacency list for node j */
94  Int jj ; /* column j of A*Q is column jj of the input matrix A */
95 
96  /* variables that need to be pushed then popped from the stack: */
97  Int p ; /* current index into the adj. list for node j */
98  /* the variables j and p are stacked in Jstack and Pstack */
99 
100  /* local copies of variables in the calling routine */
101  Int nblocks = *p_nblocks ;
102  Int timestamp = *p_timestamp ;
103 
104  /* ---------------------------------------------------------------------- */
105  /* start a DFS at node j (same as the recursive call dfs (EMPTY, j)) */
106  /* ---------------------------------------------------------------------- */
107 
108  chead = 0 ; /* component stack is empty */
109  jhead = 0 ; /* Jstack and Pstack are empty */
110  Jstack [0] = j ; /* put the first node j on the Jstack */
111  ASSERT (Flag [j] == UNVISITED) ;
112 
113  while (jhead >= 0)
114  {
115  j = Jstack [jhead] ; /* grab the node j from the top of Jstack */
116 
117  /* determine which column jj of the A is column j of A*Q */
118  jj = (Q == (Int *) NULL) ? (j) : (BTF_UNFLIP (Q [j])) ;
119  pend = Ap [jj+1] ; /* j's row index list ends at Ai [pend-1] */
120 
121  if (Flag [j] == UNVISITED)
122  {
123 
124  /* -------------------------------------------------------------- */
125  /* prework at node j */
126  /* -------------------------------------------------------------- */
127 
128  /* node j is being visited for the first time */
129  Cstack [++chead] = j ; /* push j onto the stack */
130  timestamp++ ; /* get a timestamp */
131  Time [j] = timestamp ; /* give the timestamp to node j */
132  Low [j] = timestamp ;
133  Flag [j] = UNASSIGNED ; /* flag node j as visited */
134 
135  /* -------------------------------------------------------------- */
136  /* set Pstack [jhead] to the first entry in column j to scan */
137  /* -------------------------------------------------------------- */
138 
139  Pstack [jhead] = Ap [jj] ;
140  }
141 
142  /* ------------------------------------------------------------------ */
143  /* DFS rooted at node j (start it, or continue where left off) */
144  /* ------------------------------------------------------------------ */
145 
146  for (p = Pstack [jhead] ; p < pend ; p++)
147  {
148  i = Ai [p] ; /* examine the edge from node j to node i */
149  if (Flag [i] == UNVISITED)
150  {
151  /* Node i has not been visited - start a DFS at node i.
152  * Keep track of where we left off in the scan of adjacency list
153  * of node j so we can restart j where we left off. */
154  Pstack [jhead] = p + 1 ;
155  /* Push i onto the stack and immediately break
156  * so we can recurse on node i. */
157  Jstack [++jhead] = i ;
158  ASSERT (Time [i] == EMPTY) ;
159  ASSERT (Low [i] == EMPTY) ;
160  /* break here to do what the recursive call dfs (j,i) does */
161  break ;
162  }
163  else if (Flag [i] == UNASSIGNED)
164  {
165  /* Node i has been visited, but still unassigned to a block
166  * this is a back or cross edge if Time [i] < Time [j].
167  * Note that i might equal j, in which case this code does
168  * nothing. */
169  ASSERT (Time [i] > 0) ;
170  ASSERT (Low [i] > 0) ;
171  Low [j] = MIN (Low [j], Time [i]) ;
172  }
173  }
174 
175  if (p == pend)
176  {
177  /* If all adjacent nodes of j are already visited, pop j from
178  * Jstack and do the post work for node j. This also pops p
179  * from the Pstack. */
180  jhead-- ;
181 
182  /* -------------------------------------------------------------- */
183  /* postwork at node j */
184  /* -------------------------------------------------------------- */
185 
186  /* determine if node j is the head of a component */
187  if (Low [j] == Time [j])
188  {
189  /* pop all nodes in this SCC from Cstack */
190  while (TRUE)
191  {
192  ASSERT (chead >= 0) ; /* stack not empty (j in it) */
193  i = Cstack [chead--] ; /* pop a node from the Cstack */
194  ASSERT (i >= 0) ;
195  ASSERT (Flag [i] == UNASSIGNED) ;
196  Flag [i] = nblocks ; /* assign i to current block */
197  if (i == j) break ; /* current block ends at j */
198  }
199  nblocks++ ; /* one more block has been found */
200  }
201  /* update Low [parent], if the parent exists */
202  if (jhead >= 0)
203  {
204  parent = Jstack [jhead] ;
205  Low [parent] = MIN (Low [parent], Low [j]) ;
206  }
207  }
208  }
209 
210  /* ---------------------------------------------------------------------- */
211  /* cleanup: update timestamp and nblocks */
212  /* ---------------------------------------------------------------------- */
213 
214  *p_timestamp = timestamp ;
215  *p_nblocks = nblocks ;
216 }
217 
218 #else
219 
220 /* ========================================================================== */
221 /* === dfs: recursive version (only for illustration) ======================= */
222 /* ========================================================================== */
223 
224 /* The following is a recursive version of dfs, which computes identical results
225  * as the non-recursive dfs. It is included here because it is easier to read.
226  * Compare the comments in the code below with the identical comments in the
227  * non-recursive code above, and that will help you see the correlation between
228  * the two routines.
229  *
230  * This routine can cause stack overflow, and is thus not recommended for heavy
231  * usage, particularly for large matrices. To help in delaying stack overflow,
232  * global variables are used, reducing the amount of information each call to
233  * dfs places on the call/return stack (the integers i, j, p, parent, and the
234  * return address). Note that this means the recursive code is not thread-safe.
235  * To try this version, compile the code with -DRECURSIVE or include the
236  * following line at the top of this file:
237 
238 #define RECURSIVE
239 
240  */
241 
242 static Int /* for recursive illustration only, not for production use */
243  chead, timestamp, nblocks, n, *Ap, *Ai, *Flag, *Cstack, *Time, *Low,
244  *P, *R, *Q ;
245 
246 static void amesos_dfs
247 (
248  Int parent, /* came from parent node */
249  Int j /* at node j in the DFS */
250 )
251 {
252  Int p ; /* current index into the adj. list for node j */
253  Int i ; /* edge (j,i) considered; i can be next node to traverse */
254  Int jj ; /* column j of A*Q is column jj of the input matrix A */
255 
256  /* ---------------------------------------------------------------------- */
257  /* prework at node j */
258  /* ---------------------------------------------------------------------- */
259 
260  /* node j is being visited for the first time */
261  Cstack [++chead] = j ; /* push j onto the stack */
262  timestamp++ ; /* get a timestamp */
263  Time [j] = timestamp ; /* give the timestamp to node j */
264  Low [j] = timestamp ;
265  Flag [j] = UNASSIGNED ; /* flag node j as visited */
266 
267  /* ---------------------------------------------------------------------- */
268  /* DFS rooted at node j */
269  /* ---------------------------------------------------------------------- */
270 
271  /* determine which column jj of the A is column j of A*Q */
272  jj = (Q == (Int *) NULL) ? (j) : (BTF_UNFLIP (Q [j])) ;
273  for (p = Ap [jj] ; p < Ap [jj+1] ; p++)
274  {
275  i = Ai [p] ; /* examine the edge from node j to node i */
276  if (Flag [i] == UNVISITED)
277  {
278  /* Node i has not been visited - start a DFS at node i. */
279  amesos_dfs (j, i) ;
280  }
281  else if (Flag [i] == UNASSIGNED)
282  {
283  /* Node i has been visited, but still unassigned to a block
284  * this is a back or cross edge if Time [i] < Time [j].
285  * Note that i might equal j, in which case this code does
286  * nothing. */
287  Low [j] = MIN (Low [j], Time [i]) ;
288  }
289  }
290 
291  /* ---------------------------------------------------------------------- */
292  /* postwork at node j */
293  /* ---------------------------------------------------------------------- */
294 
295  /* determine if node j is the head of a component */
296  if (Low [j] == Time [j])
297  {
298  /* pop all nodes in this strongly connected component from Cstack */
299  while (TRUE)
300  {
301  i = Cstack [chead--] ; /* pop a node from the Cstack */
302  Flag [i] = nblocks ; /* assign node i to current block */
303  if (i == j) break ; /* current block ends at node j */
304  }
305  nblocks++ ; /* one more block has been found */
306  }
307  /* update Low [parent] */
308  if (parent != EMPTY)
309  {
310  /* Note that this could be done with Low[j] = MIN(Low[j],Low[i]) just
311  * after the dfs (j,i) statement above, and then parent would not have
312  * to be an input argument. Putting it here places all the postwork
313  * for node j in one place, thus making the non-recursive DFS easier. */
314  Low [parent] = MIN (Low [parent], Low [j]) ;
315  }
316 }
317 
318 #endif
319 
320 /* ========================================================================== */
321 /* === btf_strongcomp ======================================================= */
322 /* ========================================================================== */
323 
324 #ifndef RECURSIVE
325 
326 Int BTF(strongcomp) /* return # of strongly connected components */
327 (
328  /* input, not modified: */
329  Int n, /* A is n-by-n in compressed column form */
330  Int Ap [ ], /* size n+1 */
331  Int Ai [ ], /* size nz = Ap [n] */
332 
333  /* optional input, modified (if present) on output: */
334  Int Q [ ], /* size n, input column permutation. The permutation Q can
335  * include a flag which indicates an unmatched row.
336  * jold = BTF_UNFLIP (Q [jnew]) is the permutation;
337  * this function ingnores these flags. On output, it is
338  * modified according to the permutation P. */
339 
340  /* output, not defined on input: */
341  Int P [ ], /* size n. P [k] = j if row and column j are kth row/col
342  * in permuted matrix. */
343  Int R [ ], /* size n+1. kth block is in rows/cols R[k] ... R[k+1]-1
344  * of the permuted matrix. */
345 
346  /* workspace, not defined on input or output: */
347  Int Work [ ] /* size 4n */
348 )
349 
350 #else
351 
352 Int BTF(strongcomp) /* recursive version - same as above except for Work size */
353 (
354  Int n_in,
355  Int Ap_in [ ],
356  Int Ai_in [ ],
357  Int Q_in [ ],
358  Int P_in [ ],
359  Int R_in [ ],
360  Int Work [ ] /* size 2n */
361 )
362 
363 #endif
364 
365 {
366  Int j, k, b ;
367 
368 #ifndef RECURSIVE
369  Int timestamp, nblocks, *Flag, *Cstack, *Time, *Low, *Jstack, *Pstack ;
370 #else
371  n = n_in ;
372  Ap = Ap_in ;
373  Ai = Ai_in ;
374  Q = Q_in ;
375  P = P_in ;
376  R = R_in ;
377  chead = EMPTY ;
378 #endif
379 
380  /* ---------------------------------------------------------------------- */
381  /* get and initialize workspace */
382  /* ---------------------------------------------------------------------- */
383 
384  /* timestamp is incremented each time a new node is visited.
385  *
386  * Time [j] is the timestamp given to node j.
387  *
388  * Low [j] is the lowest timestamp of any node reachable from j via either
389  * a path to any descendent of j in the DFS tree, or via a single edge to
390  * an either an ancestor (a back edge) or another node that's neither an
391  * ancestor nor a descendant (a cross edge). If Low [j] is equal to
392  * the timestamp of node j (Time [j]), then node j is the "head" of a
393  * strongly connected component (SCC). That is, it is the first node
394  * visited in its strongly connected component, and the DFS subtree rooted
395  * at node j spans all the nodes of the strongly connected component.
396  *
397  * The term "block" and "component" are used interchangebly in this code;
398  * "block" being a matrix term and "component" being a graph term for the
399  * same thing.
400  *
401  * When a node is visited, it is placed on the Cstack (for "component"
402  * stack). When node j is found to be an SCC head, all the nodes from the
403  * top of the stack to node j itself form the nodes in the SCC. This Cstack
404  * is used for both the recursive and non-recursive versions.
405  */
406 
407  Time = Work ; Work += n ;
408  Flag = Work ; Work += n ;
409  Low = P ; /* use output array P as workspace for Low */
410  Cstack = R ; /* use output array R as workspace for Cstack */
411 
412 #ifndef RECURSIVE
413  /* stack for non-recursive dfs */
414  Jstack = Work ; Work += n ; /* stack for j */
415  Pstack = Work ; /* stack for p */
416 #endif
417 
418  for (j = 0 ; j < n ; j++)
419  {
420  Flag [j] = UNVISITED ;
421  Low [j] = EMPTY ;
422  Time [j] = EMPTY ;
423 #ifndef NDEBUG
424  Cstack [j] = EMPTY ;
425 #ifndef RECURSIVE
426  Jstack [j] = EMPTY ;
427  Pstack [j] = EMPTY ;
428 #endif
429 #endif
430  }
431 
432  timestamp = 0 ; /* each node given a timestamp when it is visited */
433  nblocks = 0 ; /* number of blocks found so far */
434 
435  /* ---------------------------------------------------------------------- */
436  /* find the connected components via a depth-first-search */
437  /* ---------------------------------------------------------------------- */
438 
439  for (j = 0 ; j < n ; j++)
440  {
441  /* node j is unvisited or assigned to a block. Cstack is empty. */
442  ASSERT (Flag [j] == UNVISITED || (Flag [j] >= 0 && Flag [j] < nblocks));
443  if (Flag [j] == UNVISITED)
444  {
445 #ifndef RECURSIVE
446  /* non-recursive dfs (default) */
447  amesos_dfs (j, Ap, Ai, Q, Time, Flag, Low, &nblocks, &timestamp,
448  Cstack, Jstack, Pstack) ;
449 #else
450  /* recursive dfs (for illustration only) */
451  ASSERT (chead == EMPTY) ;
452  amesos_dfs (EMPTY, j) ;
453  ASSERT (chead == EMPTY) ;
454 #endif
455  }
456  }
457  ASSERT (timestamp == n) ;
458 
459  /* ---------------------------------------------------------------------- */
460  /* construct the block boundary array, R */
461  /* ---------------------------------------------------------------------- */
462 
463  for (b = 0 ; b < nblocks ; b++)
464  {
465  R [b] = 0 ;
466  }
467  for (j = 0 ; j < n ; j++)
468  {
469  /* node j has been assigned to block b = Flag [j] */
470  ASSERT (Time [j] > 0 && Time [j] <= n) ;
471  ASSERT (Low [j] > 0 && Low [j] <= n) ;
472  ASSERT (Flag [j] >= 0 && Flag [j] < nblocks) ;
473  R [Flag [j]]++ ;
474  }
475  /* R [b] is now the number of nodes in block b. Compute cumulative sum
476  * of R, using Time [0 ... nblocks-1] as workspace. */
477  Time [0] = 0 ;
478  for (b = 1 ; b < nblocks ; b++)
479  {
480  Time [b] = Time [b-1] + R [b-1] ;
481  }
482  for (b = 0 ; b < nblocks ; b++)
483  {
484  R [b] = Time [b] ;
485  }
486  R [nblocks] = n ;
487 
488  /* ---------------------------------------------------------------------- */
489  /* construct the permutation, preserving the natural order */
490  /* ---------------------------------------------------------------------- */
491 
492 #ifndef NDEBUG
493  for (k = 0 ; k < n ; k++)
494  {
495  P [k] = EMPTY ;
496  }
497 #endif
498 
499  for (j = 0 ; j < n ; j++)
500  {
501  /* place column j in the permutation */
502  P [Time [Flag [j]]++] = j ;
503  }
504 
505 #ifndef NDEBUG
506  for (k = 0 ; k < n ; k++)
507  {
508  ASSERT (P [k] != EMPTY) ;
509  }
510 #endif
511 
512  /* Now block b consists of the nodes k1 to k2-1 in the permuted matrix,
513  * where k1 = R [b] and k2 = R [b+1]. Row and column j of the original
514  * matrix becomes row and column P [k] of the permuted matrix. The set of
515  * of rows/columns (nodes) in block b is given by P [k1 ... k2-1], and this
516  * set is sorted in ascending order. Thus, if the matrix consists of just
517  * one block, P is the identity permutation. */
518 
519  /* ---------------------------------------------------------------------- */
520  /* if Q is present on input, set Q = Q*P' */
521  /* ---------------------------------------------------------------------- */
522 
523  if (Q != (Int *) NULL)
524  {
525  /* We found a symmetric permutation P for the matrix A*Q. The overall
526  * permutation is thus P*(A*Q)*P'. Set Q=Q*P' so that the final
527  * permutation is P*A*Q. Use Time as workspace. Note that this
528  * preserves the negative values of Q if the matrix is structurally
529  * singular. */
530  for (k = 0 ; k < n ; k++)
531  {
532  Time [k] = Q [P [k]] ;
533  }
534  for (k = 0 ; k < n ; k++)
535  {
536  Q [k] = Time [k] ;
537  }
538  }
539 
540  /* ---------------------------------------------------------------------- */
541  /* how to traverse the permuted matrix */
542  /* ---------------------------------------------------------------------- */
543 
544  /* If Q is not present, the following code can be used to traverse the
545  * permuted matrix P*A*P'
546  *
547  * // compute the inverse of P
548  * for (knew = 0 ; knew < n ; knew++)
549  * {
550  * // row and column kold in the old matrix is row/column knew
551  * // in the permuted matrix P*A*P'
552  * kold = P [knew] ;
553  * Pinv [kold] = knew ;
554  * }
555  * for (b = 0 ; b < nblocks ; b++)
556  * {
557  * // traverse block b of the permuted matrix P*A*P'
558  * k1 = R [b] ;
559  * k2 = R [b+1] ;
560  * nk = k2 - k1 ;
561  * for (jnew = k1 ; jnew < k2 ; jnew++)
562  * {
563  * jold = P [jnew] ;
564  * for (p = Ap [jold] ; p < Ap [jold+1] ; p++)
565  * {
566  * iold = Ai [p] ;
567  * inew = Pinv [iold] ;
568  * // Entry in the old matrix is A (iold, jold), and its
569  * // position in the new matrix P*A*P' is (inew, jnew).
570  * // Let B be the bth diagonal block of the permuted
571  * // matrix. If inew >= k1, then this entry is in row/
572  * // column (inew-k1, jnew-k1) of the nk-by-nk matrix B.
573  * // Otherwise, the entry is in the upper block triangular
574  * // part, not in any diagonal block.
575  * }
576  * }
577  * }
578  *
579  * If Q is present replace the above statement
580  * jold = P [jnew] ;
581  * with
582  * jold = Q [jnew] ;
583  * or
584  * jold = BTF_UNFLIP (Q [jnew]) ;
585  *
586  * then entry A (iold,jold) in the old (unpermuted) matrix is at (inew,jnew)
587  * in the permuted matrix P*A*Q. Everything else remains the same as the
588  * above (simply replace P*A*P' with P*A*Q in the above comments).
589  */
590 
591  /* ---------------------------------------------------------------------- */
592  /* return # of blocks / # of strongly connected components */
593  /* ---------------------------------------------------------------------- */
594 
595  return (nblocks) ;
596 }
#define EMPTY
#define Int
static void amesos_dfs(Int j, Int Ap[], Int Ai[], Int Q[], Int Time[], Int Flag[], Int Low[], Int *p_nblocks, Int *p_timestamp, Int Cstack[], Int Jstack[], Int Pstack[])
#define UNASSIGNED
#define P(k)
#define UNVISITED
#define BTF_UNFLIP(j)
#define NULL
#define ASSERT(expression)
Int BTF(strongcomp)
#define MIN(a, b)
int n
#define TRUE