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