//------------------------------------------------------------------------------ // CHOLMOD/Tcov/t_lpdemo: test program with an LP-style operations in CHOLMOD //------------------------------------------------------------------------------ // CHOLMOD/Tcov Module. Copyright (C) 2005-2023, Timothy A. Davis. // All Rights Reserved. // SPDX-License-Identifier: GPL-2.0+ //------------------------------------------------------------------------------ // A rectangular matrix is being tested (# nrows < # cols). This is a // linear programming problem. Process the system using the same kind of // operations that occur in an LP solver (the LP Dual Active Set Algorithm). // This routine does not actually solve the LP. It simply mimics the kind // of matrix operations that occur in LPDASA. // // The active set f is held in fset [0..fsize-1]. It is a subset of the columns // of A. Columns not in the fset are in the list fnot [0..ncol-fsize-1]. // // Rows can be added and deleted from A as well. A "dead" row is one that has // been (temporarily) set to zero in A. If row i is dead, rflag [i] is 0, // and 1 otherwise. // // The list r of "live" rows is kept in rset [0..rsize-1]. The list of "dead" // rows is kept in rnot [0..nrow-rsize-1]. // // The system to solve as r and/or f change is (beta*I + A(r,f)*A(r,f)') x = b. // If a row i is deleted from A, it is set to zero. Row i of L and D are set // to the ith row of the identity matrix. #include "cm.h" #define MAXCOLS 8 //------------------------------------------------------------------------------ // Lcheck //------------------------------------------------------------------------------ // Testing only: make sure there are no dead rows in L (excluding diagonal) static void Lcheck (cholmod_factor *L, Int *rflag) { Int *Lp, *Li, *Lnz ; Int i, n, j, p, pend ; Real *Lx ; if (L == NULL) { return ; } Lp = L->p ; Li = L->i ; Lx = L->x ; Lnz = L->nz ; n = L->n ; for (j = 0 ; j < n ; j++) { p = Lp [j] ; pend = p + Lnz [j] ; for (p++ ; p < pend ; p++) { i = Li [p] ; OK (IMPLIES (!rflag [i], Lx [p] == 0)) ; } } } //------------------------------------------------------------------------------ // lp_prune //------------------------------------------------------------------------------ // C = A (r,f), except that C and A have the same row dimension. Row i of C // and A(:,f) are equal if row i is in the rset. Row i of C is zero // otherwise. C has as many columns as the size of f. cholmod_sparse *lp_prune ( cholmod_sparse *A, Int *rflag, Int *fset, Int fsize ) { cholmod_sparse *C ; Real *Ax, *Cx ; Int *Ai, *Ap, *Ci, *Cp ; Int i, kk, j, p, nz, nf, ncol ; if (A == NULL) { ERROR (CHOLMOD_INVALID, "nothing to prune") ; return (NULL) ; } Ap = A->p ; Ai = A->i ; Ax = A->x ; ncol = A->ncol ; nf = (fset == NULL) ? ncol : fsize ; OK (fsize >= 0) ; C = CHOLMOD(allocate_sparse) (A->nrow, nf, A->nzmax, A->sorted, TRUE, 0, CHOLMOD_REAL + DTYPE, cm) ; if (C == NULL) { ERROR (CHOLMOD_INVALID, "cannot create pruned C") ; return (NULL) ; } Cp = C->p ; Ci = C->i ; Cx = C->x ; nz = 0 ; for (kk = 0 ; kk < nf ; kk++) { j = (fset == NULL) ? (kk) : (fset [kk]) ; Cp [kk] = nz ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; if (rflag [i]) { Ci [nz] = i ; Cx [nz] = Ax [p] ; nz++ ; } } } Cp [nf] = nz ; return (C) ; } //------------------------------------------------------------------------------ // lp_resid //------------------------------------------------------------------------------ // Compute the 2-norm of the residual. // norm ((beta*I + C*C')y(r) - b(r)), where C = A (r,f). double lp_resid ( cholmod_sparse *A, Int *rflag, Int *fset, Int fsize, double beta [2], cholmod_dense *Y, cholmod_dense *B ) { cholmod_dense *R ; Real *Rx, *Yx ; double rnorm, bnorm, ynorm, norm ; cholmod_sparse *C ; cholmod_dense *W ; Int i, nrow ; if (A == NULL) { ERROR (CHOLMOD_INVALID, "cannot compute LP resid") ; return (1) ; } nrow = A->nrow ; R = CHOLMOD(zeros) (nrow, 1, CHOLMOD_REAL + DTYPE, cm) ; // C = A(r,f). In LPDASA, we do this in place, without making a copy. C = lp_prune (A, rflag, fset, fsize) ; // W = C'*Y OK (fsize >= 0) ; W = CHOLMOD(zeros) (fsize, 1, CHOLMOD_REAL + DTYPE, cm) ; CHOLMOD(sdmult) (C, TRUE, one, zero, Y, W, cm) ; // R = B CHOLMOD(copy_dense2) (B, R, cm) ; // R = C*W - R CHOLMOD(sdmult) (C, FALSE, one, minusone, W, R, cm) ; // R = R + beta*Y, (beta = 1 for dropped rows) if (R != NULL && Y != NULL) { Rx = R->x ; Yx = Y->x ; for (i = 0 ; i < nrow ; i++) { if (rflag [i]) { Rx [i] += beta [0] * Yx [i] ; } else { Rx [i] += Yx [i] ; } } } // rnorm = norm (R) rnorm = CHOLMOD(norm_dense) (R, 2, cm) ; bnorm = CHOLMOD(norm_dense) (B, 2, cm) ; ynorm = CHOLMOD(norm_dense) (Y, 2, cm) ; norm = MAX (bnorm, ynorm) ; if (norm > 0) { rnorm /= norm ; } CHOLMOD(print_dense) (R, "R, resid", cm) ; CHOLMOD(free_sparse) (&C, cm) ; CHOLMOD(free_dense) (&W, cm) ; CHOLMOD(free_dense) (&R, cm) ; return (rnorm) ; } //------------------------------------------------------------------------------ // get_row //------------------------------------------------------------------------------ // S = column i of beta*I + A(r,f)*A(r,f)' cholmod_sparse *get_row ( cholmod_sparse *A, Int i, Int *rflag, Int *fset, Int fsize, double beta [2] ) { cholmod_sparse *Ri, *R, *C, *S ; Real *Sx ; Int *Sp, *Si ; Int p, ii, found ; if (rflag [i] == 0) { S = CHOLMOD(speye) (A->nrow, A->nrow, CHOLMOD_REAL + DTYPE, cm) ; CHOLMOD(print_sparse) (S, "S identity", cm) ; return (S) ; } OK (fsize >= 0) ; // Getting row i of A is expensive. In LPDASA, we maintain // a copy of A(r,f)', and extact row i as column i of that // matrix. We compute S = A(r,f)*A(i,f)' and S(i) += beta // in a single pass. This is a simpler but slower method. // R = A (i,f)' Ri = CHOLMOD(submatrix) (A, &i, 1, fset, fsize, TRUE, FALSE, cm) ; R = CHOLMOD(transpose) (Ri, 1, cm) ; CHOLMOD(free_sparse) (&Ri, cm) ; // C = A (r,f) C = lp_prune (A, rflag, fset, fsize) ; // S = C*R S = CHOLMOD(ssmult) (C, R, 0, TRUE, TRUE, cm) ; CHOLMOD(free_sparse) (&C, cm) ; CHOLMOD(free_sparse) (&R, cm) ; if (S == NULL) { return (NULL) ; } // S (i) += beta found = FALSE ; Sp = S->p ; Si = S->i ; Sx = S->x ; for (p = Sp [0] ; p < Sp [1] ; p++) { ii = Si [p] ; if (ii == i) { found = TRUE ; Sx [p] += beta [0] ; break ; } } if (!found) { // oops, row index i is not present in S. Add it. CHOLMOD(reallocate_sparse) (S->nzmax+1, S, cm) ; OK (Sp [1] < (Int) (S->nzmax)) ; Si = S->i ; Sx = S->x ; Si [Sp [1]] = i ; Sx [Sp [1]] = beta [0] ; Sp [1]++ ; S->sorted = FALSE ; } CHOLMOD(print_sparse) (S, "S", cm) ; return (S) ; } //------------------------------------------------------------------------------ // lpdemo //------------------------------------------------------------------------------ double lpdemo (cholmod_triplet *T) { double r, maxerr = 0, anorm, bnorm, norm, xnorm, ynorm ; Real *b = NULL, *Yx = NULL, *Xx = NULL, *Sx ; cholmod_sparse *A, *AT, *Apermuted, *C, *S, *Row ; cholmod_dense *X, *B, *Y, *DeltaB, *R ; cholmod_factor *L ; Int *init, *rset, *rnot, *fset, *fnot, *rflag, *P, *Pinv, *Lperm, *fflag, *Sp, *Si, *StaticParent ; Int i, j, k, nrow, ncol, fsize, cols [MAXCOLS+1], trial, rank, kk, rsize, p, op, ok ; double beta [2], bk [2], yk [2] ; //-------------------------------------------------------------------------- // convert T into a sparse matrix A //-------------------------------------------------------------------------- if (T == NULL || T->ncol == 0) { // nothing to do return (0) ; } if (T->xtype != CHOLMOD_REAL) { return (0) ; } A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ; if (A == NULL) { ERROR (CHOLMOD_INVALID, "cannot continue LP demo") ; return (1) ; } nrow = A->nrow ; ncol = A->ncol ; anorm = CHOLMOD(norm_sparse) (A, 1, cm) ; // switch for afiro, but not galenet cm->supernodal_switch = 5 ; //-------------------------------------------------------------------------- // select a random initial row and column basis //-------------------------------------------------------------------------- // select an initial fset of size nrow init = prand (ncol) ; // RAND CHOLMOD(print_perm) (init, ncol, ncol, "init", cm) ; fset = CHOLMOD(malloc) (ncol, sizeof (Int), cm) ; fnot = CHOLMOD(malloc) (ncol, sizeof (Int), cm) ; fflag = CHOLMOD(malloc) (ncol, sizeof (Int), cm) ; fsize = MIN (nrow, ncol) ; if (init != NULL && fset != NULL && fflag != NULL) { for (k = 0 ; k < fsize ; k++) { j = init [k] ; fset [k] = j ; fflag [j] = 1 ; } for ( ; k < ncol ; k++) { j = init [k] ; fnot [k-fsize] = j ; fflag [j] = 0 ; } } CHOLMOD(free) (ncol, sizeof (Int), init, cm) ; // all rows are live rsize = nrow ; rflag = CHOLMOD(malloc) (nrow, sizeof (Int), cm) ; rset = CHOLMOD(malloc) (nrow, sizeof (Int), cm) ; rnot = CHOLMOD(malloc) (nrow, sizeof (Int), cm) ; if (rset != NULL && rflag != NULL) { for (i = 0 ; i < nrow ; i++) { rflag [i] = 1 ; rset [i] = i ; } } //-------------------------------------------------------------------------- // factorize the first matrix, beta*I + A(p,f)*A(p,f)' //-------------------------------------------------------------------------- #ifdef DOUBLE beta [0] = 1e-6 ; #else beta [0] = 0.1 ; #endif beta [1] = 0 ; // Need to prune entries due to relaxed amalgamation, or else // cholmod_row_subtree will not be able to find all the entries in row // k of L. cm->final_resymbol = TRUE ; cm->final_asis = FALSE ; cm->final_super = FALSE ; cm->final_ll = FALSE ; cm->final_pack = FALSE ; cm->final_monotonic = FALSE ; L = CHOLMOD(analyze_p) (A, NULL, fset, fsize, cm) ; CHOLMOD(factorize_p) (A, beta, fset, fsize, L, cm) ; // get a copy of the fill-reducing permutation P and compute its inverse Lperm = (L != NULL) ? (L->Perm) : NULL ; P = CHOLMOD(malloc) (nrow, sizeof (Int), cm) ; Pinv = CHOLMOD(malloc) (nrow, sizeof (Int), cm) ; if (P != NULL && Pinv != NULL && Lperm != NULL) { for (k = 0 ; k < nrow ; k++) { P [k] = Lperm [k] ; Pinv [P [k]] = k ; } } else { P = CHOLMOD(free) (nrow, sizeof (Int), P, cm) ; Pinv = CHOLMOD(free) (nrow, sizeof (Int), Pinv, cm) ; } if (cm->print > 1) { k = cm->print ; cm->print = 5 ; CHOLMOD(print_common) ("5:cm for lpdemo", cm) ; cm->print = k ; } //-------------------------------------------------------------------------- // A=P*A: permute the rows of A according to P //-------------------------------------------------------------------------- // This is done just once, since the system will be solved and modified // many times. It's faster, and easier, to work in the permuted ordering // rather than the original ordering. // A will become unsorted later on; don't bother to sort it here Apermuted = CHOLMOD(submatrix) (A, P, nrow, NULL, -1, TRUE, TRUE, cm) ; CHOLMOD(free_sparse) (&A, cm) ; A = Apermuted ; //-------------------------------------------------------------------------- // find the etree of A*A' //-------------------------------------------------------------------------- // Since the fset is a subset of 0:ncol-1, and rset is a subset of 0:nrow-1, // the nonzero pattern of the Cholesky factorization of A(r,f)*A(r,f)' is a // subset of the Cholesky factorization of A*A'. After many updates/ // downdates/rowadds/rowdels, any given row i of L may have entries that // are not in the factorization of A (r,f)*A(r,f)'. To drop a row using // cholmod_rowdel, we either need to know the pattern of the ith row of L, // we can pass NULL and have cholmod_rowdel look at each column 0 to i-1. // The StaticParent array is the etree of A*A', and it suffices to compute // the pattern of the ith row of L based on that etree, and A and A' // (ignoring the fset and rset). This gives us an upper bound on the // nonzero pattern of the ith row of the current L (the factorization // of A(r,f)*A(r,f)'. // AT = nonzero pattern of A', used for row-subtree computations AT = CHOLMOD(transpose) (A, 0, cm) ; // Row = cholmod_row_subtree workspace (unsorted, packed, unsym, pattern) Row = CHOLMOD(allocate_sparse) (nrow, 1, nrow, FALSE, TRUE, 0, CHOLMOD_PATTERN + DTYPE, cm) ; // Compute the "static" etree; the etree of A*A' StaticParent = CHOLMOD(malloc) (nrow, sizeof (Int), cm) ; CHOLMOD(etree) (AT, StaticParent, cm) ; //-------------------------------------------------------------------------- // compute initial right-hand-side //-------------------------------------------------------------------------- // If row i of the original A and B is row k of the permuted P*A and P*B, // then P [k] = i and Pinv [i] = k. Row indices of A now refer to the // permuted form of A, not the original A. Likewise, row k of B will // refer to the permuted row k = Pinv [i], not the original row i. In a // real program, this would affect how B is computed. This program just // creates a random B anyway, so the order of B does not matter. It does // use Pinv [i], just to show you how you would do it. B = CHOLMOD(zeros) (nrow, 1, CHOLMOD_REAL + DTYPE, cm) ; if (B != NULL && Pinv != NULL) { b = B->x ; for (i = 0 ; i < nrow ; i++) { // row i of the original B is row k of the permuted B k = Pinv [i] ; b [k] = xrand (1.) ; // RAND } } //-------------------------------------------------------------------------- // solve the system //-------------------------------------------------------------------------- // Solve the system (beta*I + A(:,f)*A(:,f)')y=b without using L->Perm, // since A and B have already been permuted according to L->Perm. DeltaB = CHOLMOD(zeros) (nrow, 1, CHOLMOD_REAL + DTYPE, cm) ; // solve Lx=b X = CHOLMOD(solve) (CHOLMOD_L, L, B, cm) ; // solve DL'y=x Y = CHOLMOD(solve) (CHOLMOD_DLt, L, X, cm) ; r = lp_resid (A, rflag, fset, fsize, beta, Y, B) ; MAXERR (maxerr, r, 1) ; bk [0] = 0 ; bk [1] = 0 ; yk [0] = 0 ; yk [1] = 0 ; bnorm = CHOLMOD(norm_dense) (B, 1, cm) ; //-------------------------------------------------------------------------- // modify the system //-------------------------------------------------------------------------- ok = (fset != NULL && fnot != NULL && fflag != NULL && rset != NULL && rnot != NULL && rflag != NULL && B != NULL && Y != NULL && X != NULL && Row != NULL && A != NULL && AT != NULL && StaticParent != NULL && DeltaB != NULL && L != NULL && L->xtype != CHOLMOD_PATTERN && !(L->is_ll) && !(L->is_super)) ; for (trial = 1 ; ok && trial < MAX (64, 2*ncol) ; trial++) { // select an operation at random op = nrand (6) ; // RAND Xx = X->x ; Yx = Y->x ; switch (op) { //------------------------------------------------------------------ case 0: // update //------------------------------------------------------------------ // pick some columns at random, but not all columns rank = 1 + nrand (MAXCOLS+4) ; // RAND rank = MIN (rank, MAXCOLS) ; rank = MIN (rank, ncol-fsize-1) ; if (rank <= 0) { continue ; } // remove the columns from fnot and add them to fset for (k = 0 ; k < rank ; k++) { kk = nrand (ncol-fsize) ; // RAND j = fnot [kk] ; fnot [kk] = fnot [ncol-fsize-1] ; fset [fsize++] = j ; OK (fsize < ncol) ; cols [k] = j ; fflag [j] = 1 ; } // update L, and the solution to Lx=b+deltaB C = lp_prune (A, rflag, cols, rank) ; ok = CHOLMOD(updown_solve) (TRUE, C, L, X, DeltaB, cm) ; CHOLMOD(free_sparse) (&C, cm) ; break ; //------------------------------------------------------------------ case 1: // downdate //------------------------------------------------------------------ // pick some columns at random, but not all columns rank = 1 + nrand (MAXCOLS+4) ; // RAND rank = MIN (rank, MAXCOLS) ; rank = MIN (rank, fsize-1) ; if (rank <= 0) { continue ; } // remove the columns from fset and add them to fnot for (k = 0 ; k < rank ; k++) { kk = nrand (fsize) ; // RAND j = fset [kk] ; fset [kk] = fset [fsize-1] ; fnot [ncol-fsize] = j ; fsize-- ; OK (fsize > 0) ; cols [k] = j ; fflag [j] = 0 ; } // downdate L, and the solution to Lx=b+deltaB C = lp_prune (A, rflag, cols, rank) ; ok = CHOLMOD(updown_solve) (FALSE, C, L, X, DeltaB, cm) ; CHOLMOD(free_sparse) (&C, cm) ; break ; //------------------------------------------------------------------ case 2: // resymbol (no change to numerical values) //------------------------------------------------------------------ // let resymbol handle the fset C = lp_prune (A, rflag, NULL, 0) ; ok = CHOLMOD(resymbol_noperm) (C, fset, fsize, TRUE, L, cm) ; CHOLMOD(free_sparse) (&C, cm) ; break; //------------------------------------------------------------------ case 3: // add row //------------------------------------------------------------------ // remove a row from rnot and add to rset if (nrow == rsize) { continue ; } kk = nrand (nrow-rsize) ; // RAND i = rnot [kk] ; OK (rflag [i] == 0) ; rnot [kk] = rnot [nrow-rsize-1] ; rset [rsize++] = i ; rflag [i] = 1 ; // S = column i of beta*I + A(r,f)*A(r,f)' S = get_row (A, i, rflag, fset, fsize, beta) ; ok = (S != NULL) ; if (ok) { // pick a random right-hand-side for this new row b [i] = 1 ; // xrand (1) bk [0] = b [i] ; bk [1] = 0 ; ok = CHOLMOD(rowadd_solve) (i, S, bk, L, X, DeltaB, cm) ; } CHOLMOD(free_sparse) (&S, cm) ; break ; //------------------------------------------------------------------ case 4: // delete row //------------------------------------------------------------------ // remove a row from rset and add to rnot if (rsize == 0) { continue ; } kk = nrand (rsize) ; // RAND i = rset [kk] ; OK (rflag [i] == 1) ; rset [kk] = rset [rsize-1] ; rnot [nrow-rsize] = i ; rsize-- ; // S = column i of beta*I + A(r,f)*A(r,f)' S = get_row (A, i, rflag, fset, fsize, beta) ; ok = (S != NULL) ; if (ok) { // B = B - S * y(i) Sp = S->p ; Si = S->i ; Sx = S->x ; for (p = 0 ; p < Sp [1] ; p++) { b [Si [p]] -= Sx [p] * Yx [i] ; } // B(i) = y(i) b [i] = Yx [i] ; yk [0] = Yx [i] ; yk [1] = 0 ; // pick a method arbitrarily if (trial % 2) { // get upper bound nonzero pattern of L(i,0:i-1) CHOLMOD(row_subtree) (A, AT, i, StaticParent, Row, cm) ; ok = CHOLMOD(rowdel_solve) (i, Row, yk, L, X, DeltaB, cm) ; } else { // Look in all cols 0 to i-1 for entries in L(i,0:i-1). // This is more costly, but requires no knowledge of // an upper bound on the pattern of L. ok = CHOLMOD(rowdel_solve) (i, NULL, yk, L, X, DeltaB, cm) ; } // for testing only, to ensure cholmod_row_subtree worked if (ok) { rflag [i] = 0 ; Lcheck (L, rflag) ; } } if (ok) { // let resymbol handle the fset C = lp_prune (A, rflag, NULL, 0) ; ok = CHOLMOD(resymbol_noperm) (C, fset, fsize, TRUE, L, cm); CHOLMOD(free_sparse) (&C, cm) ; } CHOLMOD(free_sparse) (&S, cm) ; break ; //------------------------------------------------------------------ case 5: // convert, just for testing //------------------------------------------------------------------ // convert to LDL', optionally packed if (trial % 2) { ok = CHOLMOD(change_factor) (CHOLMOD_REAL, FALSE, FALSE, TRUE, TRUE, L, cm) ; } else { ok = CHOLMOD(change_factor) (CHOLMOD_REAL, FALSE, FALSE, FALSE, TRUE, L, cm) ; } break ; } if (ok) { // scale B and X if their norm is getting large ynorm = CHOLMOD(norm_dense) (Y, 1, cm) ; bnorm = CHOLMOD(norm_dense) (B, 1, cm) ; xnorm = CHOLMOD(norm_dense) (X, 1, cm) ; norm = MAX (bnorm, xnorm) ; norm = MAX (norm, ynorm) ; if (norm > 1e10) { for (i = 0 ; i < nrow ; i++) { Xx [i] /= norm ; b [i] /= norm ; } } CHOLMOD(free_dense) (&Y, cm) ; Y = CHOLMOD(solve) (CHOLMOD_DLt, L, X, cm) ; r = lp_resid (A, rflag, fset, fsize, beta, Y, B) ; OK (!ISNAN (r)) ; MAXERR (maxerr, r, 1) ; if (r > 1e-6 && cm->print > 1) { printf ("lp err %.1g operation: "ID" ok "ID"\n", r, op, ok) ; } ok = (Y != NULL) ; } } CHOLMOD(free_dense) (&Y, cm) ; OK (CHOLMOD(print_common) ("6:cm in lpdemo", cm)) ; //-------------------------------------------------------------------------- // convert to LDL packed, LDL unpacked or LL packed and solve again //-------------------------------------------------------------------------- // solve the new system and check the residual CHOLMOD(print_factor) (L, "L final, for convert", cm) ; if (ok) { switch (nrand (3)) // RAND { // pick one at random case 0: { ok = CHOLMOD(change_factor) (CHOLMOD_REAL, FALSE, FALSE, TRUE, TRUE, L, cm) ; Y = CHOLMOD(solve) (CHOLMOD_DLt, L, X, cm) ; break ; } case 1: { ok = CHOLMOD(change_factor) (CHOLMOD_REAL, FALSE, FALSE, FALSE, TRUE, L, cm) ; Y = CHOLMOD(solve) (CHOLMOD_DLt, L, X, cm) ; break ; } case 2: { ok = CHOLMOD(change_factor) (CHOLMOD_REAL, TRUE, FALSE, TRUE, TRUE, L, cm) ; Y = CHOLMOD(solve) (CHOLMOD_LDLt, L, B, cm) ; break ; } } r = lp_resid (A, rflag, fset, fsize, beta, Y, B) ; OK (!ISNAN (r)) ; MAXERR (maxerr, r, 1) ; CHOLMOD(print_factor) (L, "L after convert", cm) ; } //-------------------------------------------------------------------------- // rank-1 update, but only partial Lx=b update //-------------------------------------------------------------------------- int whatever = 0 ; if (ok && fsize < ncol && nrow > 3) { Int colmark [1] ; j = fnot [0] ; fnot [0] = fnot [ncol-fsize-1] ; fset [fsize++] = j ; OK (fsize <= ncol) ; cols [0] = j ; fflag [j] = 1 ; for (colmark [0] = 0 ; ok && colmark [0] <= nrow ; colmark [0]++) { cholmod_factor *L2 ; cholmod_dense *X2 ; Real *X2x ; L2 = CHOLMOD(copy_factor) (L, cm) ; X2 = CHOLMOD(copy_dense) (X, cm) ; if (X2 != NULL) { OK (X2->dtype == DTYPE) ; } X2x = (X2 == NULL) ? NULL : X2->x ; // update L, and the solution to Lx=b+deltaB, // but only update solution in rows 0 to colmark[0] C = lp_prune (A, rflag, cols, 1) ; if ((whatever++) % 2 == 0) { ok = CHOLMOD(updown_mark) (TRUE, C, colmark, L2, X2, DeltaB, cm) ; } else { // does the same thing as updown_mark, with different API ok = CHOLMOD(updown_mask) (TRUE, C, colmark, NULL, L2, X2, DeltaB, cm) ; } CHOLMOD(free_sparse) (&C, cm) ; // compare with Lr=b+deltaB R = CHOLMOD(solve) (CHOLMOD_L, L2, B, cm) ; ok = ok && (R != NULL) ; r = -1 ; if (ok) { Real *Rx ; OK (R->dtype == DTYPE) ; Rx = R->x ; OK (X2x != NULL) ; r = 0 ; for (i = 0 ; i < colmark [0] ; i++) { r = MAX (r, fabs (X2x [i] - Rx [i])) ; } MAXERR (maxerr, r, 1) ; } CHOLMOD(free_dense) (&R, cm) ; CHOLMOD(free_dense) (&X2, cm) ; CHOLMOD(free_factor) (&L2, cm) ; } } //-------------------------------------------------------------------------- // free everything //-------------------------------------------------------------------------- // restore defaults cm->final_resymbol = FALSE ; cm->final_asis = TRUE ; cm->supernodal_switch = 40 ; CHOLMOD(free) (nrow, sizeof (Int), StaticParent, cm) ; CHOLMOD(free) (nrow, sizeof (Int), Pinv, cm) ; CHOLMOD(free) (nrow, sizeof (Int), P, cm) ; CHOLMOD(free) (nrow, sizeof (Int), rflag, cm) ; CHOLMOD(free) (nrow, sizeof (Int), rset, cm) ; CHOLMOD(free) (nrow, sizeof (Int), rnot, cm) ; CHOLMOD(free) (ncol, sizeof (Int), fset, cm) ; CHOLMOD(free) (ncol, sizeof (Int), fnot, cm) ; CHOLMOD(free) (ncol, sizeof (Int), fflag, cm) ; CHOLMOD(free_factor) (&L, cm) ; CHOLMOD(free_sparse) (&Row, cm) ; CHOLMOD(free_sparse) (&AT, cm) ; CHOLMOD(free_sparse) (&A, cm) ; CHOLMOD(free_dense) (&B, cm) ; CHOLMOD(free_dense) (&X, cm) ; CHOLMOD(free_dense) (&Y, cm) ; CHOLMOD(free_dense) (&DeltaB, cm) ; progress (0, '.') ; printf ("maxerr %g at %d: %s\n", maxerr, __LINE__, __FILE__) ; return (maxerr) ; }