/* ========================================================================== */ /* === Tcov/cm ============================================================== */ /* ========================================================================== */ /* ----------------------------------------------------------------------------- * CHOLMOD/Tcov Module. Copyright (C) 2005-2006, Timothy A. Davis * The CHOLMOD/Tcov 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 * -------------------------------------------------------------------------- */ /* A program for exhaustive statement-coverage for CHOLMOD, AMD, COLAMD, and * CCOLAMD. It tests every line of code in all three packages. * * For a complete test, all CHOLMOD modules, AMD, COLAMD, CCOLAMD, METIS, * LAPACK, and the BLAS are required. A partial test can be performed without * the Supernodal and/or Partition modules. METIS is not required if the * Partition module is not installed. LAPACK and the BLAS are not required * if the Supernodal module is not installed. * * Usage: * * cm < input > output * * where "input" contains a sparse matrix in triplet form. The first line of * the file contains four or five integers: * * nrow ncol nnz stype complex * * where the matrix is nrow-by-ncol. nnz is the number of (i,j,aij) triplets * in the rest of the file, one triplet per line. stype is -1 (symmetric * with entries in lower triangular part provided), 0 (unsymmetric), or 1 * (symmetric upper). If the 5th entry is missing, or 0, then the matrix is * real; if 1 the matrix is complex, if 2 the matrix is zomplex. Each * subsequent line contains * * i j aij * * for the row index, column index, and value of A(i,j). Duplicate entries * are summed. If stype is 2 or 3, the rest of the file is ignored, and a * special test matrix is constructed (2: arrowhead, 3: tridiagonal plus a * dense row). Test matrices are located in the Matrix/ subdirectory. * * For complex matrices, each line consists of * * i j xij zij * * where xij is the real part of A(i,j) and zij is the imaginary part. * * cm takes one optional parameter. If present (it does not matter what the * argument is, actually) then extension memory-failure tests are performed. */ #include "cm.h" /* ========================================================================== */ /* === global variables ===================================================== */ /* ========================================================================== */ double zero [2], one [2], minusone [2] ; cholmod_common Common, *cm ; cholmod_dense *M1 ; Int dot = 0 ; double Zero [2] ; /* ========================================================================== */ /* === my_rand ============================================================== */ /* ========================================================================== */ /* The POSIX example of rand, duplicated here so that the same sequence will * be generated on different machines. */ static unsigned long next = 1 ; /* RAND_MAX assumed to be 32767 */ Int my_rand (void) { next = next * 1103515245 + 12345 ; return ((unsigned)(next/65536) % /* 32768 */ (MY_RAND_MAX + 1)) ; } void my_srand (unsigned seed) { next = seed ; } unsigned long my_seed (void) { return (next) ; } /* ========================================================================== */ /* === progress ============================================================= */ /* ========================================================================== */ /* print a "." on stderr to indicate progress */ #define LINEWIDTH 70 #define COUNT 100 void progress (Int force, char s) { dot++ ; if (force) { dot += (COUNT - (dot % COUNT)) ; } if (dot % COUNT == 0) { fprintf (stderr, "%c", s) ; } if (dot % (COUNT*LINEWIDTH) == 0) { fprintf (stderr, "\n") ; } fflush (stdout) ; fflush (stderr) ; } /* ========================================================================== */ /* === my_handler =========================================================== */ /* ========================================================================== */ /* An error occurred that should not have occurred */ void my_handler (int status, char *file, int line, char *msg) { printf ("Error handler: file %s line %d status %d: %s\n", file, line, status, msg) ; if (status < CHOLMOD_OK || status > CHOLMOD_DSMALL) { fprintf (stderr, "\n\n************************************************" "********************************\n" "*** Test failure: file: %s line: %d\n" "*** status: %d message: %s\n" "***********************************************************" "*********************\n", file, line, status, msg); fflush (stderr) ; fflush (stdout) ; abort ( ) ; } } /* ========================================================================== */ /* === Assert =============================================================== */ /* ========================================================================== */ void Assert (int truth, char *file, int line) { if (!truth) { my_handler (-1, file, line, "") ; } } /* ========================================================================== */ /* === nrand ================================================================ */ /* ========================================================================== */ /* return a random Int between 0 and n-1 */ Int nrand (Int n) { return ((n <= 0) ? (0) : (rand ( ) % n)) ; } /* ========================================================================== */ /* === xrand ================================================================ */ /* ========================================================================== */ /* return a random double between 0 and x */ double xrand (double range) { return ((range * (double) (my_rand ( ))) / MY_RAND_MAX) ; } /* ========================================================================== */ /* === prand ================================================================ */ /* ========================================================================== */ /* allocate and construct a random permutation of 0:n-1 */ Int *prand (Int n) { Int *P ; Int t, j, k ; P = CHOLMOD(malloc) (n, sizeof (Int), cm) ; if (P == NULL) { ERROR (CHOLMOD_INVALID, "cannot create random perm") ; return (NULL) ; } for (k = 0 ; k < n ; k++) { P [k] = k ; } for (k = 0 ; k < n-1 ; k++) { j = k + nrand (n-k) ; t = P [j] ; P [j] = P [k] ; P [k] = t ; } CHOLMOD(print_perm) (P, n, n, "Prandom", cm) ; return (P) ; } /* ========================================================================== */ /* === rand_set ============================================================= */ /* ========================================================================== */ /* allocate and construct a random set of 0:n-1, possibly with duplicates */ Int *rand_set (Int len, Int n) { Int *cset ; Int k ; cset = CHOLMOD(malloc) (len, sizeof (Int), cm) ; if (cset == NULL) { ERROR (CHOLMOD_INVALID, "cannot create random set") ; return (NULL) ; } for (k = 0 ; k < len ; k++) { cset [k] = nrand (n) ; } CHOLMOD(print_subset) (cset, len, n, "random cset", cm) ; return (cset) ; } /* ========================================================================== */ /* === read_triplet ========================================================= */ /* ========================================================================== */ /* Read a triplet matrix from a file. */ #define MAXLINE 1024 cholmod_triplet *read_triplet ( FILE *f ) { cholmod_triplet *T ; double *Tx, *Tz ; long long x1, x2, x3, x4, x5 ; Int *Ti, *Tj ; Int n, j, k, nrow, ncol, nz, stype, arrowhead, tridiag_plus_denserow, xtype, is_complex ; char s [MAXLINE] ; /* ---------------------------------------------------------------------- */ /* read in a triplet matrix from a file */ /* ---------------------------------------------------------------------- */ dot = 0 ; xtype = 0 ; if (fgets (s, MAXLINE, f) == NULL) { return (NULL) ; } x1 = 0 ; x2 = 0 ; x3 = 0 ; x4 = 0 ; x5 = 0 ; k = sscanf (s, "%lld %lld %lld %lld %lld\n", &x1, &x2, &x3, &x4, &x5) ; nrow = x1 ; ncol = x2 ; nz = x3 ; stype = x4 ; xtype = x5 ; xtype++ ; is_complex = (xtype != CHOLMOD_REAL) ; printf ("read_triplet: nrow "ID" ncol "ID" nz "ID" stype "ID" xtype "ID"\n", nrow, ncol, nz, stype, xtype) ; arrowhead = FALSE ; tridiag_plus_denserow = FALSE ; n = MAX (nrow, ncol) ; if (stype == 2) { /* ignore nz and the rest of the file, and create an arrowhead matrix */ arrowhead = TRUE ; nz = nrow + ncol + 1 ; stype = (nrow == ncol) ? (1) : (0) ; } else if (stype == 3) { tridiag_plus_denserow = TRUE ; nrow = n ; ncol = n ; nz = 4*n + 4 ; stype = 0 ; } T = CHOLMOD(allocate_triplet) (nrow, ncol, nz, stype, is_complex ? CHOLMOD_ZOMPLEX : CHOLMOD_REAL, cm) ; if (T == NULL) { ERROR (CHOLMOD_INVALID, "cannot create triplet matrix") ; return (NULL) ; } Ti = T->i ; Tj = T->j ; Tx = T->x ; Tz = T->z ; if (arrowhead) { for (k = 0 ; k < MIN (nrow,ncol) ; k++) { Ti [k] = k ; Tj [k] = k ; Tx [k] = nrow + xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = nrow + xrand (1) ; /* RAND */ } } for (j = 0 ; j < ncol ; j++) { Ti [k] = 0 ; Tj [k] = j ; Tx [k] = - xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = - xrand (1) ; /* RAND */ } k++ ; } T->nnz = k ; } else if (tridiag_plus_denserow) { /* dense row, except for the last column */ for (k = 0 ; k < n-1 ; k++) { Ti [k] = 0 ; Tj [k] = k ; Tx [k] = xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = xrand (1) ; /* RAND */ } } /* diagonal */ for (j = 0 ; j < n ; j++) { Ti [k] = j ; Tj [k] = j ; Tx [k] = nrow + xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = nrow + xrand (1) ; /* RAND */ } k++ ; } /* superdiagonal */ for (j = 1 ; j < n ; j++) { Ti [k] = j-1 ; Tj [k] = j ; Tx [k] = xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = xrand (1) ; /* RAND */ } k++ ; } /* subdiagonal */ for (j = 0 ; j < n-1 ; j++) { Ti [k] = j+1 ; Tj [k] = j ; Tx [k] = xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = xrand (1) ; /* RAND */ } k++ ; } /* a few extra terms in the last column */ Ti [k] = MAX (0, n-3) ; Tj [k] = n-1 ; Tx [k] = xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = xrand (1) ; /* RAND */ } k++ ; Ti [k] = MAX (0, n-4) ; Tj [k] = n-1 ; Tx [k] = xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = xrand (1) ; /* RAND */ } k++ ; Ti [k] = MAX (0, n-5) ; Tj [k] = n-1 ; Tx [k] = xrand (1) ; /* RAND */ if (is_complex) { Tz [k] = xrand (1) ; /* RAND */ } k++ ; T->nnz = k ; } else { if (is_complex) { for (k = 0 ; k < nz ; k++) { if (fscanf (f,""ID" "ID" %lg %lg\n", Ti+k, Tj+k, Tx+k, Tz+k) == EOF) { ERROR (CHOLMOD_INVALID, "Error reading triplet matrix\n") ; } } } else { for (k = 0 ; k < nz ; k++) { if (fscanf (f, ""ID" "ID" %lg\n", Ti+k, Tj+k, Tx+k) == EOF) { ERROR (CHOLMOD_INVALID, "Error reading triplet matrix\n") ; } } } T->nnz = nz ; } CHOLMOD(triplet_xtype) (xtype, T, cm) ; /* ---------------------------------------------------------------------- */ /* print the triplet matrix */ /* ---------------------------------------------------------------------- */ cm->print = 4 ; CHOLMOD(print_triplet) (T, "T input", cm) ; cm->print = 1 ; fprintf (stderr, "Test matrix: "ID"-by-"ID" with "ID" entries, stype: "ID "\n", (Int) T->nrow, (Int) T->ncol, (Int) T->nnz, (Int) T->stype) ; printf ("\n\n======================================================\n" "Test matrix: "ID"-by-"ID" with "ID" entries, stype: "ID"\n", (Int) T->nrow, (Int) T->ncol, (Int) T->nnz, (Int) T->stype) ; if (MAX (nrow, ncol) > NLARGE) { fprintf (stderr, "Please wait, this will take a while ...") ; dot = 39*LINEWIDTH ; } return (T) ; } /* ========================================================================== */ /* === zeros ================================================================ */ /* ========================================================================== */ /* Same as cholmod_zeros or cholmod_l_zeros, except it allows for a leading * dimension that is different than nrow */ cholmod_dense *zeros (Int nrow, Int ncol, Int d, Int xtype) { cholmod_dense *X ; double *Xx, *Xz ; Int i, nz ; X = CHOLMOD(allocate_dense) (nrow, ncol, d, xtype, cm) ; if (X == NULL) { return (NULL) ; } Xx = X->x ; Xz = X->z ; nz = MAX (1, X->nzmax) ; switch (X->xtype) { case CHOLMOD_REAL: for (i = 0 ; i < nz ; i++) { Xx [i] = 0 ; } break ; case CHOLMOD_COMPLEX: for (i = 0 ; i < 2*nz ; i++) { Xx [i] = 0 ; } break ; case CHOLMOD_ZOMPLEX: for (i = 0 ; i < nz ; i++) { Xx [i] = 0 ; } for (i = 0 ; i < nz ; i++) { Xz [i] = 0 ; } break ; } return (X) ; } /* ========================================================================== */ /* === xtrue ================================================================ */ /* ========================================================================== */ /* Allocate and construct a dense matrix, X(i,j) = i+j*d+1 */ cholmod_dense *xtrue (Int nrow, Int ncol, Int d, Int xtype) { double *x, *z ; cholmod_dense *X ; Int j, i ; X = zeros (nrow, ncol, d, xtype) ; if (X == NULL) { ERROR (CHOLMOD_INVALID, "cannot create dense matrix") ; return (NULL) ; } x = X->x ; z = X->z ; if (xtype == CHOLMOD_REAL) { for (j = 0 ; j < ncol ; j++) { for (i = 0 ; i < nrow ; i++) { x [i+j*d] = i+j*d + 1 ; } } } else if (xtype == CHOLMOD_COMPLEX) { for (j = 0 ; j < ncol ; j++) { for (i = 0 ; i < nrow ; i++) { x [2*(i+j*d) ] = i+j*d + 1 ; x [2*(i+j*d)+1] = ((double) (j+i*d + 1))/10 ; } } } else { for (j = 0 ; j < ncol ; j++) { for (i = 0 ; i < nrow ; i++) { x [i+j*d] = i+j*d + 1 ; z [i+j*d] = ((double) (j+i*d + 1))/10 ; } } } return (X) ; } /* ========================================================================== */ /* === rhs ================================================================== */ /* ========================================================================== */ /* Create a right-hand-side, b = A*x, where x is a known solution */ cholmod_dense *rhs (cholmod_sparse *A, Int nrhs, Int d) { Int n ; cholmod_dense *W, *Z, *B ; if (A == NULL) { ERROR (CHOLMOD_INVALID, "cannot compute rhs") ; return (NULL) ; } n = A->nrow ; /* B = zeros (n,rhs) but with leading dimension d */ B = zeros (n, nrhs, d, A->xtype) ; /* ---------------------------------------------------------------------- */ /* create a known solution */ /* ---------------------------------------------------------------------- */ Z = xtrue (n, nrhs, d, A->xtype) ; /* ---------------------------------------------------------------------- */ /* compute B = A*Z or A*A'*Z */ /* ---------------------------------------------------------------------- */ if (A->stype == 0) { /* W = A'*Z */ W = CHOLMOD(zeros) (A->ncol, nrhs, A->xtype, cm) ; CHOLMOD(sdmult) (A, TRUE, one, zero, Z, W, cm) ; /* B = A*W */ CHOLMOD(sdmult) (A, FALSE, one, zero, W, B, cm) ; CHOLMOD(free_dense) (&W, cm) ; } else { /* B = A*Z */ CHOLMOD(sdmult) (A, FALSE, one, zero, Z, B, cm) ; } CHOLMOD(free_dense) (&Z, cm) ; return (B) ; } /* ========================================================================== */ /* === resid ================================================================ */ /* ========================================================================== */ /* compute r = norm (A*x-b)/norm(b) or r = norm (A*A'*x-b)/norm(b) */ double resid (cholmod_sparse *A, cholmod_dense *X, cholmod_dense *B) { double r, bnorm ; cholmod_dense *R, *X2, *B2 ; cholmod_sparse *C, *A2 ; Int d, n, nrhs, xtype ; if (A == NULL || X == NULL || B == NULL) { ERROR (CHOLMOD_INVALID, "cannot compute resid") ; return (1) ; } cm->status = CHOLMOD_OK ; n = B->nrow ; /* ---------------------------------------------------------------------- */ /* convert all inputs to an identical xtype */ /* ---------------------------------------------------------------------- */ xtype = MAX (A->xtype, X->xtype) ; xtype = MAX (xtype, B->xtype) ; A2 = NULL ; B2 = NULL ; X2 = NULL ; if (A->xtype != xtype) { A2 = CHOLMOD(copy_sparse) (A, cm) ; CHOLMOD(sparse_xtype) (xtype, A2, cm) ; A = A2 ; } if (X->xtype != xtype) { X2 = CHOLMOD(copy_dense) (X, cm) ; CHOLMOD(dense_xtype) (xtype, X2, cm) ; X = X2 ; } if (B->xtype != xtype) { B2 = CHOLMOD(copy_dense) (B, cm) ; CHOLMOD(dense_xtype) (xtype, B2, cm) ; B = B2 ; } if (cm->status < CHOLMOD_OK) { ERROR (CHOLMOD_INVALID, "cannot compute resid") ; CHOLMOD(free_sparse) (&A2, cm) ; CHOLMOD(free_dense) (&B2, cm) ; CHOLMOD(free_dense) (&X2, cm) ; return (1) ; } /* ---------------------------------------------------------------------- */ /* get the right-hand-side, B, and its norm */ /* ---------------------------------------------------------------------- */ nrhs = B->ncol ; d = B->d ; if (nrhs == 1) { /* inf-norm, 1-norm, or 2-norm (random choice) */ bnorm = CHOLMOD(norm_dense) (B, nrand (2), cm) ; } else { /* inf-norm or 1-norm (random choice) */ bnorm = CHOLMOD(norm_dense) (B, nrand (1), cm) ; } /* ---------------------------------------------------------------------- */ /* compute the residual */ /* ---------------------------------------------------------------------- */ if (A->stype == 0) { if (n < 10 && A->xtype == CHOLMOD_REAL) { /* test cholmod_aat, C = A*A' */ C = CHOLMOD(aat) (A, NULL, 0, 1, cm) ; /* R = B */ R = CHOLMOD(copy_dense) (B, cm) ; /* R = C*X - R */ CHOLMOD(sdmult) (C, FALSE, one, minusone, X, R, cm) ; CHOLMOD(free_sparse) (&C, cm) ; } else { /* W = A'*X */ cholmod_dense *W ; W = CHOLMOD(zeros) (A->ncol, nrhs, A->xtype, cm) ; CHOLMOD(sdmult) (A, TRUE, one, zero, X, W, cm) ; /* R = B */ R = CHOLMOD(copy_dense) (B, cm) ; /* R = A*W - R */ CHOLMOD(sdmult) (A, FALSE, one, minusone, W, R, cm) ; CHOLMOD(free_dense) (&W, cm) ; } } else { /* R = B */ R = CHOLMOD(copy_dense) (B, cm) ; /* R = A*X - R */ CHOLMOD(sdmult) (A, FALSE, one, minusone, X, R, cm) ; } /* ---------------------------------------------------------------------- */ /* r = norm (R) / norm (B) */ /* ---------------------------------------------------------------------- */ r = CHOLMOD(norm_dense) (R, 1, cm) ; CHOLMOD(free_dense) (&R, cm) ; CHOLMOD(free_sparse) (&A2, cm) ; CHOLMOD(free_dense) (&B2, cm) ; CHOLMOD(free_dense) (&X2, cm) ; if (bnorm > 0) { r /= bnorm ; } return (r) ; } /* ========================================================================== */ /* === resid_sparse ========================================================= */ /* ========================================================================== */ /* compute r = norm (A*x-b)/norm(b) or r = norm (A*A'*x-b)/norm(b) */ double resid_sparse (cholmod_sparse *A, cholmod_sparse *X, cholmod_sparse *B) { double r, bnorm ; cholmod_sparse *R, *W, *AT, *W2 ; cholmod_dense *X2, *B2 ; Int n, nrhs, xtype ; if (A == NULL || X == NULL || B == NULL) { ERROR (CHOLMOD_INVALID, "cannot compute resid") ; return (1) ; } /* ---------------------------------------------------------------------- */ /* compute the residual */ /* ---------------------------------------------------------------------- */ xtype = MAX (A->xtype, X->xtype) ; xtype = MAX (xtype, B->xtype) ; if (xtype > CHOLMOD_REAL) { /* ------------------------------------------------------------------ */ /* convert X and B to dense if any is complex or zomplex */ /* ------------------------------------------------------------------ */ X2 = CHOLMOD(sparse_to_dense) (X, cm) ; B2 = CHOLMOD(sparse_to_dense) (B, cm) ; r = resid (A, X2, B2) ; CHOLMOD(free_dense) (&X2, cm) ; CHOLMOD(free_dense) (&B2, cm) ; } else { /* ------------------------------------------------------------------ */ /* all inputs are real */ /* ------------------------------------------------------------------ */ n = B->nrow ; nrhs = B->ncol ; /* inf-norm or 1-norm (random choice) */ bnorm = CHOLMOD(norm_sparse) (B, nrand (1), cm) ; if (A->stype == 0) { /* W = A'*X */ AT = CHOLMOD(transpose) (A, 1, cm) ; W = CHOLMOD(ssmult) (AT, X, 0, TRUE, FALSE, cm) ; CHOLMOD(free_sparse) (&AT, cm) ; /* W2 = A*W */ W2 = CHOLMOD(ssmult) (A, W, 0, TRUE, FALSE, cm) ; CHOLMOD(free_sparse) (&W, cm) ; /* R = W2 - B */ R = CHOLMOD(add) (W2, B, one, minusone, TRUE, FALSE, cm) ; CHOLMOD(free_sparse) (&W2, cm) ; } else { /* W = A*X */ W = CHOLMOD(ssmult) (A, X, 0, TRUE, FALSE, cm) ; /* R = W - B */ R = CHOLMOD(add) (W, B, one, minusone, TRUE, FALSE, cm) ; CHOLMOD(free_sparse) (&W, cm) ; } r = CHOLMOD(norm_sparse) (R, 1, cm) ; CHOLMOD(free_sparse) (&R, cm) ; if (bnorm > 0) { r /= bnorm ; } } return (r) ; } /* ========================================================================== */ /* === resid3 =============================================================== */ /* ========================================================================== */ /* r = norm (A1*A2*A3*x - b) / norm (b) */ double resid3 (cholmod_sparse *A1, cholmod_sparse *A2, cholmod_sparse *A3, cholmod_dense *X, cholmod_dense *B) { double r, bnorm ; cholmod_dense *R, *W1, *W2, *X2, *B2 ; cholmod_sparse *C1, *C2, *C3 ; Int n, nrhs, d, xtype ; if (A1 == NULL || X == NULL || B == NULL) { ERROR (CHOLMOD_INVALID, "cannot compute resid3") ; return (1) ; } cm->status = CHOLMOD_OK ; n = B->nrow ; /* ---------------------------------------------------------------------- */ /* convert all inputs to an identical xtype */ /* ---------------------------------------------------------------------- */ xtype = MAX (A1->xtype, X->xtype) ; xtype = MAX (xtype, B->xtype) ; if (A2 != NULL) { xtype = MAX (xtype, A2->xtype) ; } if (A3 != NULL) { xtype = MAX (xtype, A3->xtype) ; } C1 = NULL ; C2 = NULL ; C3 = NULL ; B2 = NULL ; X2 = NULL ; if (A1->xtype != xtype) { C1 = CHOLMOD(copy_sparse) (A1, cm) ; CHOLMOD(sparse_xtype) (xtype, C1, cm) ; A1 = C1 ; } if (A2 != NULL && A2->xtype != xtype) { C2 = CHOLMOD(copy_sparse) (A2, cm) ; CHOLMOD(sparse_xtype) (xtype, C2, cm) ; A2 = C2 ; } if (A3 != NULL && A3->xtype != xtype) { C3 = CHOLMOD(copy_sparse) (A3, cm) ; CHOLMOD(sparse_xtype) (xtype, C3, cm) ; A3 = C3 ; } if (X->xtype != xtype) { X2 = CHOLMOD(copy_dense) (X, cm) ; CHOLMOD(dense_xtype) (xtype, X2, cm) ; X = X2 ; } if (B->xtype != xtype) { B2 = CHOLMOD(copy_dense) (B, cm) ; CHOLMOD(dense_xtype) (xtype, B2, cm) ; B = B2 ; } if (cm->status < CHOLMOD_OK) { ERROR (CHOLMOD_INVALID, "cannot compute resid3") ; CHOLMOD(free_sparse) (&C1, cm) ; CHOLMOD(free_sparse) (&C2, cm) ; CHOLMOD(free_sparse) (&C3, cm) ; CHOLMOD(free_dense) (&B2, cm) ; CHOLMOD(free_dense) (&X2, cm) ; return (1) ; } /* ---------------------------------------------------------------------- */ /* get B and its norm */ /* ---------------------------------------------------------------------- */ nrhs = B->ncol ; d = B->d ; bnorm = CHOLMOD(norm_dense) (B, 1, cm) ; /* ---------------------------------------------------------------------- */ /* compute the residual */ /* ---------------------------------------------------------------------- */ if (A3 != NULL) { /* W1 = A3*X */ W1 = CHOLMOD(zeros) (n, nrhs, xtype, cm) ; CHOLMOD(sdmult) (A3, FALSE, one, zero, X, W1, cm) ; } else { W1 = X ; } if (A2 != NULL) { /* W2 = A2*W1 */ W2 = CHOLMOD(eye) (n, nrhs, xtype, cm) ; CHOLMOD(sdmult) (A2, FALSE, one, zero, W1, W2, cm) ; } else { W2 = W1 ; } /* R = B */ R = CHOLMOD(copy_dense) (B, cm) ; /* R = A1*W2 - R */ CHOLMOD(sdmult) (A1, FALSE, one, minusone, W2, R, cm) ; /* ---------------------------------------------------------------------- */ /* r = norm (R) / norm (B) */ /* ---------------------------------------------------------------------- */ r = CHOLMOD(norm_dense) (R, 1, cm) ; CHOLMOD(free_dense) (&R, cm) ; CHOLMOD(free_sparse) (&C1, cm) ; CHOLMOD(free_sparse) (&C2, cm) ; CHOLMOD(free_sparse) (&C3, cm) ; CHOLMOD(free_dense) (&B2, cm) ; CHOLMOD(free_dense) (&X2, cm) ; if (A3 != NULL) { CHOLMOD(free_dense) (&W1, cm) ; } if (A2 != NULL) { CHOLMOD(free_dense) (&W2, cm) ; } if (bnorm > 0) { r /= bnorm ; } return (r) ; } /* ========================================================================== */ /* === pnorm ================================================================ */ /* ========================================================================== */ /* r = norm (x-Pb) or r = norm(x-P'b). This is lengthy because CHOLMOD does * not provide any operations on dense matrices, and because it used to test * the sparse-to-dense conversion routine. Multiple methods are used to compute * the same thing. */ double pnorm (cholmod_dense *X, Int *P, cholmod_dense *B, Int inv) { cholmod_dense *R, *X2, *B2 ; cholmod_factor *L ; double *xx, *xz, *bx, *bz, *rx, *rz ; Int *Pinv, *Perm ; double rnorm, r ; Int i, j, k, n, nrhs, xtype, ok, save, lxtype ; if (X == NULL || P == NULL || B == NULL) { ERROR (CHOLMOD_INVALID, "cannot compute pnorm") ; return (1) ; } save = cm->prefer_zomplex ; n = X->nrow ; nrhs = X->ncol ; rnorm = 0 ; Pinv = CHOLMOD(malloc) (n, sizeof (Int), cm) ; if (Pinv != NULL) { for (k = 0 ; k < n ; k++) { Pinv [P [k]] = k ; } } xtype = MAX (X->xtype, B->xtype) ; R = CHOLMOD(zeros) (n, nrhs, CHOLMOD_ZOMPLEX, cm) ; B2 = CHOLMOD(copy_dense) (B, cm) ; ok = R != NULL && B2 != NULL ; ok = ok && CHOLMOD(dense_xtype) (CHOLMOD_ZOMPLEX, B2, cm) ; for (lxtype = CHOLMOD_REAL ; ok && lxtype <= CHOLMOD_ZOMPLEX ; lxtype++) { /* create a fake factor object */ L = CHOLMOD(allocate_factor) (n, cm) ; CHOLMOD(change_factor) (lxtype, TRUE, FALSE, TRUE, TRUE, L, cm) ; ok = ok && (L != NULL && L->Perm != NULL && Pinv != NULL) ; if (ok) { L->ordering = CHOLMOD_GIVEN ; Perm = L->Perm ; for (k = 0 ; k < n ; k++) { Perm [k] = Pinv [k] ; } } for (k = 0 ; k <= 1 ; k++) { /* solve the inverse permutation system, X2 = P*X or X2 = P'*X */ cm->prefer_zomplex = k ; X2 = CHOLMOD(solve) (inv ? CHOLMOD_Pt : CHOLMOD_P, L, X, cm) ; ok = ok && CHOLMOD(dense_xtype) (CHOLMOD_ZOMPLEX, X2, cm) ; if (ok && X2 != NULL) { rx = R->x ; rz = R->z ; xx = X2->x ; xz = X2->z ; bx = B2->x ; bz = B2->z ; for (j = 0 ; j < nrhs ; j++) { for (i = 0 ; i < n ; i++) { rx [i+j*n] = xx [i+j*n] - bx [i+j*n] ; rz [i+j*n] = xz [i+j*n] - bz [i+j*n] ; } } } r = CHOLMOD(norm_dense) (R, 0, cm) ; rnorm = MAX (r, rnorm) ; CHOLMOD(free_dense) (&X2, cm) ; } CHOLMOD(free_factor) (&L, cm) ; } CHOLMOD(free_dense) (&B2, cm) ; CHOLMOD(free_dense) (&R, cm) ; if (xtype == CHOLMOD_REAL) { /* X and B are both real */ cholmod_sparse *Bs, *Pb ; Bs = CHOLMOD(dense_to_sparse) (B, TRUE, cm) ; Pb = CHOLMOD(submatrix) (Bs, inv ? Pinv : P, n, NULL, -1, TRUE, TRUE,cm); X2 = CHOLMOD(sparse_to_dense) (Pb, cm) ; R = CHOLMOD(zeros) (n, nrhs, CHOLMOD_REAL, cm) ; if (R != NULL && X != NULL && X2 != NULL) { rx = R->x ; xx = X->x ; bx = X2->x ; for (j = 0 ; j < nrhs ; j++) { for (i = 0 ; i < n ; i++) { rx [i+j*n] = xx [i+j*n] - bx [i+j*n] ; } } } CHOLMOD(free_sparse) (&Bs, cm) ; CHOLMOD(free_sparse) (&Pb, cm) ; CHOLMOD(free_dense) (&X2, cm) ; r = CHOLMOD(norm_dense) (R, 1, cm) ; rnorm = MAX (rnorm, r) ; CHOLMOD(free_dense) (&R, cm) ; } CHOLMOD(free) (n, sizeof (Int), Pinv, cm) ; cm->prefer_zomplex = save ; return (rnorm) ; } /* ========================================================================== */ /* === prune_row ============================================================ */ /* ========================================================================== */ /* Set row k and column k of a packed matrix A to zero. Set A(k,k) to 1 * if space is available. */ void prune_row (cholmod_sparse *A, Int k) { double *Ax ; Int *Ap, *Ai ; Int ncol, p, i, j, nz ; if (A == NULL) { ERROR (CHOLMOD_INVALID, "nothing to prune") ; return ; } Ap = A->p ; Ai = A->i ; Ax = A->x ; nz = 0 ; ncol = A->ncol ; for (j = 0 ; j < ncol ; j++) { p = Ap [j] ; Ap [j] = nz ; if (j == k && nz < Ap [j+1]) { Ai [nz] = k ; Ax [nz] = 1 ; nz++ ; } else { for ( ; p < Ap [j+1] ; p++) { i = Ai [p] ; if (i != k) { Ai [nz] = i ; Ax [nz] = Ax [p] ; nz++ ; } } } } Ap [ncol] = nz ; } /* ========================================================================== */ /* === do_matrix =========================================================== */ /* ========================================================================== */ double do_matrix (cholmod_sparse *A) { double err, maxerr = 0 ; Int print, precise, maxprint, minprint, nmethods ; if (A == NULL) { ERROR (CHOLMOD_INVALID, "no matrix") ; return (1) ; } /* ---------------------------------------------------------------------- */ /* determine print level, based on matrix size */ /* ---------------------------------------------------------------------- */ if (A->nrow <= 4) { minprint = 5 ; maxprint = 5 ; } else if (A->nrow <= 8) { minprint = 4 ; maxprint = 4 ; } else { minprint = 1 ; maxprint = 1 ; } /* ---------------------------------------------------------------------- */ /* for all print levels and precisions, do: */ /* ---------------------------------------------------------------------- */ for (print = minprint ; print <= maxprint ; print++) { for (precise = 0 ; precise <= (print >= 4) ? 1 : 0 ; precise++) { Int save1, save2 ; maxerr = 0 ; my_srand (42) ; /* RAND reset */ cm->print = print ; cm->precise = precise ; printf ("\n----------Print level %d precise: %d\n", cm->print, cm->precise) ; save1 = cm->final_asis ; save2 = cm->final_super ; cm->final_asis = FALSE ; cm->final_super = TRUE ; OK (CHOLMOD(print_common) ("cm", cm)) ; cm->final_asis = save1 ; cm->final_super = save2 ; /* -------------------------------------------------------------- */ /* test various matrix operations */ /* -------------------------------------------------------------- */ err = test_ops (A) ; /* RAND */ MAXERR (maxerr, err, 1) ; /* -------------------------------------------------------------- */ /* solve the augmented system */ /* -------------------------------------------------------------- */ err = aug (A) ; /* no random number use */ MAXERR (maxerr, err, 1) ; /* -------------------------------------------------------------- */ /* solve using different methods */ /* -------------------------------------------------------------- */ printf ("test_solver (1)\n") ; cm->nmethods = 9 ; cm->final_asis = TRUE ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; printf ("test_solver (2)\n") ; cm->final_asis = TRUE ; cm->method [0].ordering = CHOLMOD_NATURAL ; for (nmethods = 0 ; nmethods < 7 ; nmethods++) { cm->nmethods = nmethods ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; } printf ("test_solver (3)\n") ; cm->nmethods = 1 ; cm->method [0].ordering = CHOLMOD_NESDIS ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; printf ("test_solver (3b)\n") ; cm->nmethods = 1 ; cm->method [0].ordering = CHOLMOD_NESDIS ; cm->method [0].nd_camd = 2 ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; printf ("test_solver (3c)\n") ; cm->nmethods = 1 ; cm->method [0].ordering = CHOLMOD_NATURAL ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; printf ("test_solver (4)\n") ; cm->nmethods = 1 ; cm->method[0].ordering = CHOLMOD_METIS ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; printf ("test_solver (5)\n") ; cm->nmethods = 1 ; cm->method [0].ordering = CHOLMOD_AMD ; CHOLMOD(free_work) (cm) ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; printf ("test_solver (6)\n") ; cm->nmethods = 1 ; cm->method[0].ordering = CHOLMOD_COLAMD ; err = test_solver (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; /* -------------------------------------------------------------- */ /* restore default control parameters */ /* -------------------------------------------------------------- */ OK (CHOLMOD(print_common) ("cm", cm)) ; CHOLMOD(defaults) (cm) ; } } printf ("do_matrix max error %.1g\n", maxerr) ; return (maxerr) ; } /* ========================================================================== */ /* === main ================================================================= */ /* ========================================================================== */ /* Usage: * cm < matrix do not perform intensive memory-failure tests * cm -m < matrix do perform memory tests * cm -s < matrix matrix is singular, nan error expected * * (The memory tests are performed if any argument is given to cm). */ int main (int argc, char **argv) { cholmod_triplet *T ; cholmod_sparse *A, *C, *AT ; char *s ; double err = 0, maxerr = 0 ; Int n = 0, nmin = 0, nrow = 0, ncol = 0, save ; int singular, do_memory, i, do_nantests ; double v = CHOLMOD_VERSION ; printf ("Testing CHOLMOD (%g)\n", v) ; printf ("%s: argc: %d\n", argv [0], argc) ; my_srand (42) ; /* RAND */ fflush (stdout) ; singular = FALSE ; do_memory = FALSE ; do_nantests = FALSE ; for (i = 1 ; i < argc ; i++) { s = argv [i] ; if (s [0] == '-' && s [1] == 'm') do_memory = TRUE ; if (s [0] == '-' && s [1] == 's') singular = TRUE ; if (s [0] == '-' && s [1] == 'n') do_nantests = TRUE ; } printf ("do_memory: %d singular: %d\n", do_memory, singular) ; /* ---------------------------------------------------------------------- */ /* initialize CHOLMOD */ /* ---------------------------------------------------------------------- */ cm = &Common ; OK (CHOLMOD(start) (cm)) ; /* ---------------------------------------------------------------------- */ /* test all methods with NULL common */ /* ---------------------------------------------------------------------- */ /* no user error handler, since lots of errors will be raised here */ cm->error_handler = NULL ; null_test (NULL) ; save = cm->itype ; cm->itype = -999 ; null_test (cm) ; cm->itype = save ; null_test2 ( ) ; CHOLMOD(finish) (cm) ; OK (cm->malloc_count == 0) ; OK (CHOLMOD(start) (cm)) ; /* ---------------------------------------------------------------------- */ /* create basic scalars */ /* ---------------------------------------------------------------------- */ Zero [0] = 0 ; Zero [1] = 0 ; zero [0] = 0 ; zero [1] = 0 ; one [0] = 1 ; one [1] = 0 ; minusone [0] = -1 ; minusone [1] = 0 ; M1 = CHOLMOD(ones) (1, 1, CHOLMOD_REAL, cm) ; if (M1 != NULL) { ((double *) (M1->x)) [0] = -1 ; } /* ---------------------------------------------------------------------- */ /* read in a triplet matrix and use it to test CHOLMOD */ /* ---------------------------------------------------------------------- */ for ( ; (T = read_triplet (stdin)) != NULL ; ) /* RAND */ { if (T->nrow > 1000000) { /* do huge-problem tests only */ huge ( ) ; CHOLMOD(free_triplet) (&T, cm) ; continue ; } maxerr = 0 ; CHOLMOD(defaults) (cm) ; cm->error_handler = my_handler ; cm->print = 4 ; cm->precise = FALSE ; cm->nmethods = 8 ; /* ------------------------------------------------------------------ */ /* convert triplet to sparse matrix */ /* ------------------------------------------------------------------ */ A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ; AT = CHOLMOD(transpose) (A, 0, cm) ; OK (CHOLMOD(print_sparse) (A, "Test matrix, A", cm)) ; C = unpack (A) ; /* RAND */ OK (CHOLMOD(print_sparse) (C, "Unpacked/unsorted version of A", cm)) ; cm->print = 1 ; if (T != NULL) { nrow = T->nrow ; ncol = T->ncol ; n = MAX (nrow, ncol) ; nmin = MIN (nrow, ncol) ; } /* ------------------------------------------------------------------ */ /* basic error tests */ /* ------------------------------------------------------------------ */ null2 (T, do_nantests) ; /* RAND */ printf ("Null2 OK : no error\n") ; if (do_nantests) { maxerr = 0 ; goto done ; /* yes, this is ugly */ } /* ------------------------------------------------------------------ */ /* raw factorization tests */ /* ------------------------------------------------------------------ */ cm->error_handler = NULL ; err = raw_factor (A, 2) ; /* RAND */ cm->error_handler = my_handler ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error %.1g\n", err) ; err = raw_factor2 (A, 0., 0) ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error2 %.1g\n", err) ; err = raw_factor2 (A, 1e-16, 0) ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error3 %.1g\n", err) ; if (n < 1000 && A && T && A->stype == 1) { /* factorize a symmetric matrix (upper part stored), possibly * with ignored entries in lower triangular part. */ cholmod_sparse *F ; int save = T->stype ; T->stype = 0 ; F = CHOLMOD(triplet_to_sparse) (T, 0, cm) ; T->stype = save ; /* ET = CHOLMOD(transpose) (E, 2, cm) ; if (E) E->stype = 0 ; if (ET) ET->stype = 0 ; F = CHOLMOD(add) (E, ET, one, one, 1, 1, cm) ; */ if (F) F->stype = 1 ; err = raw_factor2 (F, 0., 0) ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error4 %.1g\n", err) ; err = raw_factor2 (F, 1e-16, 0) ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error5 %.1g\n", err) ; cm->dbound = 1e-15 ; err = raw_factor2 (F, 0., 0) ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error6 %.1g\n", err) ; err = raw_factor2 (F, 1e-16, 0) ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error7 %.1g\n", err) ; err = raw_factor2 (F, 1e-16, 1) ; MAXERR (maxerr, err, 1) ; printf ("raw factorization error8 %.1g\n", err) ; cm->dbound = 0 ; /* CHOLMOD(free_sparse) (&E, cm) ; CHOLMOD(free_sparse) (&ET, cm) ; */ CHOLMOD(free_sparse) (&F, cm) ; } /* ------------------------------------------------------------------ */ /* matrix ops */ /* ------------------------------------------------------------------ */ err = test_ops (A) ; /* RAND */ MAXERR (maxerr, err, 1) ; printf ("initial testops error %.1g\n", err) ; /* ------------------------------------------------------------------ */ /* analyze, factorize, solve */ /* ------------------------------------------------------------------ */ err = solve (A) ; /* RAND */ MAXERR (maxerr, err, 1) ; printf ("initial solve error %.1g\n", err) ; /* ------------------------------------------------------------------ */ /* CCOLAMD tests */ /* ------------------------------------------------------------------ */ cctest (A) ; /* RAND reset */ cctest (AT) ; /* RAND reset */ /* ------------------------------------------------------------------ */ /* COLAMD tests */ /* ------------------------------------------------------------------ */ ctest (A) ; ctest (AT) ; /* ------------------------------------------------------------------ */ /* AMD tests */ /* ------------------------------------------------------------------ */ if (n < NLARGE || A->stype) { /* for unsymmetric matrices, this forms A*A' and A'*A, which can * fail if A has a dense row or column. So it is only done for * modest-sized unsymmetric matrices. */ amdtest (A) ; amdtest (AT) ; camdtest (A) ; /* RAND */ camdtest (AT) ; /* RAND */ } if (n < NSMALL) { /* -------------------------------------------------------------- */ /* do_matrix with an unpacked matrix */ /* -------------------------------------------------------------- */ /* try with an unpacked matrix, and a non-default dbound */ cm->dbound = 1e-15 ; err = do_matrix (C) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; cm->dbound = 0 ; /* -------------------------------------------------------------- */ /* do_matrix: analyze, factorize, and solve, with many options */ /* -------------------------------------------------------------- */ err = do_matrix (A) ; /* RAND reset */ MAXERR (maxerr, err, 1) ; /* -------------------------------------------------------------- */ /* pretend to solve an LP */ /* -------------------------------------------------------------- */ if (nrow != ncol) { cm->print = 2 ; err = lpdemo (T) ; /* RAND */ cm->print = 1 ; MAXERR (maxerr, err, 1) ; cm->print = 5; CHOLMOD(print_common) ("Common", cm);cm->print=1; cm->nmethods = 1 ; cm->method [0].ordering = CHOLMOD_COLAMD ; err = lpdemo (T) ; /* RAND */ MAXERR (maxerr, err, 1) ; printf ("initial lp error %.1g, dbound %g\n", err, cm->dbound) ; cm->nmethods = 0 ; cm->method [0].ordering = CHOLMOD_GIVEN ; } } progress (1, '|') ; if (n < NSMALL && do_memory) { /* -------------------------------------------------------------- */ /* Exhaustive memory-error handling */ /* -------------------------------------------------------------- */ memory_tests (T) ; /* RAND */ } /* ------------------------------------------------------------------ */ /* free matrices and print results */ /* ------------------------------------------------------------------ */ done: /* an ugly "goto" target; added to minimize code changes * when added "do_nantests", for version 1.0.2 */ CHOLMOD(free_sparse) (&C, cm) ; CHOLMOD(free_sparse) (&AT, cm) ; CHOLMOD(free_sparse) (&A, cm) ; CHOLMOD(free_triplet) (&T, cm) ; fprintf (stderr, "\n " " Test OK") ; if (nrow <= ncol && !singular) { /* maxerr should be NaN if nrow > ncol, so don't print it */ fprintf (stderr, ", maxerr %.1g", maxerr) ; } fprintf (stderr, "\n") ; my_srand (42) ; /* RAND reset */ } /* ---------------------------------------------------------------------- */ /* finalize CHOLMOD */ /* ---------------------------------------------------------------------- */ CHOLMOD(free_dense) (&M1, cm) ; CHOLMOD(finish) (cm) ; cm->print = 5 ; OK (CHOLMOD(print_common) ("cm", cm)) ; printf ("malloc count "ID" inuse "ID"\n", (Int) cm->malloc_count, (Int) cm->memory_inuse) ; OK (cm->malloc_count == 0) ; OK (cm->memory_inuse == 0) ; if (nrow > ncol) { /* maxerr should be NaN, so don't print it */ printf ("All tests successful\n") ; } else { printf ("All tests successful: max error %.1g\n", maxerr) ; } return (0) ; }