/* ========================================================================== */ /* === Cholesky/cholmod_rowfac ============================================== */ /* ========================================================================== */ /* ----------------------------------------------------------------------------- * CHOLMOD/Cholesky Module. Copyright (C) 2005-2006, Timothy A. Davis * The CHOLMOD/Cholesky Module is licensed under Version 2.1 of the GNU * Lesser General Public License. See lesser.txt for a text of the license. * CHOLMOD is also available under other licenses; contact authors for details. * http://www.cise.ufl.edu/research/sparse * -------------------------------------------------------------------------- */ /* Full or incremental numerical LDL' or LL' factorization (simplicial, not * supernodal) cholmod_factorize is the "easy" wrapper for this code, but it * does not provide access to incremental factorization. * * cholmod_rowfac computes the full or incremental LDL' or LL' factorization of * A+beta*I (where A is symmetric) or A*F+beta*I (where A and F are unsymmetric * and only the upper triangular part of A*F+beta*I is used). It computes * L (and D, for LDL') one row at a time. beta is real. * * A is nrow-by-ncol or nrow-by-nrow. In "packed" form it is a conventional * column-oriented sparse matrix. Row indices of column j are in * Ai [Ap [j] ... Ap [j+1]-1] and values in the same locations of Ax. * will be faster if A has sorted columns. In "unpacked" form the column * of A ends at Ap [j] + Anz [j] - 1 instead of Ap [j+1] - 1. * * Row indices in each column of A can be sorted or unsorted, but the routine * routine works fastest if A is sorted, or if only triu(A) is provided * for the symmetric case. * * The unit-diagonal nrow-by-nrow output matrix L is returned in "unpacked" * column form, with row indices of column j in Li [Lp [j] ... * Lp [j] + Lnz [j] - 1] and values in the same location in Lx. The row * indices in each column of L are in sorted order. The unit diagonal of L * is not stored. * * L can be a simplicial symbolic or numeric (L->is_super must be FALSE). * A symbolic factor is converted immediately into a numeric factor containing * the identity matrix. * * For a full factorization, kstart = 0 and kend = nrow. The existing nonzero * entries (numerical values in L->x and L->z for the zomplex case, and indices * in L->i), if any, are overwritten. * * To compute an incremental factorization, select kstart and kend as the range * of rows of L you wish to compute. A correct factorization will be computed * only if all descendants of all nodes k = kstart to kend-1 in the etree have * been factorized by a prior call to this routine, and if rows kstart to kend-1 * have not been factorized. This condition is NOT checked on input. * * --------------- * Symmetric case: * --------------- * * The factorization (in MATLAB notation) is: * * S = beta*I + A * S = triu (S) + triu (S,1)' * L*D*L' = S, or L*L' = S * * A is a conventional sparse matrix in compressed column form. Only the * diagonal and upper triangular part of A is accessed; the lower * triangular part is ignored and assumed to be equal to the upper * triangular part. For an incremental factorization, only columns kstart * to kend-1 of A are accessed. F is not used. * * --------------- * Unsymmetric case: * --------------- * * The factorization (in MATLAB notation) is: * * S = beta*I + A*F * S = triu (S) + triu (S,1)' * L*D*L' = S, or L*L' = S * * The typical case is F=A'. Alternatively, if F=A(:,f)', then this * routine factorizes S = beta*I + A(:,f)*A(:,f)'. * * All of A and F are accessed, but only the upper triangular part of A*F * is used. F must be of size A->ncol by A->nrow. F is used for the * unsymmetric case only. F can be packed or unpacked and it need not be * sorted. * * For a complete factorization of beta*I + A*A', * this routine performs a number of flops exactly equal to: * * sum (for each column j of A) of (Anz (j)^2 + Anz (j)), to form S * + * sum (for each column j of L) of (Lnz (j)^2 + 3*Lnz (j)), to factorize S * * where Anz (j) is the number of nonzeros in column j of A, and Lnz (j) * is the number of nonzero in column j of L below the diagonal. * * * workspace: Flag (nrow), W (nrow if real, 2*nrow if complex/zomplex), * Iwork (nrow) * * Supports any xtype, except a pattern-only input matrix A cannot be * factorized. */ #ifndef NCHOLESKY #include "cholmod_internal.h" #include "cholmod_cholesky.h" /* ========================================================================== */ /* === subtree ============================================================== */ /* ========================================================================== */ /* Compute the nonzero pattern of the sparse triangular solve Lx=b, where L in * this case is L(0:k-1,0:k-1), and b is a column of A. This is done by * traversing the kth row-subtree of the elimination tree of L, starting from * each nonzero entry in b. The pattern is returned postordered, and is valid * for a subsequent numerical triangular solve of Lx=b. The elimination tree * can be provided in a Parent array, or extracted from the pattern of L itself. * * The pattern of x = inv(L)*b is returned in Stack [top...]. * Also scatters b, or a multiple of b, into the work vector W. * * The SCATTER macro is defines how the numerical values of A or A*A' are to be * scattered. * * PARENT(i) is a macro the defines how the etree is accessed. It is either: * #define PARENT(i) Parent [i] * #define PARENT(i) (Lnz [i] > 1) ? (Li [Lp [i] + 1]) : EMPTY */ #define SUBTREE \ for ( ; p < pend ; p++) \ { \ i = Ai [p] ; \ if (i <= k) \ { \ /* scatter the column of A, or A*A' into Wx and Wz */ \ SCATTER ; \ /* start at node i and traverse up the subtree, stop at node k */ \ for (len = 0 ; i < k && i != EMPTY && Flag [i] < mark ; i = parent) \ { \ /* L(k,i) is nonzero, and seen for the first time */ \ Stack [len++] = i ; /* place i on the stack */ \ Flag [i] = mark ; /* mark i as visited */ \ parent = PARENT (i) ; /* traverse up the etree to the parent */ \ } \ /* move the path down to the bottom of the stack */ \ while (len > 0) \ { \ Stack [--top] = Stack [--len] ; \ } \ } \ else if (sorted) \ { \ break ; \ } \ } /* ========================================================================== */ /* === TEMPLATE ============================================================= */ /* ========================================================================== */ #define REAL #include "t_cholmod_rowfac.c" #define COMPLEX #include "t_cholmod_rowfac.c" #define ZOMPLEX #include "t_cholmod_rowfac.c" #define MASK #define REAL #include "t_cholmod_rowfac.c" #define COMPLEX #include "t_cholmod_rowfac.c" #define ZOMPLEX #include "t_cholmod_rowfac.c" #undef MASK /* ========================================================================== */ /* === cholmod_row_subtree ================================================== */ /* ========================================================================== */ /* Compute the nonzero pattern of the solution to the lower triangular system * L(0:k-1,0:k-1) * x = A (0:k-1,k) if A is symmetric, or * L(0:k-1,0:k-1) * x = A (0:k-1,:) * A (:,k)' if A is unsymmetric. * This gives the nonzero pattern of row k of L (excluding the diagonal). * The pattern is returned postordered. * * The symmetric case requires A to be in symmetric-upper form. * * The result is returned in R, a pre-allocated sparse matrix of size nrow-by-1, * with R->nzmax >= nrow. R is assumed to be packed (Rnz [0] is not updated); * the number of entries in R is given by Rp [0]. * * FUTURE WORK: a very minor change to this routine could allow it to compute * the nonzero pattern of x for any system Lx=b. The SUBTREE macro would need * to change, to eliminate its dependence on k. * * workspace: Flag (nrow) */ int CHOLMOD(row_subtree) ( /* ---- input ---- */ cholmod_sparse *A, /* matrix to analyze */ cholmod_sparse *F, /* used for A*A' case only. F=A' or A(:,f)' */ size_t krow, /* row k of L */ Int *Parent, /* elimination tree */ /* ---- output --- */ cholmod_sparse *R, /* pattern of L(k,:), 1-by-n with R->nzmax >= n */ /* --------------- */ cholmod_common *Common ) { Int *Rp, *Stack, *Flag, *Ap, *Ai, *Anz, *Fp, *Fi, *Fnz ; Int p, pend, parent, t, stype, nrow, k, pf, pfend, Fpacked, packed, sorted, top, len, i, mark ; /* ---------------------------------------------------------------------- */ /* check inputs */ /* ---------------------------------------------------------------------- */ RETURN_IF_NULL_COMMON (FALSE) ; RETURN_IF_NULL (A, FALSE) ; RETURN_IF_NULL (R, FALSE) ; RETURN_IF_NULL (Parent, FALSE) ; RETURN_IF_XTYPE_INVALID (A, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ; RETURN_IF_XTYPE_INVALID (R, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ; stype = A->stype ; if (stype == 0) { RETURN_IF_NULL (F, FALSE) ; RETURN_IF_XTYPE_INVALID (F, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ; } if (krow >= A->nrow) { ERROR (CHOLMOD_INVALID, "subtree: k invalid") ; return (FALSE) ; } if (R->ncol != 1 || A->nrow != R->nrow || A->nrow > R->nzmax) { ERROR (CHOLMOD_INVALID, "subtree: R invalid") ; return (FALSE) ; } Common->status = CHOLMOD_OK ; /* ---------------------------------------------------------------------- */ /* allocate workspace */ /* ---------------------------------------------------------------------- */ nrow = A->nrow ; CHOLMOD(allocate_work) (nrow, 0, 0, Common) ; if (Common->status < CHOLMOD_OK) { return (FALSE) ; } ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ; /* ---------------------------------------------------------------------- */ /* get inputs */ /* ---------------------------------------------------------------------- */ if (stype > 0) { /* symmetric upper case: F is not needed. It may be NULL */ Fp = NULL ; Fi = NULL ; Fnz = NULL ; Fpacked = TRUE ; } else if (stype == 0) { /* unsymmetric case: F is required. */ Fp = F->p ; Fi = F->i ; Fnz = F->nz ; Fpacked = F->packed ; } else { /* symmetric lower triangular form not supported */ ERROR (CHOLMOD_INVALID, "symmetric lower not supported") ; return (FALSE) ; } Ap = A->p ; Ai = A->i ; Anz = A->nz ; packed = A->packed ; sorted = A->sorted ; k = krow ; Stack = R->i ; /* ---------------------------------------------------------------------- */ /* get workspace */ /* ---------------------------------------------------------------------- */ Flag = Common->Flag ; /* size nrow, Flag [i] < mark must hold */ mark = CHOLMOD(clear_flag) (Common) ; /* ---------------------------------------------------------------------- */ /* compute the pattern of L(k,:) */ /* ---------------------------------------------------------------------- */ top = nrow ; /* Stack is empty */ Flag [k] = mark ; /* do not include diagonal entry in Stack */ #define SCATTER /* do not scatter numerical values */ #define PARENT(i) Parent [i] /* use Parent for etree */ if (stype != 0) { /* scatter kth col of triu (A), get pattern L(k,:) */ p = Ap [k] ; pend = (packed) ? (Ap [k+1]) : (p + Anz [k]) ; SUBTREE ; } else { /* scatter kth col of triu (beta*I+AA'), get pattern L(k,:) */ pf = Fp [k] ; pfend = (Fpacked) ? (Fp [k+1]) : (pf + Fnz [k]) ; for ( ; pf < pfend ; pf++) { /* get nonzero entry F (t,k) */ t = Fi [pf] ; p = Ap [t] ; pend = (packed) ? (Ap [t+1]) : (p + Anz [t]) ; SUBTREE ; } } #undef SCATTER #undef PARENT /* shift the stack upwards, to the first part of R */ len = nrow - top ; for (i = 0 ; i < len ; i++) { Stack [i] = Stack [top + i] ; } Rp = R->p ; Rp [0] = 0 ; Rp [1] = len ; R->sorted = FALSE ; CHOLMOD(clear_flag) (Common) ; ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ; return (TRUE) ; } /* ========================================================================== */ /* === cholmod_row_lsubtree ================================================= */ /* ========================================================================== */ /* Identical to cholmod_row_subtree, except that the elimination tree is * obtained from L itself, as the first off-diagonal entry in each column. * L must be simplicial, not supernodal */ int CHOLMOD(row_lsubtree) ( /* ---- input ---- */ cholmod_sparse *A, /* matrix to analyze */ Int *Fi, size_t fnz, /* nonzero pattern of kth row of A', not required * for the symmetric case. Need not be sorted. */ size_t krow, /* row k of L */ cholmod_factor *L, /* the factor L from which parent(i) is derived */ /* ---- output --- */ cholmod_sparse *R, /* pattern of L(k,:), 1-by-n with R->nzmax >= n */ /* --------------- */ cholmod_common *Common ) { Int *Rp, *Stack, *Flag, *Ap, *Ai, *Anz, *Lp, *Li, *Lnz ; Int p, pend, parent, t, stype, nrow, k, pf, packed, sorted, top, len, i, mark ; /* ---------------------------------------------------------------------- */ /* check inputs */ /* ---------------------------------------------------------------------- */ RETURN_IF_NULL_COMMON (FALSE) ; RETURN_IF_NULL (A, FALSE) ; RETURN_IF_NULL (R, FALSE) ; RETURN_IF_NULL (L, FALSE) ; RETURN_IF_XTYPE_INVALID (A, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ; RETURN_IF_XTYPE_INVALID (R, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ; RETURN_IF_XTYPE_INVALID (L, CHOLMOD_REAL, CHOLMOD_ZOMPLEX, FALSE) ; stype = A->stype ; if (stype == 0) { RETURN_IF_NULL (Fi, FALSE) ; } if (krow >= A->nrow) { ERROR (CHOLMOD_INVALID, "lsubtree: k invalid") ; return (FALSE) ; } if (R->ncol != 1 || A->nrow != R->nrow || A->nrow > R->nzmax) { ERROR (CHOLMOD_INVALID, "lsubtree: R invalid") ; return (FALSE) ; } if (L->is_super) { ERROR (CHOLMOD_INVALID, "lsubtree: L invalid (cannot be supernodal)") ; return (FALSE) ; } Common->status = CHOLMOD_OK ; /* ---------------------------------------------------------------------- */ /* allocate workspace */ /* ---------------------------------------------------------------------- */ nrow = A->nrow ; CHOLMOD(allocate_work) (nrow, 0, 0, Common) ; if (Common->status < CHOLMOD_OK) { return (FALSE) ; } ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ; /* ---------------------------------------------------------------------- */ /* get inputs */ /* ---------------------------------------------------------------------- */ if (stype < 0) { /* symmetric lower triangular form not supported */ ERROR (CHOLMOD_INVALID, "symmetric lower not supported") ; return (FALSE) ; } Ap = A->p ; Ai = A->i ; Anz = A->nz ; packed = A->packed ; sorted = A->sorted ; k = krow ; Stack = R->i ; Lp = L->p ; Li = L->i ; Lnz = L->nz ; /* ---------------------------------------------------------------------- */ /* get workspace */ /* ---------------------------------------------------------------------- */ Flag = Common->Flag ; /* size nrow, Flag [i] < mark must hold */ mark = CHOLMOD(clear_flag) (Common) ; /* ---------------------------------------------------------------------- */ /* compute the pattern of L(k,:) */ /* ---------------------------------------------------------------------- */ top = nrow ; /* Stack is empty */ Flag [k] = mark ; /* do not include diagonal entry in Stack */ #define SCATTER /* do not scatter numerical values */ #define PARENT(i) (Lnz [i] > 1) ? (Li [Lp [i] + 1]) : EMPTY if (stype != 0) { /* scatter kth col of triu (A), get pattern L(k,:) */ p = Ap [k] ; pend = (packed) ? (Ap [k+1]) : (p + Anz [k]) ; SUBTREE ; } else { /* scatter kth col of triu (beta*I+AA'), get pattern L(k,:) */ for (pf = 0 ; pf < (Int) fnz ; pf++) { /* get nonzero entry F (t,k) */ t = Fi [pf] ; p = Ap [t] ; pend = (packed) ? (Ap [t+1]) : (p + Anz [t]) ; SUBTREE ; } } #undef SCATTER #undef PARENT /* shift the stack upwards, to the first part of R */ len = nrow - top ; for (i = 0 ; i < len ; i++) { Stack [i] = Stack [top + i] ; } Rp = R->p ; Rp [0] = 0 ; Rp [1] = len ; R->sorted = FALSE ; CHOLMOD(clear_flag) (Common) ; ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ; return (TRUE) ; } /* ========================================================================== */ /* === cholmod_rowfac ======================================================= */ /* ========================================================================== */ /* This is the incremental factorization for general purpose usage. */ int CHOLMOD(rowfac) ( /* ---- input ---- */ cholmod_sparse *A, /* matrix to factorize */ cholmod_sparse *F, /* used for A*A' case only. F=A' or A(:,f)' */ double beta [2], /* factorize beta*I+A or beta*I+AA' */ size_t kstart, /* first row to factorize */ size_t kend, /* last row to factorize is kend-1 */ /* ---- in/out --- */ cholmod_factor *L, /* --------------- */ cholmod_common *Common ) { return (CHOLMOD(rowfac_mask) (A, F, beta, kstart, kend, NULL, NULL, L, Common)) ; } /* ========================================================================== */ /* === cholmod_rowfac_mask ================================================== */ /* ========================================================================== */ /* This is meant for use in LPDASA only. */ int CHOLMOD(rowfac_mask) ( /* ---- input ---- */ cholmod_sparse *A, /* matrix to factorize */ cholmod_sparse *F, /* used for A*A' case only. F=A' or A(:,f)' */ double beta [2], /* factorize beta*I+A or beta*I+AA' */ size_t kstart, /* first row to factorize */ size_t kend, /* last row to factorize is kend-1 */ Int *mask, /* size A->nrow. if mask[i] >= 0 row i is set to zero */ Int *RLinkUp, /* size A->nrow. link list of rows to compute */ /* ---- in/out --- */ cholmod_factor *L, /* --------------- */ cholmod_common *Common ) { Int n ; size_t s ; int ok = TRUE ; /* ---------------------------------------------------------------------- */ /* check inputs */ /* ---------------------------------------------------------------------- */ RETURN_IF_NULL_COMMON (FALSE) ; RETURN_IF_NULL (A, FALSE) ; RETURN_IF_NULL (L, FALSE) ; RETURN_IF_XTYPE_INVALID (A, CHOLMOD_REAL, CHOLMOD_ZOMPLEX, FALSE) ; RETURN_IF_XTYPE_INVALID (L, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ; if (L->xtype != CHOLMOD_PATTERN && A->xtype != L->xtype) { ERROR (CHOLMOD_INVALID, "xtype of A and L do not match") ; return (FALSE) ; } if (L->is_super) { ERROR (CHOLMOD_INVALID, "can only do simplicial factorization"); return (FALSE) ; } if (A->stype == 0) { RETURN_IF_NULL (F, FALSE) ; if (A->xtype != F->xtype) { ERROR (CHOLMOD_INVALID, "xtype of A and F do not match") ; return (FALSE) ; } } if (A->stype < 0) { /* symmetric lower triangular form not supported */ ERROR (CHOLMOD_INVALID, "symmetric lower not supported") ; return (FALSE) ; } if (kend > L->n) { ERROR (CHOLMOD_INVALID, "kend invalid") ; return (FALSE) ; } if (A->nrow != L->n) { ERROR (CHOLMOD_INVALID, "dimensions of A and L do not match") ; return (FALSE) ; } Common->status = CHOLMOD_OK ; Common->rowfacfl = 0 ; /* ---------------------------------------------------------------------- */ /* allocate workspace */ /* ---------------------------------------------------------------------- */ /* Xwork is of size n for the real case, 2*n for complex/zomplex */ n = L->n ; /* s = ((A->xtype != CHOLMOD_REAL) ? 2:1)*n */ s = CHOLMOD(mult_size_t) (n, ((A->xtype != CHOLMOD_REAL) ? 2:1), &ok) ; if (!ok) { ERROR (CHOLMOD_TOO_LARGE, "problem too large") ; return (FALSE) ; } CHOLMOD(allocate_work) (n, n, s, Common) ; if (Common->status < CHOLMOD_OK) { return (FALSE) ; } ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, A->nrow, Common)) ; /* ---------------------------------------------------------------------- */ /* factorize the matrix, using template routine */ /* ---------------------------------------------------------------------- */ if (RLinkUp == NULL) { switch (A->xtype) { case CHOLMOD_REAL: ok = r_cholmod_rowfac (A, F, beta, kstart, kend, L, Common) ; break ; case CHOLMOD_COMPLEX: ok = c_cholmod_rowfac (A, F, beta, kstart, kend, L, Common) ; break ; case CHOLMOD_ZOMPLEX: ok = z_cholmod_rowfac (A, F, beta, kstart, kend, L, Common) ; break ; } } else { switch (A->xtype) { case CHOLMOD_REAL: ok = r_cholmod_rowfac_mask (A, F, beta, kstart, kend, mask, RLinkUp, L, Common) ; break ; case CHOLMOD_COMPLEX: ok = c_cholmod_rowfac_mask (A, F, beta, kstart, kend, mask, RLinkUp, L, Common) ; break ; case CHOLMOD_ZOMPLEX: ok = z_cholmod_rowfac_mask (A, F, beta, kstart, kend, mask, RLinkUp, L, Common) ; break ; } } return (ok) ; } #endif