Amesos Package Browser (Single Doxygen Collection)  Development
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
amesos_cholmod_rowcolcounts.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /* === Cholesky/cholmod_rowcolcounts ======================================== */
3 /* ========================================================================== */
4 
5 /* -----------------------------------------------------------------------------
6  * CHOLMOD/Cholesky Module. Copyright (C) 2005-2006, Timothy A. Davis
7  * The CHOLMOD/Cholesky Module is licensed under Version 2.1 of the GNU
8  * Lesser General Public License. See lesser.txt for a text of the license.
9  * CHOLMOD is also available under other licenses; contact authors for details.
10  * http://www.cise.ufl.edu/research/sparse
11  * -------------------------------------------------------------------------- */
12 
13 /* Compute the row and column counts of the Cholesky factor L of the matrix
14  * A or A*A'. The etree and its postordering must already be computed (see
15  * cholmod_etree and cholmod_postorder) and given as inputs to this routine.
16  *
17  * For the symmetric case (LL'=A), A is accessed by column. Only the lower
18  * triangular part of A is used. Entries not in this part of the matrix are
19  * ignored. This is the same as storing the upper triangular part of A by
20  * rows, with entries in the lower triangular part being ignored. NOTE: this
21  * representation is the TRANSPOSE of the input to cholmod_etree.
22  *
23  * For the unsymmetric case (LL'=AA'), A is accessed by column. Equivalently,
24  * if A is viewed as a matrix in compressed-row form, this routine computes
25  * the row and column counts for L where LL'=A'A. If the input vector f is
26  * present, then F*F' is analyzed instead, where F = A(:,f).
27  *
28  * The set f is held in fset and fsize.
29  * fset = NULL means ":" in MATLAB. fset is ignored.
30  * fset != NULL means f = fset [0..fset-1].
31  * fset != NULL and fsize = 0 means f is the empty set.
32  * Common->status is set to CHOLMOD_INVALID if fset is invalid.
33  *
34  * In both cases, the columns of A need not be sorted.
35  * A can be packed or unpacked.
36  *
37  * References:
38  * J. Gilbert, E. Ng, B. Peyton, "An efficient algorithm to compute row and
39  * column counts for sparse Cholesky factorization", SIAM J. Matrix Analysis &
40  * Applic., vol 15, 1994, pp. 1075-1091.
41  *
42  * J. Gilbert, X. Li, E. Ng, B. Peyton, "Computing row and column counts for
43  * sparse QR and LU factorization", BIT, vol 41, 2001, pp. 693-710.
44  *
45  * workspace:
46  * if symmetric: Flag (nrow), Iwork (2*nrow)
47  * if unsymmetric: Flag (nrow), Iwork (2*nrow+ncol), Head (nrow+1)
48  *
49  * Supports any xtype (pattern, real, complex, or zomplex).
50  */
51 
52 #ifndef NCHOLESKY
53 
56 
57 /* ========================================================================== */
58 /* === initialize_node ====================================================== */
59 /* ========================================================================== */
60 
61 static int amesos_initialize_node /* initial work for kth node in postordered etree */
62 (
63  Int k, /* at the kth step of the algorithm (and kth node) */
64  Int Post [ ], /* Post [k] = i, the kth node in postordered etree */
65  Int Parent [ ], /* Parent [i] is the parent of i in the etree */
66  Int ColCount [ ], /* ColCount [c] is the current weight of node c */
67  Int PrevNbr [ ] /* PrevNbr [u] = k if u was last considered at step k */
68 )
69 {
70  Int p, parent ;
71  /* determine p, the kth node in the postordered etree */
72  p = Post [k] ;
73  /* adjust the weight if p is not a root of the etree */
74  parent = Parent [p] ;
75  if (parent != EMPTY)
76  {
77  ColCount [parent]-- ;
78  }
79  /* flag node p to exclude self edges (p,p) */
80  PrevNbr [p] = k ;
81  return (p) ;
82 }
83 
84 
85 /* ========================================================================== */
86 /* === process_edge ========================================================= */
87 /* ========================================================================== */
88 
89 /* edge (p,u) is being processed. p < u is a descendant of its ancestor u in
90  * the etree. node p is the kth node in the postordered etree. */
91 
92 static void amesos_process_edge
93 (
94  Int p, /* process edge (p,u) of the matrix */
95  Int u,
96  Int k, /* we are at the kth node in the postordered etree */
97  Int First [ ], /* First [i] = k if the postordering of first
98  * descendent of node i is k */
99  Int PrevNbr [ ], /* u was last considered at step k = PrevNbr [u] */
100  Int ColCount [ ], /* ColCount [c] is the current weight of node c */
101  Int PrevLeaf [ ], /* s = PrevLeaf [u] means that s was the last leaf
102  * seen in the subtree rooted at u. */
103  Int RowCount [ ], /* RowCount [i] is # of nonzeros in row i of L,
104  * including the diagonal. Not computed if NULL. */
105  Int SetParent [ ], /* the FIND/UNION data structure, which forms a set
106  * of trees. A root i has i = SetParent [i]. Following
107  * a path from i to the root q of the subtree containing
108  * i means that q is the SetParent representative of i.
109  * All nodes in the tree could have their SetParent
110  * equal to the root q; the tree representation is used
111  * to save time. When a path is traced from i to its
112  * root q, the path is re-traversed to set the SetParent
113  * of the whole path to be the root q. */
114  Int Level [ ] /* Level [i] = length of path from node i to root */
115 )
116 {
117  Int prevleaf, q, s, sparent ;
118  if (First [p] > PrevNbr [u])
119  {
120  /* p is a leaf of the subtree of u */
121  ColCount [p]++ ;
122  prevleaf = PrevLeaf [u] ;
123  if (prevleaf == EMPTY)
124  {
125  /* p is the first leaf of subtree of u; RowCount will be incremented
126  * by the length of the path in the etree from p up to u. */
127  q = u ;
128  }
129  else
130  {
131  /* q = FIND (prevleaf): find the root q of the
132  * SetParent tree containing prevleaf */
133  for (q = prevleaf ; q != SetParent [q] ; q = SetParent [q])
134  {
135  ;
136  }
137  /* the root q has been found; re-traverse the path and
138  * perform path compression */
139  s = prevleaf ;
140  for (s = prevleaf ; s != q ; s = sparent)
141  {
142  sparent = SetParent [s] ;
143  SetParent [s] = q ;
144  }
145  /* adjust the RowCount and ColCount; RowCount will be incremented by
146  * the length of the path from p to the SetParent root q, and
147  * decrement the ColCount of q by one. */
148  ColCount [q]-- ;
149  }
150  if (RowCount != NULL)
151  {
152  /* if RowCount is being computed, increment it by the length of
153  * the path from p to q */
154  RowCount [u] += (Level [p] - Level [q]) ;
155  }
156  /* p is a leaf of the subtree of u, so mark PrevLeaf [u] to be p */
157  PrevLeaf [u] = p ;
158  }
159  /* flag u has having been processed at step k */
160  PrevNbr [u] = k ;
161 }
162 
163 
164 /* ========================================================================== */
165 /* === finalize_node ======================================================== */
166 /* ========================================================================== */
167 
168 static void amesos_finalize_node /* compute UNION (p, Parent [p]) */
169 (
170  Int p,
171  Int Parent [ ], /* Parent [p] is the parent of p in the etree */
172  Int SetParent [ ] /* see process_edge, above */
173 )
174 {
175  /* all nodes in the SetParent tree rooted at p now have as their final
176  * root the node Parent [p]. This computes UNION (p, Parent [p]) */
177  if (Parent [p] != EMPTY)
178  {
179  SetParent [p] = Parent [p] ;
180  }
181 }
182 
183 
184 /* ========================================================================== */
185 /* === cholmod_rowcolcounts ================================================= */
186 /* ========================================================================== */
187 
189 (
190  /* ---- input ---- */
191  cholmod_sparse *A, /* matrix to analyze */
192  Int *fset, /* subset of 0:(A->ncol)-1 */
193  size_t fsize, /* size of fset */
194  Int *Parent, /* size nrow. Parent [i] = p if p is the parent of i */
195  Int *Post, /* size nrow. Post [k] = i if i is the kth node in
196  * the postordered etree. */
197  /* ---- output --- */
198  Int *RowCount, /* size nrow. RowCount [i] = # entries in the ith row of
199  * L, including the diagonal. */
200  Int *ColCount, /* size nrow. ColCount [i] = # entries in the ith
201  * column of L, including the diagonal. */
202  Int *First, /* size nrow. First [i] = k is the least postordering
203  * of any descendant of i. */
204  Int *Level, /* size nrow. Level [i] is the length of the path from
205  * i to the root, with Level [root] = 0. */
206  /* --------------- */
207  cholmod_common *Common
208 )
209 {
210  double fl, ff ;
211  Int *Ap, *Ai, *Anz, *PrevNbr, *SetParent, *Head, *PrevLeaf, *Anext, *Ipost,
212  *Iwork ;
213  Int i, j, r, k, len, s, p, pend, inew, stype, nf, anz, inode, parent,
214  nrow, ncol, packed, use_fset, jj ;
215  size_t w ;
216  int ok = TRUE ;
217 
218  /* ---------------------------------------------------------------------- */
219  /* check inputs */
220  /* ---------------------------------------------------------------------- */
221 
223  RETURN_IF_NULL (A, FALSE) ;
224  RETURN_IF_NULL (Parent, FALSE) ;
225  RETURN_IF_NULL (Post, FALSE) ;
226  RETURN_IF_NULL (ColCount, FALSE) ;
227  RETURN_IF_NULL (First, FALSE) ;
228  RETURN_IF_NULL (Level, FALSE) ;
230  stype = A->stype ;
231  if (stype > 0)
232  {
233  /* symmetric with upper triangular part not supported */
234  ERROR (CHOLMOD_INVALID, "symmetric upper not supported") ;
235  return (FALSE) ;
236  }
237  Common->status = CHOLMOD_OK ;
238 
239  /* ---------------------------------------------------------------------- */
240  /* allocate workspace */
241  /* ---------------------------------------------------------------------- */
242 
243  nrow = A->nrow ; /* the number of rows of A */
244  ncol = A->ncol ; /* the number of columns of A */
245 
246  /* w = 2*nrow + (stype ? 0 : ncol) */
247  w = CHOLMOD(mult_size_t) (nrow, 2, &ok) ;
248  w = CHOLMOD(add_size_t) (w, (stype ? 0 : ncol), &ok) ;
249  if (!ok)
250  {
251  ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
252  return (FALSE) ;
253  }
254 
255  CHOLMOD(allocate_work) (nrow, w, 0, Common) ;
256  if (Common->status < CHOLMOD_OK)
257  {
258  return (FALSE) ;
259  }
260 
261  ASSERT (CHOLMOD(dump_perm) (Post, nrow, nrow, "Post", Common)) ;
262  ASSERT (CHOLMOD(dump_parent) (Parent, nrow, "Parent", Common)) ;
263 
264  /* ---------------------------------------------------------------------- */
265  /* get inputs */
266  /* ---------------------------------------------------------------------- */
267 
268  Ap = A->p ; /* size ncol+1, column pointers for A */
269  Ai = A->i ; /* the row indices of A, of size nz=Ap[ncol+1] */
270  Anz = A->nz ;
271  packed = A->packed ;
272  ASSERT (IMPLIES (!packed, Anz != NULL)) ;
273 
274  /* ---------------------------------------------------------------------- */
275  /* get workspace */
276  /* ---------------------------------------------------------------------- */
277 
278  Iwork = Common->Iwork ;
279  SetParent = Iwork ; /* size nrow (i/i/l) */
280  PrevNbr = Iwork + nrow ; /* size nrow (i/i/l) */
281  Anext = Iwork + 2*((size_t) nrow) ; /* size ncol (i/i/l) (unsym only) */
282  PrevLeaf = Common->Flag ; /* size nrow */
283  Head = Common->Head ; /* size nrow+1 (unsym only)*/
284 
285  /* ---------------------------------------------------------------------- */
286  /* find the first descendant and level of each node in the tree */
287  /* ---------------------------------------------------------------------- */
288 
289  /* First [i] = k if the postordering of first descendent of node i is k */
290  /* Level [i] = length of path from node i to the root (Level [root] = 0) */
291 
292  for (i = 0 ; i < nrow ; i++)
293  {
294  First [i] = EMPTY ;
295  }
296 
297  /* postorder traversal of the etree */
298  for (k = 0 ; k < nrow ; k++)
299  {
300  /* node i of the etree is the kth node in the postordered etree */
301  i = Post [k] ;
302 
303  /* i is a leaf if First [i] is still EMPTY */
304  /* ColCount [i] starts at 1 if i is a leaf, zero otherwise */
305  ColCount [i] = (First [i] == EMPTY) ? 1 : 0 ;
306 
307  /* traverse the path from node i to the root, stopping if we find a
308  * node r whose First [r] is already defined. */
309  len = 0 ;
310  for (r = i ; (r != EMPTY) && (First [r] == EMPTY) ; r = Parent [r])
311  {
312  First [r] = k ;
313  len++ ;
314  }
315  if (r == EMPTY)
316  {
317  /* we hit a root node, the level of which is zero */
318  len-- ;
319  }
320  else
321  {
322  /* we stopped at node r, where Level [r] is already defined */
323  len += Level [r] ;
324  }
325  /* re-traverse the path from node i to r; set the level of each node */
326  for (s = i ; s != r ; s = Parent [s])
327  {
328  Level [s] = len-- ;
329  }
330  }
331 
332  /* ---------------------------------------------------------------------- */
333  /* AA' case: sort columns of A according to first postordered row index */
334  /* ---------------------------------------------------------------------- */
335 
336  fl = 0.0 ;
337  if (stype == 0)
338  {
339  /* [ use PrevNbr [0..nrow-1] as workspace for Ipost */
340  Ipost = PrevNbr ;
341  /* Ipost [i] = k if i is the kth node in the postordered etree. */
342  for (k = 0 ; k < nrow ; k++)
343  {
344  Ipost [Post [k]] = k ;
345  }
346  use_fset = (fset != NULL) ;
347  if (use_fset)
348  {
349  nf = fsize ;
350  /* clear Anext to check fset */
351  for (j = 0 ; j < ncol ; j++)
352  {
353  Anext [j] = -2 ;
354  }
355  /* find the first postordered row in each column of A (post,f)
356  * and place the column in the corresponding link list */
357  for (jj = 0 ; jj < nf ; jj++)
358  {
359  j = fset [jj] ;
360  if (j < 0 || j > ncol || Anext [j] != -2)
361  {
362  /* out-of-range or duplicate entry in fset */
363  ERROR (CHOLMOD_INVALID, "fset invalid") ;
364  return (FALSE) ;
365  }
366  /* flag column j as having been seen */
367  Anext [j] = EMPTY ;
368  }
369  /* fset is now valid */
370  ASSERT (CHOLMOD(dump_perm) (fset, nf, ncol, "fset", Common)) ;
371  }
372  else
373  {
374  nf = ncol ;
375  }
376  for (jj = 0 ; jj < nf ; jj++)
377  {
378  j = (use_fset) ? (fset [jj]) : jj ;
379  /* column j is in the fset; find the smallest row (if any) */
380  p = Ap [j] ;
381  pend = (packed) ? (Ap [j+1]) : (p + Anz [j]) ;
382  ff = (double) MAX (0, pend - p) ;
383  fl += ff*ff + ff ;
384  if (pend > p)
385  {
386  k = Ipost [Ai [p]] ;
387  for ( ; p < pend ; p++)
388  {
389  inew = Ipost [Ai [p]] ;
390  k = MIN (k, inew) ;
391  }
392  /* place column j in link list k */
393  ASSERT (k >= 0 && k < nrow) ;
394  Anext [j] = Head [k] ;
395  Head [k] = j ;
396  }
397  }
398  /* Ipost no longer needed for inverse postordering ]
399  * Head [k] contains a link list of all columns whose first
400  * postordered row index is equal to k, for k = 0 to nrow-1. */
401  }
402 
403  /* ---------------------------------------------------------------------- */
404  /* compute the row counts and node weights */
405  /* ---------------------------------------------------------------------- */
406 
407  if (RowCount != NULL)
408  {
409  for (i = 0 ; i < nrow ; i++)
410  {
411  RowCount [i] = 1 ;
412  }
413  }
414  for (i = 0 ; i < nrow ; i++)
415  {
416  PrevLeaf [i] = EMPTY ;
417  PrevNbr [i] = EMPTY ;
418  SetParent [i] = i ; /* every node is in its own set, by itself */
419  }
420 
421  if (stype != 0)
422  {
423 
424  /* ------------------------------------------------------------------ */
425  /* symmetric case: LL' = A */
426  /* ------------------------------------------------------------------ */
427 
428  /* also determine the number of entries in triu(A) */
429  anz = nrow ;
430  for (k = 0 ; k < nrow ; k++)
431  {
432  /* j is the kth node in the postordered etree */
433  j = amesos_initialize_node (k, Post, Parent, ColCount, PrevNbr) ;
434 
435  /* for all nonzeros A(i,j) below the diagonal, in column j of A */
436  p = Ap [j] ;
437  pend = (packed) ? (Ap [j+1]) : (p + Anz [j]) ;
438  for ( ; p < pend ; p++)
439  {
440  i = Ai [p] ;
441  if (i > j)
442  {
443  /* j is a descendant of i in etree(A) */
444  anz++ ;
445  amesos_process_edge (j, i, k, First, PrevNbr, ColCount,
446  PrevLeaf, RowCount, SetParent, Level) ;
447  }
448  }
449  /* update SetParent: UNION (j, Parent [j]) */
450  amesos_finalize_node (j, Parent, SetParent) ;
451  }
452  Common->anz = anz ;
453  }
454  else
455  {
456 
457  /* ------------------------------------------------------------------ */
458  /* unsymmetric case: LL' = AA' */
459  /* ------------------------------------------------------------------ */
460 
461  for (k = 0 ; k < nrow ; k++)
462  {
463  /* inode is the kth node in the postordered etree */
464  inode = amesos_initialize_node (k, Post, Parent, ColCount, PrevNbr) ;
465 
466  /* for all cols j whose first postordered row is k: */
467  for (j = Head [k] ; j != EMPTY ; j = Anext [j])
468  {
469  /* k is the first postordered row in column j of A */
470  /* for all rows i in column j: */
471  p = Ap [j] ;
472  pend = (packed) ? (Ap [j+1]) : (p + Anz [j]) ;
473  for ( ; p < pend ; p++)
474  {
475  i = Ai [p] ;
476  /* has i already been considered at this step k */
477  if (PrevNbr [i] < k)
478  {
479  /* inode is a descendant of i in etree(AA') */
480  /* process edge (inode,i) and set PrevNbr[i] to k */
481  amesos_process_edge (inode, i, k, First, PrevNbr, ColCount,
482  PrevLeaf, RowCount, SetParent, Level) ;
483  }
484  }
485  }
486  /* clear link list k */
487  Head [k] = EMPTY ;
488  /* update SetParent: UNION (inode, Parent [inode]) */
489  amesos_finalize_node (inode, Parent, SetParent) ;
490  }
491  }
492 
493  /* ---------------------------------------------------------------------- */
494  /* finish computing the column counts */
495  /* ---------------------------------------------------------------------- */
496 
497  for (j = 0 ; j < nrow ; j++)
498  {
499  parent = Parent [j] ;
500  if (parent != EMPTY)
501  {
502  /* add the ColCount of j to its parent */
503  ColCount [parent] += ColCount [j] ;
504  }
505  }
506 
507  /* ---------------------------------------------------------------------- */
508  /* clear workspace */
509  /* ---------------------------------------------------------------------- */
510 
511  Common->mark = EMPTY ;
512  CHOLMOD(clear_flag) (Common) ;
513  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
514 
515  /* ---------------------------------------------------------------------- */
516  /* flop count and nnz(L) for subsequent LL' numerical factorization */
517  /* ---------------------------------------------------------------------- */
518 
519  /* use double to avoid integer overflow. lnz cannot be NaN. */
520  Common->aatfl = fl ;
521  Common->lnz = 0. ;
522  fl = 0 ;
523  for (j = 0 ; j < nrow ; j++)
524  {
525  ff = (double) (ColCount [j]) ;
526  Common->lnz += ff ;
527  fl += ff*ff ;
528  }
529 
530  Common->fl = fl ;
531  PRINT1 (("rowcol fl %g lnz %g\n", Common->fl, Common->lnz)) ;
532 
533  return (TRUE) ;
534 }
535 #endif
#define CHOLMOD_TOO_LARGE
static void amesos_process_edge(Int p, Int u, Int k, Int First[], Int PrevNbr[], Int ColCount[], Int PrevLeaf[], Int RowCount[], Int SetParent[], Int Level[])
#define EMPTY
#define Int
size_t CHOLMOD() add_size_t(size_t a, size_t b, int *ok)
#define FALSE
#define PRINT1(params)
#define RETURN_IF_NULL_COMMON(result)
size_t CHOLMOD() mult_size_t(size_t a, size_t k, int *ok)
int CHOLMOD() dump_work(int flag, int head, UF_long wsize, cholmod_common *Common)
#define CHOLMOD_PATTERN
#define CHOLMOD(name)
#define MAX(a, b)
#define NULL
#define ASSERT(expression)
static int amesos_initialize_node(Int k, Int Post[], Int Parent[], Int ColCount[], Int PrevNbr[])
int CHOLMOD() dump_perm(Int *Perm, size_t len, size_t n, char *name, cholmod_common *Common)
#define CHOLMOD_INVALID
#define CHOLMOD_OK
static void amesos_finalize_node(Int p, Int Parent[], Int SetParent[])
int CHOLMOD() allocate_work(size_t nrow, size_t iworksize, size_t xworksize, cholmod_common *Common)
#define RETURN_IF_NULL(A, result)
int CHOLMOD() dump_parent(Int *Parent, size_t n, char *name, cholmod_common *Common)
#define MIN(a, b)
UF_long CHOLMOD() clear_flag(cholmod_common *Common)
#define IMPLIES(p, q)
#define ERROR(status, msg)
#define TRUE
int CHOLMOD() rowcolcounts(cholmod_sparse *A, Int *fset, size_t fsize, Int *Parent, Int *Post, Int *RowCount, Int *ColCount, Int *First, Int *Level, cholmod_common *Common)
#define RETURN_IF_XTYPE_INVALID(A, xtype1, xtype2, result)
#define CHOLMOD_ZOMPLEX