The R Project SVN R-packages

Rev

Rev 1820 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/* ========================================================================== */
/* === Modify/cholmod_rowdel ================================================ */
/* ========================================================================== */

/* -----------------------------------------------------------------------------
 * CHOLMOD/Modify Module.  Version 0.6.  Copyright (C) 2005, Timothy A. Davis
 * and William W. Hager.
 * The CHOLMOD/Modify Module is licensed under Version 2.0 of the GNU
 * General Public License.  See gpl.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
 * -------------------------------------------------------------------------- */

/* Deletes a row and column from an LDL' factorization.  The row and column k
 * is set to the kth row and column of the identity matrix.  Optionally
 * downdates the solution to Lx=b.
 *
 * workspace: Flag (nrow), Head (nrow+1), W (nrow*2), Iwork (2*nrow)
 *
 * Only real matrices are supported (exception: since only the pattern of R
 * is used, it can have any valid xtype).
 */

#ifndef NMODIFY

#include "cholmod_modify.h"
#include "cholmod_internal.h"


/* ========================================================================== */
/* === cholmod_rowdel ======================================================= */
/* ========================================================================== */

/* Sets the kth row and column of L to be the kth row and column of the identity
 * matrix, and updates L(k+1:n,k+1:n) accordingly.   To reduce the running time,
 * the caller can optionally provide the nonzero pattern (or an upper bound) of
 * kth row of L, as the sparse n-by-1 vector R.  Provide R as NULL if you want
 * CHOLMOD to determine this itself, which is easier for the caller, but takes
 * a little more time.
 */

int CHOLMOD(rowdel)
(
    /* ---- input ---- */
    size_t k,       /* row/column index to delete */
    cholmod_sparse *R,  /* NULL, or the nonzero pattern of kth row of L */
    /* ---- in/out --- */
    cholmod_factor *L,  /* factor to modify */
    /* --------------- */
    cholmod_common *Common
)
{
    double yk [2] ;
    yk [0] = 0. ;
    yk [1] = 0. ;
    return (CHOLMOD(rowdel_mark) (k, R, yk, NULL, L, NULL, NULL, NULL, Common));
}


/* ========================================================================== */
/* === cholmod_rowdel_solve ================================================= */
/* ========================================================================== */

/* Does the same as cholmod_rowdel, but also downdates the solution to Lx=b.
 * When row/column k of A is "deleted" from the system A*y=b, this can induce
 * a change to x, in addition to changes arising when L and b are modified.
 * If this is the case, the kth entry of y is required as input (yk) */

int CHOLMOD(rowdel_solve)
(
    /* ---- input ---- */
    size_t k,       /* row/column index to delete */
    cholmod_sparse *R,  /* NULL, or the nonzero pattern of kth row of L */
    double yk [2],  /* kth entry in the solution to A*y=b */
    /* ---- in/out --- */
    cholmod_factor *L,  /* factor to modify */
    cholmod_dense *X,   /* solution to Lx=b (size n-by-1) */
    cholmod_dense *DeltaB,  /* change in b, zero on output */
    /* --------------- */
    cholmod_common *Common
)
{
    return (CHOLMOD(rowdel_mark) (k, R, yk, NULL, L, X, DeltaB, NULL, Common)) ;
}


/* ========================================================================== */
/* === cholmod_rowdel_mark ================================================== */
/* ========================================================================== */

/* Does the same as cholmod_rowdel_solve, except only part of L is used in
 * the update/downdate of the solution to Lx=b.  This routine is an "expert"
 * routine.  It is meant for use in LPDASA only.
 *
 * if R == NULL then columns 0:k-1 of L are searched for row k.  Otherwise, it
 * searches columns in the set defined by the pattern of the first column of R.
 * This is meant to be the pattern of row k of L (a superset of that pattern is
 * OK too).  R must be a permutation of a subset of 0:k-1.
 */

int CHOLMOD(rowdel_mark)
(
    /* ---- input ---- */
    size_t kdel,    /* row/column index to delete */
    cholmod_sparse *R,  /* NULL, or the nonzero pattern of kth row of L */
    double yk [2],  /* kth entry in the solution to A*y=b */
    Int *colmark,   /* Int array of size n.  See cholmod_updown.c */
    /* ---- in/out --- */
    cholmod_factor *L,  /* factor to modify */
    cholmod_dense *X,   /* solution to Lx=b (size n-by-1) */
    cholmod_dense *DeltaB,  /* change in b, zero on output */
    Int *rowmark,   /* Int array of size n.  See cholmod_updown.c */
    /* --------------- */
    cholmod_common *Common
)
{
    double dk, sqrt_dk, xk, dj ;
    double *Lx, *Cx, *W, *Xx, *Nx ;
    Int *Li, *Lp, *Lnz, *Ci, *Rj, *Rp, *Iwork ;
    cholmod_sparse *C, Cmatrix ;
    Int j, p, pend, kk, lnz, n, Cp [2], do_solve, do_update, left, k,
    right, middle, i, klast, given_row, rnz, ok ;

    /* ---------------------------------------------------------------------- */
    /* check inputs */
    /* ---------------------------------------------------------------------- */

    RETURN_IF_NULL_COMMON (FALSE) ;
    RETURN_IF_NULL (L, FALSE) ;
    RETURN_IF_XTYPE_INVALID (L, CHOLMOD_PATTERN, CHOLMOD_REAL, FALSE) ;
    n = L->n ;
    k = kdel ;
    if (k >= n || k < 0)
    {
    ERROR (CHOLMOD_INVALID, "k invalid") ;
    return (FALSE) ;
    }
    if (R == NULL)
    {
    Rj = NULL ;
    rnz = EMPTY ;
    }
    else
    {
    RETURN_IF_XTYPE_INVALID (R, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ;
    if (R->ncol != 1 || R->nrow != L->n)
    {
        ERROR (CHOLMOD_INVALID, "R invalid") ;
        return (FALSE) ;
    }
    Rj = R->i ;
    Rp = R->p ;
    rnz = Rp [1] ;
    }
    do_solve = (X != NULL) && (DeltaB != NULL) ;
    if (do_solve)
    {
    RETURN_IF_XTYPE_INVALID (X, CHOLMOD_REAL, CHOLMOD_REAL, FALSE) ;
    RETURN_IF_XTYPE_INVALID (DeltaB, CHOLMOD_REAL, CHOLMOD_REAL, FALSE) ;
    Xx = X->x ;
    Nx = DeltaB->x ;
    if (X->nrow != L->n || X->ncol != 1 || DeltaB->nrow != L->n ||
        DeltaB->ncol != 1 || Xx == NULL || Nx == NULL)
    {
        ERROR (CHOLMOD_INVALID, "X and/or DeltaB invalid") ;
        return (FALSE) ;
    }
    }
    else
    {
    Xx = NULL ;
    Nx = NULL ;
    }
    Common->status = CHOLMOD_OK ;

    /* ---------------------------------------------------------------------- */
    /* allocate workspace */
    /* ---------------------------------------------------------------------- */

    CHOLMOD(allocate_work) (n, 2*n, 2*n, Common) ;
    if (Common->status < CHOLMOD_OK)
    {
    return (FALSE) ;
    }
    ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 2*n, Common)) ;

    /* ---------------------------------------------------------------------- */
    /* convert to simplicial numeric LDL' factor, if not already */
    /* ---------------------------------------------------------------------- */

    if (L->xtype == CHOLMOD_PATTERN || L->is_super || L->is_ll) 
    {
    /* can only update/downdate a simplicial LDL' factorization */
    CHOLMOD(change_factor) (CHOLMOD_REAL, FALSE, FALSE, FALSE, FALSE, L,
        Common) ;
    if (Common->status < CHOLMOD_OK)
    {
        /* out of memory, L is returned unchanged */
        return (FALSE) ;
    }
    }

    /* ---------------------------------------------------------------------- */
    /* get inputs */
    /* ---------------------------------------------------------------------- */

    /* inputs, not modified on output: */
    Lp = L->p ;     /* size n+1 */

    /* outputs, contents defined on input for incremental case only: */
    Lnz = L->nz ;   /* size n */
    Li = L->i ;     /* size L->nzmax.  Can change in size. */
    Lx = L->x ;     /* size L->nzmax.  Can change in size. */

    ASSERT (L->nz != NULL) ;

    /* ---------------------------------------------------------------------- */
    /* get workspace */
    /* ---------------------------------------------------------------------- */

    W = Common->Xwork ;     /* size n, used only in cholmod_updown */
    Cx = W + n ;        /* use 2nd column of Xwork for C (size n) */
    Iwork = Common->Iwork ;
    Ci = Iwork + n ;        /* size n (i/i/l) */
    /* NOTE: cholmod_updown uses Iwork [0..n-1] (i/i/l) as Stack */

    /* ---------------------------------------------------------------------- */
    /* prune row k from all columns of L */
    /* ---------------------------------------------------------------------- */

    given_row = (rnz >= 0) ;
    klast = given_row ? rnz : k ;
    PRINT2 (("given_row "ID"\n", given_row)) ;

    for (kk = 0 ; kk < klast ; kk++)
    {
    /* either search j = 0:k-1 or j = Rj [0:rnz-1] */
    j = given_row ? (Rj [kk]) : (kk) ;

    if (j < 0 || j >= k)
    {
        ERROR (CHOLMOD_INVALID, "R invalid") ;
        return (FALSE) ;
    }

    PRINT2 (("Prune col j = "ID":\n", j)) ;

    lnz = Lnz [j] ;
    dj = Lx [Lp [j]] ;
    ASSERT (Lnz [j] > 0 && Li [Lp [j]] == j) ;

    if (lnz > 1)
    {
        left = Lp [j] ;
        pend = left + lnz ;
        right = pend - 1 ;

        i = Li [right] ;

        if (i < k)
        {
        /* row k is not in column j */
        continue ;
        }
        else if (i == k)
        {
        /* k is the last row index in this column (quick delete) */
        if (do_solve)
        {
            Xx [j] -= yk [0] * dj * Lx [right] ;
        }
        Lnz [j]-- ;
        }
        else
        {
        /* binary search for row k in column j */
        PRINT2 (("\nBinary search: lnz "ID" k = "ID"\n", lnz, k)) ;
        while (left < right)
        {
            middle = (left + right) / 2 ;
            PRINT2 (("left "ID" right "ID" middle "ID": ["ID" "ID""
            ""ID"]\n", left, right, middle,
            Li [left], Li [middle], Li [right])) ;
            if (k > Li [middle])
            {
            left = middle + 1 ;
            }
            else
            {
            right = middle ;
            }
        }
        ASSERT (left >= Lp [j] && left < pend) ;

#ifndef NDEBUG
        /* brute force, linear-time search */
        {
            Int p3 = Lp [j] ;
            i = EMPTY ;
            PRINT2 (("Brute force:\n")) ;
            for ( ; p3 < pend ; p3++)
            {
            i = Li [p3] ;
            PRINT2 (("p "ID" ["ID"]\n", p3, i)) ;
            if (i >= k)
            {
                break ;
            }
            }
            if (i == k)
            {
            ASSERT (k == Li [p3]) ;
            ASSERT (p3 == left) ;
            }
        }
#endif

        if (k == Li [left])
        {
            if (do_solve)
            {
            Xx [j] -= yk [0] * dj * Lx [left] ;
            }
            /* found row k in column j.  Prune it from the column.*/
            for (p = left + 1 ; p < pend ; p++)
            {
            Li [p-1] = Li [p] ;
            Lx [p-1] = Lx [p] ;
            }
            Lnz [j]-- ;
        }
        }
    }
    }

#ifndef NDEBUG
    /* ensure that row k has been deleted from the matrix L */
    for (j = 0 ; j < k ; j++)
    {
    Int lasti ;
    lasti = EMPTY ;
    p = Lp [j] ;
    pend = p + Lnz [j] ;
    /* look for row k in column j */
    PRINT1 (("Pruned column "ID"\n", j)) ;
    for ( ; p < pend ; p++)
    {
        i = Li [p] ;
        PRINT2 ((" "ID"", i)) ;
        ASSERT (i != k) ;
        ASSERT (i > lasti) ;
        lasti = i ;
    }
    PRINT2 (("\n")) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* set diagonal and clear column k of L */
    /* ---------------------------------------------------------------------- */

    lnz = Lnz [k] - 1 ;
    ASSERT (Lnz [k] > 0) ;

    /* ---------------------------------------------------------------------- */
    /* update/downdate */
    /* ---------------------------------------------------------------------- */

    /* update or downdate L (k+1:n, k+1:n) with the vector
     * C = L (:,k) * sqrt (abs (D [k]))
     * Do a numeric update if D[k] > 0, numeric downdate otherwise.
     */

    PRINT1 (("rowdel downdate lnz = "ID"\n", lnz)) ;

    /* store the new unit diagonal */
    p = Lp [k] ;
    pend = p + lnz + 1 ;
    dk = Lx [p] ;
    Lx [p++] = 1 ;
    PRINT2 (("D [k = "ID"] = %g\n", k, dk)) ;
    ok = TRUE ;

    if (lnz > 0)
    {
    /* compute DeltaB for updown (in DeltaB) */
    if (do_solve)
    {
        xk = Xx [k] - yk [0] * dk ;
        for ( ; p < pend ; p++)
        {
        Nx [Li [p]] += Lx [p] * xk ;
        }
    }

    do_update = IS_GT_ZERO (dk) ;
    if (!do_update)
    {
        dk = -dk ;
    }
    sqrt_dk = sqrt (dk) ;
    p = Lp [k] + 1 ;
    for (kk = 0 ; kk < lnz ; kk++, p++)
    {
        Ci [kk] = Li [p] ;
        Cx [kk] = Lx [p] * sqrt_dk ;
    }
    Common->modfl += lnz + 1 ;

    /* create a n-by-1 sparse matrix to hold the single column */
    C = &Cmatrix ;
    C->nrow = n ;
    C->ncol = 1 ;
    C->nzmax = lnz ;
    C->sorted = TRUE ;
    C->packed = TRUE ;
    C->p = Cp ;
    C->i = Ci ;
    C->x = Cx ;
    C->nz = NULL ;
    C->itype = L->itype ;
    C->xtype = L->xtype ;
    C->dtype = L->dtype ;
    C->z = NULL ;
    C->stype = 0 ;

    Cp [0] = 0 ;
    Cp [1] = lnz ;

    /* numeric update if dk > 0, and with Lx=b change */
    /* workspace: Flag (nrow), Head (nrow+1), W (nrow), Iwork (2*nrow) */
    ok = CHOLMOD(updown_mark) (do_update ? (1) : (0), C, colmark,
        L, X, DeltaB, rowmark, Common) ;
    if (ok)
    {
        Lnz [k] = 1 ;
    }

    /* clear workspace */
    for (kk = 0 ; kk < lnz ; kk++)
    {
        Cx [kk] = 0 ;
    }
    }

    if (do_solve)
    {
    /* kth equation becomes identity, so X(k) is now Y(k) */
    Xx [k] = yk [0] ;
    }

    DEBUG (CHOLMOD(dump_factor) (L, "LDL factorization, L:", Common)) ;
    ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 2*n, Common)) ;
    return (ok) ;
}
#endif