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