Amesos Package Browser (Single Doxygen Collection)  Development
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
amesos_cholmod_resymbol.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /* === Cholesky/cholmod_resymbol ============================================ */
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 /* Recompute the symbolic pattern of L. Entries not in the symbolic pattern
14  * are dropped. L->Perm can be used (or not) to permute the input matrix A.
15  *
16  * These routines are used after a supernodal factorization is converted into
17  * a simplicial one, to remove zero entries that were added due to relaxed
18  * supernode amalgamation. They can also be used after a series of downdates
19  * to remove entries that would no longer be present if the matrix were
20  * factorized from scratch. A downdate (cholmod_updown) does not remove any
21  * entries from L.
22  *
23  * workspace: Flag (nrow), Head (nrow+1),
24  * if symmetric: Iwork (2*nrow)
25  * if unsymmetric: Iwork (2*nrow+ncol).
26  * Allocates up to 2 copies of its input matrix A (pattern only).
27  */
28 
29 #ifndef NCHOLESKY
30 
33 
34 /* ========================================================================== */
35 /* === cholmod_resymbol ===================================================== */
36 /* ========================================================================== */
37 
38 /* Remove entries from L that are not in the factorization of P*A*P', P*A*A'*P',
39  * or P*F*F'*P' (depending on A->stype and whether fset is NULL or not).
40  *
41  * cholmod_resymbol is the same as cholmod_resymbol_noperm, except that it
42  * first permutes A according to L->Perm. A can be upper/lower/unsymmetric,
43  * in contrast to cholmod_resymbol_noperm (which can be lower or unsym). */
44 
45 int CHOLMOD(resymbol)
46 (
47  /* ---- input ---- */
48  cholmod_sparse *A, /* matrix to analyze */
49  Int *fset, /* subset of 0:(A->ncol)-1 */
50  size_t fsize, /* size of fset */
51  int pack, /* if TRUE, pack the columns of L */
52  /* ---- in/out --- */
53  cholmod_factor *L, /* factorization, entries pruned on output */
54  /* --------------- */
55  cholmod_common *Common
56 )
57 {
58  cholmod_sparse *H, *F, *G ;
59  Int stype, nrow, ncol ;
60  size_t s ;
61  int ok = TRUE ;
62 
63  /* ---------------------------------------------------------------------- */
64  /* check inputs */
65  /* ---------------------------------------------------------------------- */
66 
68  RETURN_IF_NULL (A, FALSE) ;
69  RETURN_IF_NULL (L, FALSE) ;
72  Common->status = CHOLMOD_OK ;
73  if (L->is_super)
74  {
75  /* cannot operate on a supernodal factorization */
76  ERROR (CHOLMOD_INVALID, "cannot operate on supernodal L") ;
77  return (FALSE) ;
78  }
79  if (L->n != A->nrow)
80  {
81  /* dimensions must agree */
82  ERROR (CHOLMOD_INVALID, "A and L dimensions do not match") ;
83  return (FALSE) ;
84  }
85 
86  /* ---------------------------------------------------------------------- */
87  /* allocate workspace */
88  /* ---------------------------------------------------------------------- */
89 
90  stype = A->stype ;
91  nrow = A->nrow ;
92  ncol = A->ncol ;
93 
94  /* s = 2*nrow + (stype ? 0 : ncol) */
95  s = CHOLMOD(mult_size_t) (nrow, 2, &ok) ;
96  s = CHOLMOD(add_size_t) (s, (stype ? 0 : ncol), &ok) ;
97  if (!ok)
98  {
99  ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
100  return (FALSE) ;
101  }
102 
103  CHOLMOD(allocate_work) (nrow, s, 0, Common) ;
104  if (Common->status < CHOLMOD_OK)
105  {
106  return (FALSE) ;
107  }
108 
109  /* ---------------------------------------------------------------------- */
110  /* permute the input matrix if necessary */
111  /* ---------------------------------------------------------------------- */
112 
113  H = NULL ;
114  G = NULL ;
115 
116  if (stype > 0)
117  {
118  if (L->ordering == CHOLMOD_NATURAL)
119  {
120  /* F = triu(A)' */
121  /* workspace: Iwork (nrow) */
122  G = CHOLMOD(ptranspose) (A, 0, NULL, NULL, 0, Common) ;
123  }
124  else
125  {
126  /* F = triu(A(p,p))' */
127  /* workspace: Iwork (2*nrow) */
128  G = CHOLMOD(ptranspose) (A, 0, L->Perm, NULL, 0, Common) ;
129  }
130  F = G ;
131  }
132  else if (stype < 0)
133  {
134  if (L->ordering == CHOLMOD_NATURAL)
135  {
136  F = A ;
137  }
138  else
139  {
140  /* G = triu(A(p,p))' */
141  /* workspace: Iwork (2*nrow) */
142  G = CHOLMOD(ptranspose) (A, 0, L->Perm, NULL, 0, Common) ;
143  /* H = G' */
144  /* workspace: Iwork (nrow) */
145  H = CHOLMOD(ptranspose) (G, 0, NULL, NULL, 0, Common) ;
146  F = H ;
147  }
148  }
149  else
150  {
151  if (L->ordering == CHOLMOD_NATURAL)
152  {
153  F = A ;
154  }
155  else
156  {
157  /* G = A(p,f)' */
158  /* workspace: Iwork (nrow if no fset; MAX (nrow,ncol) if fset)*/
159  G = CHOLMOD(ptranspose) (A, 0, L->Perm, fset, fsize, Common) ;
160  /* H = G' */
161  /* workspace: Iwork (ncol) */
162  H = CHOLMOD(ptranspose) (G, 0, NULL, NULL, 0, Common) ;
163  F = H ;
164  }
165  }
166 
167  /* No need to check for failure here. cholmod_resymbol_noperm will return
168  * FALSE if F is NULL. */
169 
170  /* ---------------------------------------------------------------------- */
171  /* resymbol */
172  /* ---------------------------------------------------------------------- */
173 
174  ok = CHOLMOD(resymbol_noperm) (F, fset, fsize, pack, L, Common) ;
175 
176  /* ---------------------------------------------------------------------- */
177  /* free the temporary matrices, if they exist */
178  /* ---------------------------------------------------------------------- */
179 
180  CHOLMOD(free_sparse) (&H, Common) ;
181  CHOLMOD(free_sparse) (&G, Common) ;
182  return (ok) ;
183 }
184 
185 
186 /* ========================================================================== */
187 /* === cholmod_resymbol_noperm ============================================== */
188 /* ========================================================================== */
189 
190 /* Redo symbolic LDL' or LL' factorization of I + F*F' or I+A, where F=A(:,f).
191  *
192  * L already exists, but is a superset of the true dynamic pattern (simple
193  * column downdates and row deletions haven't pruned anything). Just redo the
194  * symbolic factorization and drop entries that are no longer there. The
195  * diagonal is not modified. The number of nonzeros in column j of L
196  * (L->nz[j]) can decrease. The column pointers (L->p[j]) remain unchanged if
197  * pack is FALSE or if L is not monotonic. Otherwise, the columns of L are
198  * packed in place.
199  *
200  * For the symmetric case, the columns of the lower triangular part of A
201  * are accessed by column. NOTE that this the transpose of the general case.
202  *
203  * For the unsymmetric case, F=A(:,f) is accessed by column.
204  *
205  * A need not be sorted, and can be packed or unpacked. If L->Perm is not
206  * identity, then A must already be permuted according to the permutation used
207  * to factorize L. The advantage of using this routine is that it does not
208  * need to create permuted copies of A first.
209  *
210  * This routine can be called if L is only partially factored via cholmod_rowfac
211  * since all it does is prune. If an entry is in F*F' or A, but not in L, it
212  * isn't added to L.
213  *
214  * L must be simplicial LDL' or LL'; it cannot be supernodal or symbolic.
215  *
216  * The set f is held in fset and fsize.
217  * fset = NULL means ":" in MATLAB. fset is ignored.
218  * fset != NULL means f = fset [0..fset-1].
219  * fset != NULL and fsize = 0 means f is the empty set.
220  * There can be no duplicates in fset.
221  * Common->status is set to CHOLMOD_INVALID if fset is invalid.
222  *
223  * workspace: Flag (nrow), Head (nrow+1),
224  * if symmetric: Iwork (2*nrow)
225  * if unsymmetric: Iwork (2*nrow+ncol).
226  * Unlike cholmod_resymbol, this routine does not allocate any temporary
227  * copies of its input matrix.
228  */
229 
231 (
232  /* ---- input ---- */
233  cholmod_sparse *A, /* matrix to analyze */
234  Int *fset, /* subset of 0:(A->ncol)-1 */
235  size_t fsize, /* size of fset */
236  int pack, /* if TRUE, pack the columns of L */
237  /* ---- in/out --- */
238  cholmod_factor *L, /* factorization, entries pruned on output */
239  /* --------------- */
240  cholmod_common *Common
241 )
242 {
243  double *Lx, *Lz ;
244  Int i, j, k, row, parent, p, pend, pdest, ncol, apacked, sorted, nrow, nf,
245  use_fset, mark, jj, stype, xtype ;
246  Int *Ap, *Ai, *Anz, *Li, *Lp, *Lnz, *Flag, *Head, *Link, *Anext, *Iwork ;
247  size_t s ;
248  int ok = TRUE ;
249 
250  /* ---------------------------------------------------------------------- */
251  /* check inputs */
252  /* ---------------------------------------------------------------------- */
253 
255  RETURN_IF_NULL (A, FALSE) ;
256  RETURN_IF_NULL (L, FALSE) ;
259  ncol = A->ncol ;
260  nrow = A->nrow ;
261  stype = A->stype ;
262  ASSERT (IMPLIES (stype != 0, nrow == ncol)) ;
263  if (stype > 0)
264  {
265  /* symmetric, with upper triangular part, not supported */
266  ERROR (CHOLMOD_INVALID, "symmetric upper not supported ") ;
267  return (FALSE) ;
268  }
269  if (L->is_super)
270  {
271  /* cannot operate on a supernodal or symbolic factorization */
272  ERROR (CHOLMOD_INVALID, "cannot operate on supernodal L") ;
273  return (FALSE) ;
274  }
275  if (L->n != A->nrow)
276  {
277  /* dimensions must agree */
278  ERROR (CHOLMOD_INVALID, "A and L dimensions do not match") ;
279  return (FALSE) ;
280  }
281  Common->status = CHOLMOD_OK ;
282 
283  /* ---------------------------------------------------------------------- */
284  /* allocate workspace */
285  /* ---------------------------------------------------------------------- */
286 
287  /* s = 2*nrow + (stype ? 0 : ncol) */
288  s = CHOLMOD(mult_size_t) (nrow, 2, &ok) ;
289  if (stype != 0)
290  {
291  s = CHOLMOD(add_size_t) (s, ncol, &ok) ;
292  }
293  if (!ok)
294  {
295  ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
296  return (FALSE) ;
297  }
298 
299  CHOLMOD(allocate_work) (nrow, s, 0, Common) ;
300  if (Common->status < CHOLMOD_OK)
301  {
302  return (FALSE) ; /* out of memory */
303  }
304  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
305 
306  /* ---------------------------------------------------------------------- */
307  /* get inputs */
308  /* ---------------------------------------------------------------------- */
309 
310  Ai = A->i ;
311  Ap = A->p ;
312  Anz = A->nz ;
313  apacked = A->packed ;
314  sorted = A->sorted ;
315 
316  Li = L->i ;
317  Lx = L->x ;
318  Lz = L->z ;
319  Lp = L->p ;
320  Lnz = L->nz ;
321  xtype = L->xtype ;
322 
323  /* If L is monotonic on input, then it can be packed or
324  * unpacked on output, depending on the pack input parameter. */
325 
326  /* cannot pack a non-monotonic matrix */
327  if (!(L->is_monotonic))
328  {
329  pack = FALSE ;
330  }
331 
332  ASSERT (L->nzmax >= (size_t) (Lp [L->n])) ;
333 
334  pdest = 0 ;
335 
336  PRINT1 (("\n\n===================== Resymbol pack %d Apacked %d\n",
337  pack, A->packed)) ;
338  ASSERT (CHOLMOD(dump_sparse) (A, "ReSymbol A:", Common) >= 0) ;
339  DEBUG (CHOLMOD(dump_factor) (L, "ReSymbol initial L (i, x):", Common)) ;
340 
341  /* ---------------------------------------------------------------------- */
342  /* get workspace */
343  /* ---------------------------------------------------------------------- */
344 
345  Flag = Common->Flag ; /* size nrow */
346  Head = Common->Head ; /* size nrow+1 */
347  Iwork = Common->Iwork ;
348  Link = Iwork ; /* size nrow (i/i/l) [ */
349  Lnz = Iwork + nrow ; /* size nrow (i/i/l), if L not packed */
350  Anext = Iwork + 2*((size_t) nrow) ; /* size ncol (i/i/l), unsym. only */
351  for (j = 0 ; j < nrow ; j++)
352  {
353  Link [j] = EMPTY ;
354  }
355 
356  /* use Lnz in L itself */
357  Lnz = L->nz ;
358  ASSERT (Lnz != NULL) ;
359 
360  /* ---------------------------------------------------------------------- */
361  /* for the unsymmetric case, queue each column of A (:,f) */
362  /* ---------------------------------------------------------------------- */
363 
364  /* place each column of the basis set on the link list corresponding to */
365  /* the smallest row index in that column */
366 
367  if (stype == 0)
368  {
369  use_fset = (fset != NULL) ;
370  if (use_fset)
371  {
372  nf = fsize ;
373  /* This is the only O(ncol) loop in cholmod_resymbol.
374  * It is required only to check the fset. */
375  for (j = 0 ; j < ncol ; j++)
376  {
377  Anext [j] = -2 ;
378  }
379  for (jj = 0 ; jj < nf ; jj++)
380  {
381  j = fset [jj] ;
382  if (j < 0 || j > ncol || Anext [j] != -2)
383  {
384  /* out-of-range or duplicate entry in fset */
385  ERROR (CHOLMOD_INVALID, "fset invalid") ;
386  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
387  return (FALSE) ;
388  }
389  /* flag column j as having been seen */
390  Anext [j] = EMPTY ;
391  }
392  /* the fset is now valid */
393  ASSERT (CHOLMOD(dump_perm) (fset, nf, ncol, "fset", Common)) ;
394  }
395  else
396  {
397  nf = ncol ;
398  }
399  for (jj = 0 ; jj < nf ; jj++)
400  {
401  j = (use_fset) ? (fset [jj]) : jj ;
402  /* column j is the fset; find the smallest row (if any) */
403  p = Ap [j] ;
404  pend = (apacked) ? (Ap [j+1]) : (p + Anz [j]) ;
405  if (pend > p)
406  {
407  k = Ai [p] ;
408  if (!sorted)
409  {
410  for ( ; p < pend ; p++)
411  {
412  k = MIN (k, Ai [p]) ;
413  }
414  }
415  /* place column j on link list k */
416  ASSERT (k >= 0 && k < nrow) ;
417  Anext [j] = Head [k] ;
418  Head [k] = j ;
419  }
420  }
421  }
422 
423  /* ---------------------------------------------------------------------- */
424  /* recompute symbolic LDL' factorization */
425  /* ---------------------------------------------------------------------- */
426 
427  for (k = 0 ; k < nrow ; k++)
428  {
429 
430 #ifndef NDEBUG
431  PRINT1 (("\n\n================== Initial column k = "ID"\n", k)) ;
432  for (p = Lp [k] ; p < Lp [k] + Lnz [k] ; p++)
433  {
434  PRINT1 ((" row: "ID" value: ", Li [p])) ;
435  PRINT1 (("\n")) ;
436  }
437  PRINT1 (("Recomputing LDL, column k = "ID"\n", k)) ;
438 #endif
439 
440  /* ------------------------------------------------------------------ */
441  /* compute column k of I+F*F' or I+A */
442  /* ------------------------------------------------------------------ */
443 
444  /* flag the diagonal entry */
445  mark = CHOLMOD(clear_flag) (Common) ;
446  Flag [k] = mark ;
447  PRINT1 ((" row: "ID" (diagonal)\n", k)) ;
448 
449  if (stype != 0)
450  {
451  /* merge column k of A into Flag (lower triangular part only) */
452  p = Ap [k] ;
453  pend = (apacked) ? (Ap [k+1]) : (p + Anz [k]) ;
454  for ( ; p < pend ; p++)
455  {
456  i = Ai [p] ;
457  if (i > k)
458  {
459  Flag [i] = mark ;
460  }
461  }
462  }
463  else
464  {
465  /* for each column j whos first row index is in row k */
466  for (j = Head [k] ; j != EMPTY ; j = Anext [j])
467  {
468  /* merge column j of A into Flag */
469  PRINT1 ((" ---- A column "ID"\n", j)) ;
470  p = Ap [j] ;
471  pend = (apacked) ? (Ap [j+1]) : (p + Anz [j]) ;
472  PRINT1 ((" length "ID" adding\n", pend-p)) ;
473  for ( ; p < pend ; p++)
474  {
475 #ifndef NDEBUG
476  ASSERT (Ai [p] >= k && Ai [p] < nrow) ;
477  if (Flag [Ai [p]] < mark) PRINT1 ((" row "ID"\n", Ai [p])) ;
478 #endif
479  Flag [Ai [p]] = mark ;
480  }
481  }
482  /* clear the kth link list */
483  Head [k] = EMPTY ;
484  }
485 
486  /* ------------------------------------------------------------------ */
487  /* compute pruned pattern of kth column of L = union of children */
488  /* ------------------------------------------------------------------ */
489 
490  /* for each column j of L whose parent is k */
491  for (j = Link [k] ; j != EMPTY ; j = Link [j])
492  {
493  /* merge column j of L into Flag */
494  PRINT1 ((" ---- L column "ID"\n", k)) ;
495  ASSERT (j < k) ;
496  ASSERT (Lnz [j] > 0) ;
497  p = Lp [j] ;
498  pend = p + Lnz [j] ;
499  ASSERT (Li [p] == j && Li [p+1] == k) ;
500  p++ ; /* skip past the diagonal entry */
501  for ( ; p < pend ; p++)
502  {
503  /* add to pattern */
504  ASSERT (Li [p] >= k && Li [p] < nrow) ;
505  Flag [Li [p]] = mark ;
506  }
507  }
508 
509  /* ------------------------------------------------------------------ */
510  /* prune the kth column of L */
511  /* ------------------------------------------------------------------ */
512 
513  PRINT1 (("Final column of L:\n")) ;
514  p = Lp [k] ;
515  pend = p + Lnz [k] ;
516 
517  if (pack)
518  {
519  /* shift column k upwards */
520  Lp [k] = pdest ;
521  }
522  else
523  {
524  /* leave column k in place, just reduce Lnz [k] */
525  pdest = p ;
526  }
527 
528  for ( ; p < pend ; p++)
529  {
530  ASSERT (pdest < pend) ;
531  ASSERT (pdest <= p) ;
532  row = Li [p] ;
533  ASSERT (row >= k && row < nrow) ;
534  if (Flag [row] == mark)
535  {
536  /* keep this entry */
537  Li [pdest] = row ;
538  if (xtype == CHOLMOD_REAL)
539  {
540  Lx [pdest] = Lx [p] ;
541  }
542  else if (xtype == CHOLMOD_COMPLEX)
543  {
544  Lx [2*pdest ] = Lx [2*p ] ;
545  Lx [2*pdest+1] = Lx [2*p+1] ;
546  }
547  else if (xtype == CHOLMOD_ZOMPLEX)
548  {
549  Lx [pdest] = Lx [p] ;
550  Lz [pdest] = Lz [p] ;
551  }
552  pdest++ ;
553  }
554  }
555 
556  /* ------------------------------------------------------------------ */
557  /* prepare this column for its parent */
558  /* ------------------------------------------------------------------ */
559 
560  Lnz [k] = pdest - Lp [k] ;
561 
562  PRINT1 ((" L("ID") length "ID"\n", k, Lnz [k])) ;
563  ASSERT (Lnz [k] > 0) ;
564 
565  /* parent is the first entry in the column after the diagonal */
566  parent = (Lnz [k] > 1) ? (Li [Lp [k] + 1]) : EMPTY ;
567 
568  PRINT1 (("parent ("ID") = "ID"\n", k, parent)) ;
569  ASSERT ((parent > k && parent < nrow) || (parent == EMPTY)) ;
570 
571  if (parent != EMPTY)
572  {
573  Link [k] = Link [parent] ;
574  Link [parent] = k ;
575  }
576  }
577 
578  /* done using Iwork for Link, Lnz (if needed), and Anext ] */
579 
580  /* ---------------------------------------------------------------------- */
581  /* convert L to packed, if requested */
582  /* ---------------------------------------------------------------------- */
583 
584  if (pack)
585  {
586  /* finalize Lp */
587  Lp [nrow] = pdest ;
588  /* Shrink L to be just large enough. It cannot fail. */
589  /* workspace: none */
590  ASSERT ((size_t) (Lp [nrow]) <= L->nzmax) ;
591  CHOLMOD(reallocate_factor) (Lp [nrow], L, Common) ;
592  ASSERT (Common->status >= CHOLMOD_OK) ;
593  }
594 
595  /* ---------------------------------------------------------------------- */
596  /* clear workspace */
597  /* ---------------------------------------------------------------------- */
598 
599  CHOLMOD(clear_flag) (Common) ;
600  DEBUG (CHOLMOD(dump_factor) (L, "ReSymbol final L (i, x):", Common)) ;
601  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
602  return (TRUE) ;
603 }
604 #endif
#define CHOLMOD_TOO_LARGE
int CHOLMOD() resymbol(cholmod_sparse *A, Int *fset, size_t fsize, int pack, cholmod_factor *L, cholmod_common *Common)
#define EMPTY
#define Int
size_t CHOLMOD() add_size_t(size_t a, size_t b, int *ok)
#define CHOLMOD_COMPLEX
#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() reallocate_factor(size_t nznew, cholmod_factor *L, cholmod_common *Common)
int CHOLMOD() dump_work(int flag, int head, UF_long wsize, cholmod_common *Common)
#define CHOLMOD_PATTERN
#define CHOLMOD(name)
cholmod_sparse *CHOLMOD() ptranspose(cholmod_sparse *A, int values, Int *Perm, Int *fset, size_t fsize, cholmod_common *Common)
int CHOLMOD() free_sparse(cholmod_sparse **AHandle, cholmod_common *Common)
#define CHOLMOD_REAL
#define NULL
#define ASSERT(expression)
#define ID
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)
int CHOLMOD() resymbol_noperm(cholmod_sparse *A, Int *fset, size_t fsize, int pack, cholmod_factor *L, cholmod_common *Common)
#define RETURN_IF_NULL(A, result)
#define DEBUG(statement)
#define MIN(a, b)
int CHOLMOD() dump_factor(cholmod_factor *L, char *name, cholmod_common *Common)
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
#define CHOLMOD_NATURAL