// ============================================================================= // === qrest_template.hpp ====================================================== // ============================================================================= // ============================================================================= // === SPQR_qmult ============================================================== // ============================================================================= // wrapper for SuiteSparseQR_qmult (dense), optionally testing memory alloc. template cholmod_dense *SPQR_qmult ( // arguments for SuiteSparseQR_qmult: int method, cholmod_sparse *H, cholmod_dense *Tau, Int *HPinv, cholmod_dense *X, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_dense *Y = NULL ; if (!memory_test) { // just call the method directly; no memory testing Y = SuiteSparseQR_qmult (method, H, Tau, HPinv, X, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; Y = SuiteSparseQR_qmult (method, H, Tau, HPinv, X, cc) ; if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (Y) ; } // ============================================================================= // === SPQR_qmult_sparse ======================================================= // ============================================================================= // wrapper for SuiteSparseQR_qmult (sparse), optionally testing memory alloc. template cholmod_sparse *SPQR_qmult ( // arguments for SuiteSparseQR_qmult: int method, cholmod_sparse *H, cholmod_dense *Tau, Int *HPinv, cholmod_sparse *X, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_sparse *Y = NULL ; if (!memory_test) { // just call the method directly; no memory testing Y = SuiteSparseQR_qmult (method, H, Tau, HPinv, X, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; Y = SuiteSparseQR_qmult (method, H, Tau, HPinv, X, cc); if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (Y) ; } #ifndef NEXPERT // ============================================================================= // === SPQR_qmult (dense case) ================================================= // ============================================================================= // wrapper for SuiteSparseQR_qmult (dense), optionally testing memory alloc. template cholmod_dense *SPQR_qmult ( // arguments for SuiteSparseQR_qmult: int method, SuiteSparseQR_factorization *QR, cholmod_dense *X, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_dense *Y = NULL ; if (!memory_test) { // just call the method directly; no memory testing Y = SuiteSparseQR_qmult (method, QR, X, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; Y = SuiteSparseQR_qmult (method, QR, X, cc) ; if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (Y) ; } // ============================================================================= // === SPQR_qmult (sparse case) ================================================ // ============================================================================= // wrapper for SuiteSparseQR_qmult (sparse), optionally testing memory alloc. template cholmod_sparse *SPQR_qmult ( // arguments for SuiteSparseQR_qmult: int method, SuiteSparseQR_factorization *QR, cholmod_sparse *X, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_sparse *Y = NULL ; if (!memory_test) { // just call the method directly; no memory testing Y = SuiteSparseQR_qmult (method, QR, X, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; Y = SuiteSparseQR_qmult (method, QR, X, cc) ; if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (Y) ; } // ============================================================================= // === SPQR_solve (dense case) ================================================= // ============================================================================= // Wrapper for testing SuiteSparseQR_solve template cholmod_dense *SPQR_solve ( // arguments for SuiteSparseQR_solve: int system, SuiteSparseQR_factorization *QR, cholmod_dense *B, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_dense *X = NULL ; if (!memory_test) { // just call the method directly; no memory testing X = SuiteSparseQR_solve (system, QR, B, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; X = SuiteSparseQR_solve (system, QR, B, cc) ; if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (X) ; } // ============================================================================= // === SPQR_solve (sparse case) ================================================ // ============================================================================= // Wrapper for testing SuiteSparseQR_solve template cholmod_sparse *SPQR_solve ( // arguments for SuiteSparseQR_solve: int system, SuiteSparseQR_factorization *QR, cholmod_sparse *B, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_sparse *X = NULL ; if (!memory_test) { // just call the method directly; no memory testing X = SuiteSparseQR_solve (system, QR, B, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; X = SuiteSparseQR_solve (system, QR, B, cc) ; if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (X) ; } // ============================================================================= // === SPQR_min2norm (dense case) ============================================== // ============================================================================= // Wrapper for testing SuiteSparseQR_min2norm template cholmod_dense *SPQR_min2norm ( // arguments for SuiteSparseQR_min2norm: int ordering, double tol, cholmod_sparse *A, cholmod_dense *B, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_dense *X = NULL ; if (!memory_test) { // just call the method directly; no memory testing X = SuiteSparseQR_min2norm (ordering, tol, A, B, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; X = SuiteSparseQR_min2norm (ordering, tol, A, B, cc) ; if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (X) ; } // ============================================================================= // === SPQR_min2norm (sparse case) ============================================= // ============================================================================= // Wrapper for testing SuiteSparseQR_min2norm template cholmod_sparse *SPQR_min2norm ( // arguments for SuiteSparseQR_min2norm: int ordering, double tol, cholmod_sparse *A, cholmod_sparse *B, cholmod_common *cc, // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { cholmod_sparse *X = NULL ; if (!memory_test) { // just call the method directly; no memory testing X = SuiteSparseQR_min2norm (ordering, tol, A, B, cc) ; } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; X = SuiteSparseQR_min2norm (ordering, tol, A, B, cc) ; if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } return (X) ; } // ============================================================================= // === SPQR_factorize ========================================================== // ============================================================================= // Wrapper for testing SuiteSparseQR_factorize or // SuiteSparseQR_symbolic and SuiteSparseQR_numeric template SuiteSparseQR_factorization *SPQR_factorize ( // arguments for SuiteSparseQR_factorize: int ordering, double tol, cholmod_sparse *A, cholmod_common *cc, // method to use int split, // if 1 use SuiteSparseQR_symbolic followed by // SuiteSparseQR_numeric, if 0 use // SuiteSparseQR_factorize, if 2, do the // numeric factorization twice, just for testing. // if 3 use SuiteSparseQR_C_factorize // if 3 use SuiteSparseQR_C_symbolic / _C_numeric // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { SuiteSparseQR_factorization *QR ; SuiteSparseQR_C_factorization *C_QR ; if (!memory_test) { // just call the method directly; no memory testing if (split == 4) { C_QR = SuiteSparseQR_C_symbolic (ordering, tol >= 0, A, cc) ; SuiteSparseQR_C_numeric (tol, A, C_QR, cc) ; if (C_QR == NULL) { cc->status = CHOLMOD_OUT_OF_MEMORY ; QR = NULL ; } else { QR = (SuiteSparseQR_factorization *) (C_QR->factors) ; if (QR == NULL || QR->QRnum == NULL) { cc->status = CHOLMOD_OUT_OF_MEMORY ; QR = NULL ; } else { // QR itself will be kept; free the C wrapper C_QR->factors = NULL ; cc->status = CHOLMOD_OK ; if (QR == NULL) printf ("Hey!\n") ; } } SuiteSparseQR_C_free (&C_QR, cc) ; } else if (split == 3) { C_QR = SuiteSparseQR_C_factorize (ordering, tol, A, cc) ; int save = cc->status ; if (C_QR == NULL) { QR = NULL ; } else { QR = (SuiteSparseQR_factorization *) (C_QR->factors) ; C_QR->factors = NULL ; SuiteSparseQR_C_free (&C_QR, cc) ; } cc->status = save ; } else if (split == 2) { QR = SuiteSparseQR_symbolic (ordering, tol >= 0 || tol <= SPQR_DEFAULT_TOL, A, cc) ; ASSERT (QR != NULL) ; SuiteSparseQR_numeric (tol, A, QR, cc) ; // just for testing SuiteSparseQR_numeric (tol, A, QR, cc) ; } else if (split == 1) { // split symbolic/numeric, no singletons exploited QR = SuiteSparseQR_symbolic (ordering, tol >= 0 || tol <= SPQR_DEFAULT_TOL, A, cc) ; ASSERT (QR != NULL) ; SuiteSparseQR_numeric (tol, A, QR, cc) ; } else { QR = SuiteSparseQR_factorize (ordering, tol, A, cc) ; } } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; if (split == 4) { C_QR = SuiteSparseQR_C_symbolic (ordering, tol >= 0, A, cc) ; SuiteSparseQR_C_numeric (tol, A, C_QR, cc) ; if (C_QR == NULL) { cc->status = CHOLMOD_OUT_OF_MEMORY ; QR = NULL ; } else { QR = (SuiteSparseQR_factorization *) (C_QR->factors) ; if (QR == NULL || QR->QRnum == NULL) { cc->status = CHOLMOD_OUT_OF_MEMORY ; QR = NULL ; } else { // QR itself will be kept; free the C wrapper C_QR->factors = NULL ; cc->status = CHOLMOD_OK ; if (QR == NULL) printf ("Hey!!\n") ; } } SuiteSparseQR_C_free (&C_QR, cc) ; } else if (split == 3) { C_QR = SuiteSparseQR_C_factorize (ordering, tol, A, cc) ; int save = cc->status ; if (C_QR == NULL) { QR = NULL ; } else { QR = (SuiteSparseQR_factorization *) (C_QR->factors) ; C_QR->factors = NULL ; SuiteSparseQR_C_free (&C_QR, cc) ; } cc->status = save ; } else if (split == 1 || split == 2) { // split symbolic/numeric, no singletons exploited QR = SuiteSparseQR_symbolic (ordering, tol >= 0 || tol <= SPQR_DEFAULT_TOL, A, cc) ; if (cc->status < CHOLMOD_OK) { continue ; } ASSERT (QR != NULL) ; SuiteSparseQR_numeric (tol, A, QR, cc) ; if (cc->status < CHOLMOD_OK) { SuiteSparseQR_free (&QR, cc) ; } } else { QR = SuiteSparseQR_factorize (ordering, tol, A, cc) ; } if (cc->status == CHOLMOD_OK) break ; } normal_memory_handler (cc, true) ; } if (QR == NULL) printf ("huh?? split: %d ordering %d tol %g\n", split, ordering, tol) ; return (QR) ; } #endif // ============================================================================= // === SPQR_qr ================================================================= // ============================================================================= // wrapper for SuiteSparseQR, optionally testing memory allocation template Int SPQR_qr ( // arguments for SuiteSparseQR: int ordering, double tol, Int econ, int getCTX, cholmod_sparse *A, cholmod_sparse *Bsparse, cholmod_dense *Bdense, cholmod_sparse **Zsparse, cholmod_dense **Zdense, cholmod_sparse **R, Int **E, cholmod_sparse **H, Int **HPinv, cholmod_dense **HTau, cholmod_common *cc, // which version to use (C or C++) int use_c_version, // if TRUE use C version, otherwise use C++ // malloc control int memory_test, // if TRUE, test malloc error handling int memory_punt // if TRUE, test punt case ) { Int rank ; if (!memory_test) { // just call the method directly; no memory testing if (use_c_version) { if (sizeof (Int) == sizeof (int64_t)) { rank = SuiteSparseQR_C (ordering, tol, econ, getCTX, A, Bsparse, Bdense, Zsparse, Zdense, R, (int64_t **) E, H, (int64_t **) HPinv, HTau, cc) ; } else { rank = SuiteSparseQR_i_C (ordering, tol, econ, getCTX, A, Bsparse, Bdense, Zsparse, Zdense, R, (int32_t **) E, H, (int32_t **) HPinv, HTau, cc) ; } } else { rank = SuiteSparseQR (ordering, tol, econ, getCTX, A, Bsparse, Bdense, Zsparse, Zdense, R, E, H, HPinv, HTau, cc) ; } } else { // test malloc error handling int64_t tries ; test_memory_handler (cc, true) ; my_punt = memory_punt ; for (tries = 0 ; my_tries < 0 ; tries++) { my_tries = tries ; if (use_c_version) { if (sizeof (Int) == sizeof (int64_t)) { rank = SuiteSparseQR_C (ordering, tol, econ, getCTX, A, Bsparse, Bdense, Zsparse, Zdense, R, (int64_t **) E, H, (int64_t **) HPinv, HTau, cc) ; } else { rank = SuiteSparseQR_i_C (ordering, tol, econ, getCTX, A, Bsparse, Bdense, Zsparse, Zdense, R, (int32_t **) E, H, (int32_t **) HPinv, HTau, cc) ; } } else { rank = SuiteSparseQR (ordering, tol, econ, getCTX, A, Bsparse, Bdense, Zsparse, Zdense, R, E, H, HPinv, HTau, cc); } if (cc->status == CHOLMOD_OK) { break ; } } normal_memory_handler (cc, true) ; } return (rank) ; } // ============================================================================= // === 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 ; #define MY_RAND_MAX 32767 // RAND_MAX assumed to be 32767 int64_t my_rand (void) { next = next * 1103515245 + 12345 ; return ((unsigned)(next/65536) % (MY_RAND_MAX + 1)) ; } void my_srand (unsigned seed) { next = seed ; } unsigned long my_seed (void) { return (next) ; } int64_t nrand (int64_t n) // return a random int64_t between 0 and n-1 { return ((n <= 0) ? 0 : (my_rand ( ) % n)) ; } double xrand ( ) // return a random double between -1 and 1 { double x = ((double) my_rand ( )) / MY_RAND_MAX ; return (2*x-1) ; } double erand (double range) { return (range * xrand ( )) ; } Complex erand (Complex range) { /* Complex x ; x.real ( ) = xrand ( ) ; x.imag ( ) = xrand ( ) ; return (range * x) ; */ Complex i = Complex (0,1) ; return (range * (xrand ( ) + i * xrand ( ))) ; } // ============================================================================= // === getreal ================================================================= // ============================================================================= // return real part of a scalar x inline double getreal (double x) { return (x) ; } inline double getreal (Complex x) { return (x.real ( )) ; } // ============================================================================= // === getimag ================================================================= // ============================================================================= // return imaginary part of a scalar x inline double getimag (double x) // x is an unused parameter { return (0) ; } inline double getimag (Complex x) { return (x.imag ( )) ; } // ============================================================================= // === dense_wrapper =========================================================== // ============================================================================= // place a column-oriented matrix in a cholmod_dense wrapper template cholmod_dense *dense_wrapper ( cholmod_dense *X, Int nrow, Int ncol, Entry *Xx ) { X->xtype = spqr_type ( ) ; X->nrow = nrow ; X->ncol = ncol ; X->d = nrow ; // leading dimension = nrow X->nzmax = nrow * ncol ; X->x = Xx ; X->z = NULL ; // ZOMPLEX case not supported X->dtype = CHOLMOD_DOUBLE ; return (X) ; } // ============================================================================= // === sparse_split ============================================================ // ============================================================================= // Return the real or imaginary part of a packed complex sparse matrix template cholmod_sparse *sparse_split ( cholmod_sparse *A, int part, cholmod_common *cc ) { cholmod_sparse *C ; if (!A || A->xtype != CHOLMOD_COMPLEX || A->nz != NULL) return (NULL) ; if (! (part == 0 || part == 1)) return (NULL) ; int64_t nz = spqr_nnz (A, cc) ; C = spqr_allocate_sparse (A->nrow, A->ncol, nz, TRUE, TRUE, 0, CHOLMOD_REAL, cc) ; Int *Ap = (Int *) A->p ; Int *Ai = (Int *) A->i ; double *Ax = (double *) A->x ; Int *Cp = (Int *) C->p ; Int *Ci = (Int *) C->i ; double *Cx = (double *) C->x ; Int n = A->ncol ; for (Int k = 0 ; k < n+1 ; k++) { Cp [k] = Ap [k] ; } for (int64_t k = 0 ; k < nz ; k++) { Ci [k] = Ai [k] ; } for (Int k = 0 ; k < nz ; k++) { Cx [k] = Ax [2*k + part] ; } return (C) ; } template cholmod_sparse *sparse_real (cholmod_sparse *A, cholmod_common *cc) { return (sparse_split (A, 0, cc)) ; } template cholmod_sparse *sparse_imag (cholmod_sparse *A, cholmod_common *cc) { return (sparse_split (A, 1, cc)) ; } // ============================================================================= // === sparse_merge ============================================================ // ============================================================================= // Add the real and imaginary parts of a matrix (both stored in real form) // into a single matrix. The two parts must have the same nonzero pattern. // A is CHOLMOD_REAL on input and holds the real part, it is CHOLMOD_COMPLEX // on output. A_imag is CHOLMOD_REAL on input; it holds the imaginary part // of A as a real matrix. template int sparse_merge ( cholmod_sparse *A, // input/output cholmod_sparse *A_imag, // input only cholmod_common *cc ) { if (A == NULL || A_imag == NULL) { return (FALSE) ; } int64_t nz1 = spqr_nnz (A, cc) ; int64_t nz2 = spqr_nnz (A_imag, cc) ; if (A->xtype != CHOLMOD_REAL || A_imag->xtype != CHOLMOD_REAL || nz1 != nz2) { return (FALSE) ; } // change A from real to complex spqr_sparse_xtype (CHOLMOD_COMPLEX, A, cc) ; double *Ax = (double *) A->x ; double *Az = (double *) A_imag->x ; // merge in the imaginary part from A_imag into A for (int64_t k = 0 ; k < nz1 ; k++) { Ax [2*k+1] = Az [k] ; } return (TRUE) ; } // ============================================================================= // === sparse_diff ============================================================= // ============================================================================= // Compute C = A-B where A and B are either both real, or both complex template cholmod_sparse *sparse_diff ( cholmod_sparse *A, cholmod_sparse *B, cholmod_common *cc ) { cholmod_sparse *C ; double one [2] = {1,0}, minusone [2] = {-1,0} ; if (spqr_type ( ) == CHOLMOD_REAL) { // C = A - B C = spqr_ssadd (A, B, one, minusone, TRUE, TRUE, cc) ; } else { cholmod_sparse *A_real, *A_imag, *B_real, *B_imag, *C_imag ; A_real = sparse_real (A, cc) ; A_imag = sparse_imag (A, cc) ; B_real = sparse_real (B, cc) ; B_imag = sparse_imag (B, cc) ; // real(C) = real(A) - real (B) C = spqr_ssadd (A_real, B_real, one, minusone, TRUE, TRUE, cc) ; // imag(C) = imag(A) - imag (B) C_imag = spqr_ssadd (A_imag, B_imag, one, minusone, TRUE, TRUE, cc); // C = real(C) + 1i*imag(C) sparse_merge (C, C_imag, cc) ; spqr_free_sparse (&C_imag, cc) ; spqr_free_sparse (&A_real, cc) ; spqr_free_sparse (&A_imag, cc) ; spqr_free_sparse (&B_real, cc) ; spqr_free_sparse (&B_imag, cc) ; } return (C) ; } // ============================================================================= // === permute_columns ========================================================= // ============================================================================= // compute A(:,P) template cholmod_sparse *permute_columns ( cholmod_sparse *A, Int *P, cholmod_common *cc ) { Int m = A->nrow ; Int n = A->ncol ; Int nz = spqr_nnz (A, cc) ; int xtype = spqr_type ( ) ; Int *Ap = (Int *) A->p ; Int *Ai = (Int *) A->i ; Entry *Ax = (Entry *) A->x ; cholmod_sparse *C ; // allocate empty matrix C with space for nz entries C = spqr_allocate_sparse (m, n, nz, TRUE, TRUE, 0, xtype, cc) ; Int *Cp = (Int *) C->p ; Int *Ci = (Int *) C->i ; Entry *Cx = (Entry *) C->x ; // construct column pointers for C for (Int k = 0 ; k < n ; k++) { // column j of A becomes column k of C Int j = P ? P [k] : k ; Cp [k] = Ap [j+1] - Ap [j] ; } spqr_cumsum (n, Cp) ; // copy columns from A to C for (Int k = 0 ; k < n ; k++) { // copy column k of A into column j of C Int j = P ? P [k] : k ; Int pdest = Cp [k] ; Int psrc = Ap [j] ; Int len = Ap [j+1] - Ap [j] ; for (Int t = 0 ; t < len ; t++) { Ci [pdest + t] = Ai [psrc + t] ; Cx [pdest + t] = Ax [psrc + t] ; } } return (C) ; } // ============================================================================= // === sparse_multiply ========================================================= // ============================================================================= // compute A*B where A and B can be both real or both complex template cholmod_sparse *sparse_multiply ( cholmod_sparse *A, cholmod_sparse *B, cholmod_common *cc ) { cholmod_sparse *C ; if (spqr_type ( ) == CHOLMOD_REAL) { // C = A*B C = spqr_ssmult (A, B, 0, TRUE, TRUE, cc) ; } else { // cholmod_ssmult and cholmod_add only work for real matrices cholmod_sparse *A_real, *A_imag, *B_real, *B_imag, *C_imag, *T1, *T2 ; double one [2] = {1,0}, minusone [2] = {-1,0} ; A_real = sparse_real (A, cc) ; A_imag = sparse_imag (A, cc) ; B_real = sparse_real (B, cc) ; B_imag = sparse_imag (B, cc) ; // real(C) = real(A)*real(B) - imag(A)*imag(B) T1 = spqr_ssmult (A_real, B_real, 0, TRUE, TRUE, cc) ; T2 = spqr_ssmult (A_imag, B_imag, 0, TRUE, TRUE, cc) ; C = spqr_ssadd (T1, T2, one, minusone, TRUE, TRUE, cc) ; spqr_free_sparse (&T1, cc) ; spqr_free_sparse (&T2, cc) ; // imag(C) = imag(A)*real(B) + real(A)*imag(B) T1 = spqr_ssmult (A_imag, B_real, 0, TRUE, TRUE, cc) ; T2 = spqr_ssmult (A_real, B_imag, 0, TRUE, TRUE, cc) ; C_imag = spqr_ssadd (T1, T2, one, one, TRUE, TRUE, cc) ; spqr_free_sparse (&T1, cc) ; spqr_free_sparse (&T2, cc) ; // C = real(C) + 1i*imag(C) sparse_merge (C, C_imag, cc) ; spqr_free_sparse (&C_imag, cc) ; spqr_free_sparse (&A_real, cc) ; spqr_free_sparse (&A_imag, cc) ; spqr_free_sparse (&B_real, cc) ; spqr_free_sparse (&B_imag, cc) ; } return (C) ; } // ============================================================================= // === sparse_resid ============================================================ // ============================================================================= // compute norm (A*x-b,1) for A,x, and b all sparse template double sparse_resid ( cholmod_sparse *A, double anorm, cholmod_sparse *X, cholmod_sparse *B, cholmod_common *cc ) { cholmod_sparse *AX, *Resid ; // AX = A*X AX = sparse_multiply (A, X, cc) ; // Resid = AX - B Resid = sparse_diff (AX, B, cc) ; // resid = norm (Resid,1) double resid = spqr_norm_sparse (Resid, 1, cc) ; resid = CHECK_NAN (resid) ; spqr_free_sparse (&AX, cc) ; spqr_free_sparse (&Resid, cc) ; return (CHECK_NAN (resid / anorm)) ; } // ============================================================================= // === dense_resid ============================================================= // ============================================================================= // compute norm (A*x-b,1) for A sparse, x and b dense template double dense_resid ( cholmod_sparse *A, double anorm, cholmod_dense *X, Int nb, Entry *Bx, cholmod_common *cc ) { cholmod_dense *B, Bmatrix, *Resid ; double one [2] = {1,0}, minusone [2] = {-1,0} ; B = dense_wrapper (&Bmatrix, A->nrow, nb, Bx) ; // Resid = B Resid = spqr_copy_dense (B, cc) ; // Resid = A*X - Resid spqr_sdmult (A, FALSE, one, minusone, X, Resid, cc) ; // resid = norm (Resid,1) double resid = spqr_norm_dense (Resid, 1, cc) ; resid = CHECK_NAN (resid) ; spqr_free_dense (&Resid, cc) ; return (CHECK_NAN (resid / anorm)) ; } // ============================================================================= // === check_r_factor ========================================================== // ============================================================================= // compute norm (R'*R - (A(:,P))'*(A(:,P)), 1) / norm (A'*A,1) template double check_r_factor ( cholmod_sparse *R, cholmod_sparse *A, Int *P, cholmod_common *cc ) { cholmod_sparse *RTR, *RT, *C, *CT, *CTC, *D ; // RTR = R'*R RT = spqr_transpose (R, 2, cc) ; RTR = sparse_multiply (RT, R, cc) ; spqr_free_sparse (&RT, cc) ; // C = A(:,P) C = permute_columns (A, P, cc) ; // CTC = C'*C CT = spqr_transpose (C, 2, cc) ; CTC = sparse_multiply (CT, C, cc) ; spqr_free_sparse (&CT, cc) ; spqr_free_sparse (&C, cc) ; double ctcnorm = spqr_norm_sparse (CTC, 1, cc) ; if (ctcnorm == 0) ctcnorm = 1 ; ctcnorm = CHECK_NAN (ctcnorm) ; // D = RTR - CTC D = sparse_diff (RTR, CTC, cc) ; spqr_free_sparse (&CTC, cc) ; spqr_free_sparse (&RTR, cc) ; // err = norm (D,1) double err = spqr_norm_sparse (D, 1, cc) / ctcnorm ; err = CHECK_NAN (err) ; spqr_free_sparse (&D, cc) ; return (CHECK_NAN (err)) ; } // ============================================================================= // === check_qr ================================================================ // ============================================================================= // compute norm (Q*R - A(:,P)) / norm (A) template double check_qr ( cholmod_sparse *Q, cholmod_sparse *R, cholmod_sparse *A, Int *P, double anorm, cholmod_common *cc ) { cholmod_sparse *QR, *C, *D ; // C = A(:,P) C = permute_columns (A, P, cc) ; // QR = Q*R QR = sparse_multiply (Q, R, cc) ; // D = RTR - CTC D = sparse_diff (QR, C, cc) ; spqr_free_sparse (&QR, cc) ; spqr_free_sparse (&C, cc) ; // err = norm (D,1) double err = spqr_norm_sparse (D, 1, cc) / anorm ; err = CHECK_NAN (err) ; spqr_free_sparse (&D, cc) ; return (CHECK_NAN (err)) ; } // ============================================================================= // === Rsolve ================================================================== // ============================================================================= template int Rsolve ( // R is n-by-n, upper triangular with zero-free diagonal Int n, cholmod_sparse *R, Entry *X, // X is n-by-nx, leading dimension n, overwritten with soln Int nx, cholmod_common *cc ) { // Int n = R->n ; Int *Rp = (Int *) R->p ; Int *Ri = (Int *) R->i ; Entry *Rx = (Entry *) R->x ; // check the diagonal for (Int j = 0 ; j < n ; j++) { if (Rp [j] == Rp [j+1] || Ri [Rp [j+1]-1] != j) { printf ("Rsolve: R not upper triangular w/ zero-free diagonal\n") ; return (FALSE) ; } } // do the backsolve for (Int k = 0 ; k < nx ; k++) { for (Int j = n-1 ; j >= 0 ; j--) { Entry rjj = Rx [Rp [j+1]-1] ; if (rjj == (Entry) 0) { printf ("Rsolve: R has an explicit zero on the diagonal\n") ; return (FALSE) ; } X [j] /= rjj ; for (Int p = Rp [j] ; p < Rp [j+1]-1 ; p++) { X [Ri [p]] -= Rx [p] * X [j] ; } } X += n ; } return (TRUE) ; } // ============================================================================= // === create_Q ================================================================ // ============================================================================= // create a sparse Q template cholmod_sparse *create_Q ( cholmod_sparse *H, cholmod_dense *HTau, Int *HPinv, cholmod_common *cc ) { cholmod_sparse *Q, *I ; Int m = H->nrow ; int xtype = spqr_type ( ) ; I = spqr_speye (m, m, xtype, cc) ; Q = SPQR_qmult (1, H, HTau, HPinv, I, cc, m<300, nrand (2)) ; spqr_free_sparse (&I, cc) ; return (Q) ; } // ============================================================================= // === QRsolve ================================================================= // ============================================================================= // solve Ax=b using H, R, and E template double QRsolve ( cholmod_sparse *A, double anorm, Int rank, int method, cholmod_sparse *H, cholmod_dense *HTau, Int *HPinv, cholmod_sparse *R, Int *Qfill, cholmod_dense *Bdense, cholmod_common *cc ) { double one [2] = {1,0}, zero [2] = {0,0}, resid = EMPTY ; int xtype = spqr_type ( ) ; Int n = A->ncol ; Int m = A->nrow ; Int nrhs = Bdense->ncol ; Entry *X, *Y = NULL, *B ; cholmod_dense *Ydense = NULL ; cholmod_dense *Xdense ; B = (Entry *) Bdense->x ; Xdense = spqr_zeros (n, nrhs, xtype, cc) ; X = (Entry *) Xdense->x ; if (method == 0) { // solve Ax=b using H, R, and E // Y = Q'*B Ydense = SPQR_qmult (0, H, HTau, HPinv, Bdense, cc, m < 300, nrand (2)) ; } else { cholmod_sparse *Q, *QT ; // solve using Q instead of qmult Q = create_Q (H, HTau, HPinv, cc) ; QT = spqr_transpose (Q, 2, cc) ; // Y = Q'*B Ydense = spqr_zeros (m, nrhs, xtype, cc) ; spqr_sdmult (QT, FALSE, one, zero, Bdense, Ydense, cc) ; spqr_free_sparse (&Q, cc) ; spqr_free_sparse (&QT, cc) ; } // Y (1:rank) = R (1:rank,1:rank) \ Y (1:rank) Y = (Entry *) Ydense->x ; Int ok = Rsolve (rank, R, Y, nrhs, cc) ; // X = E*Y if (ok) { for (Int kk = 0 ; kk < nrhs ; kk++) { for (Int k = 0 ; k < rank ; k++) { Int j = Qfill ? Qfill [k] : k ; X [j] = Y [k] ; } X += n ; Y += m ; } // check norm (A*x-b), x and b dense resid = dense_resid (A, anorm, Xdense, nrhs, B, cc) ; } spqr_free_dense (&Ydense, cc) ; spqr_free_dense (&Xdense, cc) ; return (CHECK_NAN (resid)) ; } // ============================================================================= // === check_qmult ============================================================= // ============================================================================= // Test qmult template double check_qmult ( cholmod_sparse *H, cholmod_dense *HTau, Int *HPinv, int test_errors, cholmod_common *cc ) { cholmod_sparse *Q, *QT, *Xsparse, *Ssparse, *Zsparse ; cholmod_dense *Xdense, *Zdense, *Sdense, *Ydense ; int xtype = spqr_type ( ) ; Entry *X, *Y, *Z, *S ; double err, maxerr = 0 ; double one [2] = {1,0}, zero [2] = {0,0} ; Entry range = (Entry) 1.0 ; Int k ; Int m = H->nrow ; Q = create_Q (H, HTau, HPinv, cc) ; // construct Q from H QT = spqr_transpose (Q, 2, cc) ; // QT = Q' // compare Q with qmult for sparse and dense X for (Int nx = 0 ; nx < 5 ; nx++) // # of columns of X { Int xsize = m * nx ; // size of X for (Int nz = 1 ; nz <= xsize+1 ; nz *= 16) // # of nonzeros in X { // ----------------------------------------------------------------- // create X as m-by-nx, both sparse and dense // ----------------------------------------------------------------- Xdense = spqr_zeros (m, nx, xtype, cc) ; X = (Entry *) Xdense->x ; for (k = 0 ; k < nz ; k++) { X [nrand (xsize)] += erand (range) ; } Xsparse = spqr_dense_to_sparse (Xdense, TRUE, cc) ; // ----------------------------------------------------------------- // Y = Q'*X for method 0, Y = Q*X for method 1 // ----------------------------------------------------------------- for (int method = 0 ; method <= 1 ; method++) { // Y = Q'*X or Q*X Ydense = SPQR_qmult (method, H, HTau, HPinv, Xdense, cc, m < 300, nrand (2)) ; Y = (Entry *) Ydense->x ; Zdense = spqr_zeros (m, nx, xtype, cc) ; spqr_sdmult (Q, (method == 0), one, zero, Xdense, Zdense, cc) ; Z = (Entry *) Zdense->x ; for (err = 0, k = 0 ; k < xsize ; k++) { double e1 = spqr_abs (Y [k] - Z [k]) ; e1 = CHECK_NAN (e1) ; err = MAX (err, e1) ; } maxerr = MAX (maxerr, err) ; // S = Q'*Xsparse or Q*Xsparse Ssparse = SPQR_qmult ( method, H, HTau, HPinv, Xsparse, cc, m < 300, nrand (2)) ; Sdense = spqr_sparse_to_dense (Ssparse, cc) ; S = (Entry *) Sdense->x ; for (err = 0, k = 0 ; k < xsize ; k++) { double e1 = spqr_abs (S [k] - Y [k]) ; e1 = CHECK_NAN (e1) ; err = MAX (err, e1) ; } maxerr = MAX (maxerr, err) ; spqr_free_dense (&Ydense, cc) ; spqr_free_dense (&Zdense, cc) ; spqr_free_sparse (&Ssparse, cc) ; spqr_free_dense (&Sdense, cc) ; } spqr_free_dense (&Xdense, cc) ; spqr_free_sparse (&Xsparse, cc) ; // ----------------------------------------------------------------- // create X as nx-by-m, both sparse and dense // ----------------------------------------------------------------- Xdense = spqr_zeros (nx, m, xtype, cc) ; X = (Entry *) Xdense->x ; for (k = 0 ; k < nz ; k++) { X [nrand (xsize)] += erand (range) ; } Xsparse = spqr_dense_to_sparse (Xdense, TRUE, cc) ; // ----------------------------------------------------------------- // Y = X*Q' for method 2, Y = X*Q for method 3 // ----------------------------------------------------------------- for (int method = 2 ; method <= 3 ; method++) { // Y = X*Q' or X*Q Ydense = SPQR_qmult (method, H, HTau, HPinv, Xdense, cc, m < 300, nrand (2)) ; Y = (Entry *) Ydense->x ; if (method == 2) { // Zsparse = (X*Q') Zsparse = sparse_multiply (Xsparse, QT, cc) ; } else { // Zsparse = (X*Q) Zsparse = sparse_multiply (Xsparse, Q, cc) ; } Zdense = spqr_sparse_to_dense (Zsparse, cc) ; Z = (Entry *) Zdense->x ; for (err = 0, k = 0 ; k < xsize ; k++) { double e1 = spqr_abs (Y [k] - Z [k]) ; e1 = CHECK_NAN (e1) ; err = MAX (err, e1) ; } maxerr = MAX (maxerr, err) ; // S = X*Q' or X*Q Ssparse = SPQR_qmult ( method, H, HTau, HPinv, Xsparse, cc, m < 300, nrand (2)) ; Sdense = spqr_sparse_to_dense (Ssparse, cc) ; S = (Entry *) Sdense->x ; for (err = 0, k = 0 ; k < xsize ; k++) { double e1 = spqr_abs (S [k] - Y [k]) ; e1 = CHECK_NAN (e1) ; err = MAX (err, e1) ; } maxerr = MAX (maxerr, err) ; spqr_free_dense (&Ydense, cc) ; spqr_free_dense (&Zdense, cc) ; spqr_free_sparse (&Ssparse, cc) ; spqr_free_sparse (&Zsparse, cc) ; spqr_free_dense (&Sdense, cc) ; } spqr_free_dense (&Xdense, cc) ; spqr_free_sparse (&Xsparse, cc) ; } } spqr_free_sparse (&Q, cc) ; spqr_free_sparse (&QT, cc) ; // ------------------------------------------------------------------------- // qmult error conditions // ------------------------------------------------------------------------- // These should fail; expect 6 error messages in the output if (test_errors) { err = 0 ; printf ("The following six errors are expected:\n") ; Xdense = spqr_zeros (m+1, 1, xtype,cc) ; Ydense = SuiteSparseQR_qmult (0, H, HTau, HPinv, Xdense, cc) ; if (Ydense != NULL || cc->status != CHOLMOD_INVALID) err++ ; spqr_free_dense (&Xdense, cc) ; Xdense = spqr_zeros (1, m+1, xtype,cc) ; Ydense = SuiteSparseQR_qmult (2, H, HTau, HPinv, Xdense, cc) ; if (Ydense != NULL || cc->status != CHOLMOD_INVALID) err++ ; Ydense = SuiteSparseQR_qmult (42, H, HTau, HPinv, Xdense, cc) ; if (Ydense != NULL || cc->status != CHOLMOD_INVALID) err++ ; spqr_free_dense (&Xdense, cc) ; Xsparse = spqr_speye (m+1, m+1, xtype, cc) ; Q = SuiteSparseQR_qmult (0, H, HTau, HPinv, Xsparse, cc); if (Q != NULL || cc->status != CHOLMOD_INVALID) err++ ; Q = SuiteSparseQR_qmult (2, H, HTau, HPinv, Xsparse, cc); if (Q != NULL || cc->status != CHOLMOD_INVALID) err++ ; Q = SuiteSparseQR_qmult (9, H, HTau, HPinv, Xsparse, cc); if (Q != NULL || cc->status != CHOLMOD_INVALID) err++ ; printf (" ... error handling done\n\n") ; spqr_free_sparse (&Xsparse, cc) ; maxerr = MAX (maxerr, err) ; } return (CHECK_NAN (maxerr)) ; } // ============================================================================= // === check_rc ================================================================ // ============================================================================= // X = Q'*B has been done, continue with C=R\C and template double check_rc ( Int rank, cholmod_sparse *R, cholmod_sparse *A, Entry *B, cholmod_dense *X, Int nrhs, double anorm, Int *Qfill, cholmod_common *cc ) { double resid = EMPTY ; int xtype = spqr_type ( ) ; cholmod_dense *W ; Int n ; int ok ; Entry *W1, *X1 ; if (!R || !X) { ok = 0 ; } else { n = X->nrow ; X1 = (Entry *) X->x ; // solve X = R\X, overwriting X with solution ok = Rsolve (rank, R, X1, nrhs, cc) ; } if (ok) { // W = E*X // W = (Entry *) spqr_calloc (A->ncol * nrhs, sizeof (Entry), cc); W = spqr_zeros (A->ncol, nrhs, xtype, cc) ; W1 = (Entry *) W->x ; for (Int col = 0 ; col < nrhs ; col++) { for (Int k = 0 ; k < rank ; k++) { Int j = Qfill ? Qfill [k] : k ; if (j < (Int) A->ncol) W1 [j] = X1 [k] ; } W1 += A->ncol ; X1 += n ; } // check norm (A*x-b), x and b dense resid = dense_resid (A, anorm, W, nrhs, B, cc) ; // spqr_free (A->ncol * nrhs, sizeof (Entry), W, cc) ; spqr_free_dense (&W, cc) ; } return (CHECK_NAN (resid)) ; } // ============================================================================= // === transpose =============================================================== // ============================================================================= // Transpose a dense matrix. template cholmod_dense *transpose ( cholmod_dense *Xdense, cholmod_common *cc ) { Entry *X, *Y ; cholmod_dense *Ydense ; if (Xdense == NULL) { printf ("transpose failed!\n") ; return (NULL) ; } Int m = Xdense->nrow ; Int n = Xdense->ncol ; Int ldx = Xdense->d ; int xtype = spqr_type ( ) ; Ydense = spqr_allocate_dense (n, m, n, xtype, cc) ; X = (Entry *) Xdense->x ; Y = (Entry *) Ydense->x ; for (Int i = 0 ; i < m ; i++) { for (Int j = 0 ; j < n ; j++) { Y [j+i*n] = spqr_conj (X [i+j*ldx]) ; } } return (Ydense) ; } // ============================================================================= // === qrtest ================================================================== // ============================================================================= template void qrtest ( cholmod_sparse *A, double errs [5], cholmod_common *cc ) { cholmod_sparse *H, *I, *R, *Q, *Csparse, *Xsparse, *AT, *Bsparse ; cholmod_dense *Cdense, *Xdense, *Bdense, *HTau ; ; double tol = DBL_EPSILON, err, resid, maxerr, maxresid [2][2] ; double tols [ ] = { SPQR_DEFAULT_TOL, -1, 0, DBL_EPSILON } ; Int n, m, nz, *HPinv, ntol, *Ai, *Ap, k, *Qfill, rank, nb, *Cp, *Ci, econ ; int which ; Entry *B, *Ax, *Cx ; int xtype = spqr_type ( ) ; int ordering ; Entry range = (Entry) 1.0 ; errs [0] = EMPTY ; errs [1] = EMPTY ; errs [2] = EMPTY ; errs [3] = EMPTY ; errs [4] = EMPTY ; if (A == NULL) { fprintf (stderr, "qrtest: no input matrix\n") ; return ; } m = A->nrow ; n = A->ncol ; Ap = (Int *) A->p ; Ai = (Int *) A->i ; Ax = (Entry *) A->x ; double anorm = spqr_norm_sparse (A, 1, cc) ; anorm = CHECK_NAN (anorm) ; printf ("\n===========================================================\n") ; printf ("Matrix: %d by %d, nnz(A) = %ld, norm(A,1) = %g\n", (int) m, (int) n, spqr_nnz (A, cc), anorm) ; printf ( "===========================================================\n") ; if (anorm == 0) anorm = 1 ; // these should all be zero, no matter what the matrix maxerr = 0 ; // residuals for well-determined or under-determined systems. If // not rank deficient, these should be zero maxresid [0][0] = 0 ; // for m <= n, default tol, ordering not fixed maxresid [0][1] = 0 ; // for m <= n, all other cases // residuals for least-squares systems (these will not be zero) maxresid [1][0] = 0 ; // for m > n, default tol, ordering not fixed maxresid [1][1] = 0 ; // for m > n, all other cases my_srand (m+n) ; if (m > 45000) { // The only thing returned are the statistics (rank, etc) printf ("\n=== QR huge (expect int overflow):\n") ; rank = SPQR_qr ( 0, 0, 0, 0, A, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, cc, FALSE, FALSE, FALSE) ; return ; } if (MAX (m,n) >= 300) fprintf (stderr, "please wait ... ") ; AT = spqr_transpose (A, 2, cc) ; // ------------------------------------------------------------------------- // test QR routines // ------------------------------------------------------------------------- econ = m ; for (ordering = 0 ; ordering <= 9 ; ordering++) { // skip SPQR_ORDERING_GIVEN, unless the matrix is tiny if (ordering == SPQR_ORDERING_GIVEN && MAX (m,n) > 10) continue ; for (ntol = 0 ; ntol < NTOL ; ntol++) { tol = tols [ntol] ; if // (ntol == 0) // this old test is fragile ... (tol == SPQR_DEFAULT_TOL) // use this instead { // with default tolerance, the fixed ordering can sometimes // fail if the matrix is rank deficient (R cannot be permuted // to upper trapezoidal form). which = (ordering == 0) ; } else { // with non-default tolerance, the solution can sometimes be // poor; this is expected. which = 1 ; } printf ("\n=== QR with ordering %d tol %g:\n", ordering, tol) ; // ----------------------------------------------------------------- // create dense and sparse right-hand-sides // ----------------------------------------------------------------- nb = 5 ; Bdense = spqr_zeros (m, nb, xtype, cc) ; B = (Entry *) Bdense->x ; for (k = 0 ; k < m*nb ; k++) { B [k] = ((double) (my_rand ( ) % 2)) * erand (range) ; } Bsparse = spqr_dense_to_sparse (Bdense, TRUE, cc) ; // ----------------------------------------------------------------- // X = qrsolve(A,B) where X and B are dense // ----------------------------------------------------------------- // X = A\B if (ordering == SPQR_ORDERING_DEFAULT && tol == SPQR_DEFAULT_TOL) { printf ("[ backslach, A and B and X dense: defaults\n") ; Xdense = SuiteSparseQR (A, Bdense, cc) ; printf ("] done backslach, A and B and X dense: defaults\n") ; } else { printf ("[ backslach, A and B and X dense: tol %g order %d\n", tol, ordering) ; Xdense = SuiteSparseQR (ordering, tol, A, Bdense, cc) ; printf ("] done backslach, A B X dense: tol %g order %d\n", tol, ordering) ; } // check norm (A*x-b), x and b dense resid = dense_resid (A, anorm, Xdense, nb, B, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid0b %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Xdense, cc) ; if (cc->useGPU) { // error testing for infeasible GPU memory size_t save = cc->gpuMemorySize ; cc->gpuMemorySize = 1 ; printf ("[ Pretend GPU memory is too small:\n") ; Xdense = SuiteSparseQR (ordering, tol, A, Bdense, cc) ; cc->gpuMemorySize = save ; printf ("] test done infeasible GPU, status %2d, useGPU: %d\n", cc->status, cc->useGPU) ; spqr_free_dense (&Xdense, cc) ; } spqr_free_dense (&Bdense, cc) ; // ----------------------------------------------------------------- // X = qrsolve(A,B) where X and B are sparse // ----------------------------------------------------------------- // X = A\B printf ("[ backslash with sparse B: tol %g\n", tol) ; Xsparse = SuiteSparseQR (ordering, tol, A, Bsparse, cc) ; printf ("] did tol %g\n", tol) ; // check norm (A*x-b), x and b sparse resid = sparse_resid (A, anorm, Xsparse, Bsparse, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid0 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_sparse (&Xsparse, cc) ; spqr_free_sparse (&Bsparse, cc) ; // ----------------------------------------------------------------- // X = qrsolve (A,B) where X and B are sparse, with memory test // ----------------------------------------------------------------- // use B = A and solve AX=B where X is sparse cc->SPQR_shrink = 0 ; // shrink = 0 ; rank = SPQR_qr ( ordering, tol, econ, 2, A, A, NULL, &Xsparse, NULL, NULL, &Qfill, NULL, NULL, NULL, cc, TRUE, m < 300, nrand (2)) ; cc->SPQR_shrink = 1 ; // restore default shrink = 1 ; if // (ntol == 0) // old test is fragile ... (tol == SPQR_DEFAULT_TOL) // use this instead. { printf ("using default tol: %g\n", cc->SPQR_tol_used) ; } // check norm (A*x-b), x and b sparse resid = sparse_resid (A, anorm, Xsparse, A, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid1 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_sparse (&Xsparse, cc) ; spqr_free (n+n, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // X = qrsolve (A,B) where X and B are dense, with memory test // ----------------------------------------------------------------- // use B = dense m-by-2 matrix with some zeros nb = 5 ; Bdense = spqr_zeros (m, nb, xtype, cc) ; B = (Entry *) Bdense->x ; for (k = 0 ; k < m*nb ; k++) { B [k] = (k+1) % 7 ; } rank = SPQR_qr ( ordering, tol, econ, 2, A, NULL, Bdense, NULL, &Xdense, NULL, &Qfill, NULL, NULL, NULL, cc, FALSE, m < 300, nrand (2)) ; // check norm (A*x-b), x and b dense resid = dense_resid (A, anorm, Xdense, nb, B, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid2 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Xdense, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // X = qrsolve (A,B) where X and B are full and H is kept // ----------------------------------------------------------------- cc->SPQR_shrink = 2 ; // shrink = 2 ; rank = SPQR_qr ( ordering, tol, econ, 2, A, NULL, Bdense, NULL, &Xdense, NULL, &Qfill, &H, &HPinv, &HTau, cc, FALSE, m < 300, nrand (2)) ; cc->SPQR_shrink = 1 ; // restore default shrink = 1 ; spqr_free_dense (&HTau, cc) ; spqr_free (m, sizeof (Int), HPinv, cc) ; spqr_free_sparse (&H, cc) ; // check norm (A*x-b), x and b dense resid = dense_resid (A, anorm, Xdense, nb, B, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid3 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Xdense, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [C,R,E] = qr (A,B) where C is sparse and B is full // ----------------------------------------------------------------- cc->SPQR_shrink = 2 ; // shrink = 2 ; rank = SPQR_qr ( ordering, tol, econ, 0, A, NULL, Bdense, &Csparse, NULL, &R, &Qfill, NULL, NULL, NULL, cc, FALSE, m < 300, nrand (2)) ; cc->SPQR_shrink = 1 ; // restore default shrink = 1 ; // compute x=R\C and check norm (A*x-b) Cdense = spqr_sparse_to_dense (Csparse, cc) ; resid = check_rc (rank, R, A, B, Cdense, nb, anorm, Qfill, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid4 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Cdense, cc) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err1: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_sparse (&Csparse, cc) ; spqr_free_sparse (&R, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [C,R,E] = qr (A,B) where C and B are full // ----------------------------------------------------------------- cc->SPQR_shrink = 0 ; // shrink = 0 ; rank = SPQR_qr ( ordering, tol, econ, 0, A, NULL, Bdense, NULL, &Cdense, &R, &Qfill, NULL, NULL, NULL, cc, FALSE, m < 300, nrand (2)) ; cc->SPQR_shrink = 1 ; // restore default shrink = 1 ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err2: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_dense (&Cdense, cc) ; spqr_free_sparse (&R, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [C,R,E] = qr (A,B) where C and B are full, simple wrapper // ----------------------------------------------------------------- SuiteSparseQR (ordering, tol, econ, A, Bdense, &Cdense, &R, &Qfill, cc) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err3: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_dense (&Cdense, cc) ; spqr_free_sparse (&R, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [C,R,E] = qr (A,B) where C and B are sparse, simple wrapper // ----------------------------------------------------------------- Bsparse = spqr_dense_to_sparse (Bdense, TRUE, cc) ; SuiteSparseQR (ordering, tol, econ, A, Bsparse, &Csparse, &R, &Qfill, cc) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err4: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_sparse (&Csparse, cc) ; spqr_free_sparse (&Bsparse, cc) ; spqr_free_sparse (&R, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [CT,R,E] = qr (A,B), but do not return R // ----------------------------------------------------------------- rank = SPQR_qr ( ordering, tol, econ, 1, A, NULL, Bdense, &Csparse, NULL, NULL, &Qfill, NULL, NULL, NULL, cc, FALSE, m < 300, nrand (2)) ; spqr_free_sparse (&Csparse, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [Q,R,E] = qr (A), Q in Householder form // ----------------------------------------------------------------- rank = SPQR_qr ( ordering, tol, econ, -1, A, NULL, NULL, NULL, NULL, &R, &Qfill, &H, &HPinv, &HTau, cc, FALSE, m < 300, nrand (2)) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err5: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; // solve Ax=b using Householder form resid = QRsolve (A, anorm, rank, 0, H, HTau, HPinv, R, Qfill, Bdense, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid5 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; // solve Ax=b using Q matrix form resid = QRsolve (A, anorm, rank, 1, H, HTau, HPinv, R, Qfill, Bdense, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid6 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&HTau, cc) ; spqr_free_sparse (&H, cc) ; spqr_free (m, sizeof (Int), HPinv, cc) ; spqr_free (n, sizeof (Int), Qfill, cc) ; spqr_free_sparse (&R, cc) ; // ----------------------------------------------------------------- // [Q,R,E] = qr (A), non-economy // ----------------------------------------------------------------- cc->SPQR_shrink = 0 ; // shrink = 0 ; I = spqr_speye (m, m, xtype, cc) ; rank = SPQR_qr ( ordering, tol, m, 1, A, I, NULL, &Q, NULL, &R, &Qfill, NULL, NULL, NULL, cc, FALSE, m<300, nrand (2)) ; cc->SPQR_shrink = 1 ; // restore default shrink = 1 ; // ensure norm (Q*R - A*E) is small err = check_qr (Q, R, A, Qfill, anorm, cc) ; printf ("order %d : Q*R-A*E Err6: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_sparse (&I, cc) ; spqr_free_sparse (&R, cc) ; spqr_free_sparse (&Q, cc) ; spqr_free (n+m, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [Q,R,E] = qr (A), non-economy, using simple wrapper // ----------------------------------------------------------------- if (nrand (2)) { // use C version if (sizeof (Int) == sizeof (int64_t)) { SuiteSparseQR_C_QR (ordering, tol, m, A, &Q, &R, (int64_t **) &Qfill, cc) ; } else { SuiteSparseQR_i_C_QR (ordering, tol, m, A, &Q, &R, (int32_t **) &Qfill, cc) ; } } else { // use C++ version SuiteSparseQR (ordering, tol, m, A, &Q, &R, &Qfill, cc) ; } // ensure norm (Q*R - A*E) is small err = check_qr (Q, R, A, Qfill, anorm, cc) ; printf ("order %d : Q*R-A*E Err7: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_sparse (&R, cc) ; spqr_free_sparse (&Q, cc) ; spqr_free (n+m, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [R,E] = qr (A) // ----------------------------------------------------------------- rank = SPQR_qr ( ordering, tol, econ, 0, A, NULL, NULL, NULL, NULL, &R, &Qfill, NULL, NULL, NULL, cc, FALSE, m < 300, nrand (2)) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err8: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; Int rank1 = rank ; spqr_free_sparse (&R, cc) ; spqr_free (n, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [R,E] = qr (A) using simple wrapper // ----------------------------------------------------------------- SuiteSparseQR (ordering, tol, econ, A, &R, &Qfill, cc) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err9: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_sparse (&R, cc) ; spqr_free (n, sizeof (Int), Qfill, cc) ; // ----------------------------------------------------------------- // [ ] = qr (A) // ----------------------------------------------------------------- // The only thing returned are the statistics (rank, etc) cc->SPQR_shrink = 0 ; // shrink = 0 ; rank = SPQR_qr ( ordering, tol, econ, 0, A, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, cc, FALSE, m < 300, nrand (2)) ; cc->SPQR_shrink = 1 ; // restore default shrink = 1 ; err = (rank != rank1) ; printf ("order %d : rank %6d %6d Err10: %g\n", ordering, (int) rank, (int) rank1, err) ; maxerr = MAX (maxerr, err) ; // ----------------------------------------------------------------- // [C,H,R,E] = qr (A) // ----------------------------------------------------------------- rank = SPQR_qr ( ordering, tol, econ, 0, A, NULL, Bdense, &Csparse, NULL, &R, &Qfill, &H, &HPinv, &HTau, cc, FALSE, m < 300, nrand (2)) ; // compute x=R\C and check norm (A*x-b) Cdense = spqr_sparse_to_dense (Csparse, cc) ; resid = check_rc (rank, R, A, B, Cdense, nb, anorm, Qfill, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("Resid7 %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Cdense, cc) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err11: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_sparse (&Csparse, cc) ; spqr_free_sparse (&R, cc) ; // compare Q with qmult err = check_qmult (H, HTau, HPinv, ordering == 2 && /* ntol == 0 */ tol == SPQR_DEFAULT_TOL, cc) ; printf ("order %d : check qmult Err12: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_dense (&HTau, cc) ; spqr_free_sparse (&H, cc) ; spqr_free (m, sizeof (Int), HPinv, cc) ; spqr_free (n+nb, sizeof (Int), Qfill, cc) ; spqr_free_dense (&Bdense, cc) ; // ----------------------------------------------------------------- // [H,R,E] = qr (A), simple wrapper // ----------------------------------------------------------------- SuiteSparseQR (ordering, tol, econ, A, &R, &Qfill, &H, &HPinv, &HTau, cc) ; // check that R'*R = (A*E)'*(A*E) err = check_r_factor (R, A, Qfill, cc) ; printf ("order %d : R'R-(A*E)'*(A*E), Err13: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; // compare Q with qmult err = check_qmult (H, HTau, HPinv, FALSE, cc) ; printf ("order %d : check qmult Err14: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; spqr_free_sparse (&R, cc) ; spqr_free_dense (&HTau, cc) ; spqr_free_sparse (&H, cc) ; spqr_free (m, sizeof (Int), HPinv, cc) ; spqr_free (n, sizeof (Int), Qfill, cc) ; #ifndef NEXPERT // ================================================================= // === expert routines ============================================= // ================================================================= SuiteSparseQR_factorization *QR ; cholmod_dense *XT, *Zdense, *BT, *Ydense ; cholmod_sparse *Ysparse ; // ----------------------------------------------------------------- // QR = qr (A), then solve // ----------------------------------------------------------------- for (int split = 0 ; split <= 4 ; split++) { QR = SPQR_factorize (ordering, tol, A, cc, split, m < 300, nrand (2)) ; // split == 4 does not use singletons, so it can fail if // rank < n int wh = which || (split == 4) ; // solve Ax=b nb = 5 ; Bdense = spqr_zeros (m, nb, xtype, cc) ; B = (Entry *) Bdense->x ; for (k = 0 ; k < m*nb ; k++) { B [k] = erand (range) ; } // Y = Q'*B Ydense = SPQR_qmult (SPQR_QTX, QR, Bdense, cc, m < 300, nrand (2)) ; // X = R\(E*Y) Xdense = SPQR_solve (SPQR_RETX_EQUALS_B, QR, Ydense, cc, m < 300, nrand (2)) ; // check norm (A*x-b), x and b dense resid = dense_resid (A, anorm, Xdense, nb, B, cc) ; maxresid [m>n][wh] = MAX (maxresid [m>n][wh], resid) ; printf ("Resid8_%d %d %d %d : %g (%d) tol %g\n", split, m>n, (int) ntol, ordering, resid, wh, tol) ; spqr_free_dense (&Xdense, cc) ; spqr_free_dense (&Ydense, cc) ; // Y = (B'*Q)' BT = transpose (Bdense, cc) ; XT = SPQR_qmult (SPQR_XQ, QR, BT, cc, m < 300, nrand (2)) ; Ydense = transpose (XT, cc) ; spqr_free_dense (&XT, cc) ; spqr_free_dense (&BT, cc) ; // X = R\(E*Y) Xdense = SPQR_solve (SPQR_RETX_EQUALS_B, QR, Ydense, cc, m < 300, nrand (2)) ; // check norm (A*x-b), x and b dense resid = dense_resid (A, anorm, Xdense, nb, B, cc) ; maxresid [m>n][wh] = MAX (maxresid [m>n][wh], resid) ; printf ("Resid9_%d %d %d %d : %g (%d) tol %g\n", split, m>n, (int) ntol, ordering, resid, wh, tol) ; spqr_free_dense (&Xdense, cc) ; spqr_free_dense (&Ydense, cc) ; // ------------------------------------------------------------- // error testing // ------------------------------------------------------------- if (ordering == 0 && /* ntol == 0 */ tol == SPQR_DEFAULT_TOL) { printf ("Error handling ... expect 3 error messages: \n") ; err = (SuiteSparseQR_qmult (-1, QR, Bdense, cc) != NULL) ; spqr_free_dense (&Bdense, cc) ; Bdense = spqr_zeros (m+1, 1, xtype, cc) ; err += (SuiteSparseQR_qmult (SPQR_QX,QR,Bdense,cc) != NULL); spqr_free_dense (&Bdense, cc) ; Bdense = spqr_zeros (1, m+1, xtype, cc) ; err += (SuiteSparseQR_qmult (SPQR_XQ,QR,Bdense,cc) != NULL); if (QR->n1cols > 0) { // this will fail; cannot refactorize with singletons printf ("Error handling ... expect error message:\n") ; err += (SuiteSparseQR_numeric (tol, A, QR, cc) != FALSE) ; } printf ("order %d : error handling Err15: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; printf (" ... error handling done\n\n") ; } spqr_free_dense (&Bdense, cc) ; // ------------------------------------------------------------- // solve A'x=b nb = 5 ; Bdense = spqr_zeros (n, nb, xtype, cc) ; B = (Entry *) Bdense->x ; for (k = 0 ; k < n*nb ; k++) { B [k] = erand (range) ; } // Y = R'\(E'*B) Ydense = SPQR_solve (SPQR_RTX_EQUALS_ETB, QR, Bdense, cc, m < 300, nrand (2)) ; // X = Q*Y Xdense = SPQR_qmult (SPQR_QX, QR, Ydense, cc, m < 300, nrand (2)) ; // check norm (A'*x-b), x and b dense resid = dense_resid (AT, anorm, Xdense, nb, B, cc) ; maxresid [m (&Xdense, cc) ; spqr_free_dense (&Ydense, cc) ; // ------------------------------------------------------------- // error testing // ------------------------------------------------------------- if (!split && ordering == 0 && /* ntol == 0 */ tol == SPQR_DEFAULT_TOL) { printf ("Error testing ... expect 3 error messages:\n") ; err = (SuiteSparseQR_solve (-1, QR, Bdense, cc) != NULL) ; spqr_free_dense (&Bdense, cc) ; err += (SuiteSparseQR_solve (SPQR_RTX_EQUALS_ETB, QR, Bdense, cc) != NULL) ; Bdense = spqr_zeros (n+1, 1, xtype, cc) ; err += (SuiteSparseQR_solve (SPQR_RTX_EQUALS_ETB, QR, Bdense, cc) != NULL) ; printf ("order %d : error handling Err16: %g\n", ordering, err) ; maxerr = MAX (maxerr, err) ; printf (" ... error handling done\n\n") ; } SuiteSparseQR_free (&QR, cc) ; spqr_free_dense (&Bdense, cc) ; } // ----------------------------------------------------------------- // QR = qr (A'), then solve // ----------------------------------------------------------------- // use qmult to solve min-2-norm problem QR = SuiteSparseQR_factorize (ordering, tol, AT, cc) ; nb = 5 ; Bdense = spqr_zeros (m, nb, xtype, cc) ; B = (Entry *) Bdense->x ; for (k = 0 ; k < m*nb ; k++) { B [k] = erand (range) ; } // solve X = R'\B Xdense = SPQR_solve (SPQR_RTX_EQUALS_B, QR, Bdense, cc, m < 300, nrand (2)) ; spqr_free_dense (&Xdense, cc) ; // solve X = R'\(E'*B) Xdense = SPQR_solve (SPQR_RTX_EQUALS_ETB, QR, Bdense, cc, m < 300, nrand (2)) ; // Y = Q*X Ydense = SPQR_qmult (SPQR_QX, QR, Xdense, cc, m < 300, nrand (2)) ; // check norm (A*y-b), y and b dense resid = dense_resid (A, anorm, Ydense, nb, B, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("ResidB %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Ydense, cc) ; // Y = (X'*Q')' XT = transpose (Xdense, cc) ; Zdense = SPQR_qmult (SPQR_XQT, QR, XT, cc, m < 300, nrand (2)) ; Ydense = transpose (Zdense, cc) ; spqr_free_dense (&XT, cc) ; spqr_free_dense (&Zdense, cc) ; // check norm (A*y-b), y and b dense resid = dense_resid (A, anorm, Ydense, nb, B, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("ResidC %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Ydense, cc) ; spqr_free_dense (&Xdense, cc) ; // ----------------------------------------------------------------- // min 2-norm solution using min2norm // ----------------------------------------------------------------- Xdense = SPQR_min2norm (ordering, tol, A, Bdense, cc, m < 300, nrand (2)) ; // check norm (A*x-b), y and b dense resid = dense_resid (A, anorm, Xdense, nb, B, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("ResidD %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_dense (&Xdense, cc) ; spqr_free_dense (&Bdense, cc) ; // ----------------------------------------------------------------- // sparse case // ----------------------------------------------------------------- nb = 5 ; Bdense = spqr_zeros (m, nb, xtype, cc) ; B = (Entry *) Bdense->x ; for (k = 0 ; k < m*nb ; k++) { B [k] = ((double) (my_rand ( ) % 2)) * erand (range) ; } Bsparse = spqr_dense_to_sparse (Bdense, TRUE, cc) ; spqr_free_dense (&Bdense, cc) ; // solve X = R'\(E'*B) Xsparse = NULL ; Xsparse = SPQR_solve (SPQR_RTX_EQUALS_ETB, QR, Bsparse, cc, m < 300, nrand (2)) ; // Y = Q*X Ysparse = NULL ; Ysparse = SPQR_qmult (SPQR_QX, QR, Xsparse, cc, m < 300, nrand (2)) ; // check norm (A*y-b), y and b sparse resid = sparse_resid (A, anorm, Ysparse, Bsparse, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("ResidE %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_sparse (&Xsparse, cc) ; spqr_free_sparse (&Ysparse, cc) ; Xsparse = SPQR_min2norm (ordering, tol, A, Bsparse, cc, m < 300, nrand (2)) ; // check norm (A*x-b), x and b sparse resid = sparse_resid (A, anorm, Xsparse, Bsparse, cc) ; maxresid [m>n][which] = MAX (maxresid [m>n][which], resid) ; printf ("ResidF %d %d %d : %g\n", m>n, (int) ntol, ordering, resid) ; spqr_free_sparse (&Xsparse, cc) ; spqr_free_sparse (&Bsparse, cc) ; SuiteSparseQR_free (&QR, cc) ; #endif } } // ------------------------------------------------------------------------- // check error handling // ------------------------------------------------------------------------- printf ("Check error handling, one error message is expected:\n") ; cholmod_dense *Bgunk = spqr_ones (m+1, 1, xtype, cc) ; rank = SuiteSparseQR ( 0, 0, econ, -1, A, NULL, Bgunk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, cc) ; spqr_free_dense (&Bgunk, cc) ; err = (rank != EMPTY) ; maxerr = MAX (maxerr, err) ; // rank should be EMPTY printf (" ... error handling done\n\n") ; // ------------------------------------------------------------------------- // test non-user callable functions // ------------------------------------------------------------------------- // attempt to permute A to upper triangular form Int *Qtrap ; rank = spqr_trapezoidal (n, Ap, Ai, Ax, 0, NULL, FALSE, &Cp, &Ci, &Cx, &Qtrap, cc) ; printf ("Rank of A, if A*P permutable to upper trapezoidal: %d\n", (int) rank) ; if (Cp != NULL) { nz = Cp [n] ; spqr_free (n+1, sizeof (Int), Cp, cc) ; spqr_free (nz, sizeof (Int), Ci, cc) ; spqr_free (nz, sizeof (Entry), Cx, cc) ; spqr_free (n, sizeof (Int), Qtrap, cc) ; } spqr_free_sparse (&AT, cc) ; // ------------------------------------------------------------------------- // test the C API // ------------------------------------------------------------------------- qrtest_C (A, anorm, errs, maxresid, cc) ; // ------------------------------------------------------------------------- // final results // ------------------------------------------------------------------------- errs [0] = CHECK_NAN (maxerr) ; errs [1] = CHECK_NAN (maxresid [0][0]) ; errs [2] = CHECK_NAN (maxresid [0][1]) ; errs [3] = CHECK_NAN (maxresid [1][0]) ; errs [4] = CHECK_NAN (maxresid [1][1]) ; } // ============================================================================= // === do_matrix2 ============================================================== // ============================================================================= // Read in a matrix, and use it to test SuiteSparseQR // If kind == 0, then the first two residuals should be low. template int do_matrix2 (int kind, cholmod_sparse *A, cholmod_common *cc) { double errs [5] = {0,0,0,0,0} ; Int m = A->nrow ; Int n = A->ncol ; // ------------------------------------------------------------------------- // use it to test SuiteSparseQR // ------------------------------------------------------------------------- if (A->xtype == CHOLMOD_COMPLEX && A->stype == 0) { qrtest (A, errs, cc) ; } else if (A->xtype == CHOLMOD_REAL) { if (A->stype != 0) { cholmod_sparse *A1 ; A1 = spqr_copy (A, 0, 1, cc) ; qrtest (A1, errs, cc) ; spqr_free_sparse (&A1, cc) ; } else { qrtest (A, errs, cc) ; } } else { // cannot handle ZOMPLEX, PATTERN, or symmetric/Hermitian COMPLEX fprintf (stderr, "invalid matrix\n") ; errs [0] = 1 ; } // ------------------------------------------------------------------------- // report the results // ------------------------------------------------------------------------- if (kind == 0) { printf ("First Resid and ") ; } printf ("Err should be low:\n") ; printf ("RESULT: Err %8.1e Resid %8.1e %8.1e", errs [0], errs [1], errs [2]) ; if (m == n) { printf (" ") ; } else { printf (" %8.1e %8.1e", errs [3], errs [4]) ; } if (errs [0] > 1e-10) { printf (" : FAIL\n") ; fprintf (stderr, "Error: %g FAIL\n", errs [0]) ; return (1) ; } // if kind == 0, then this full-rank matrix should have low residual if (kind == 0 && (errs [1] > 1e-10)) { printf (" : FAIL\n") ; fprintf (stderr, "error: %g FAIL\n", errs [1]) ; return (1) ; } printf (" : OK.") ; fprintf (stderr, "OK.") ; return (0) ; } // ============================================================================= // === do_matrix =============================================================== // ============================================================================= // Read in a matrix, and use it to test SuiteSparseQR // If kind == 0, then the first two residuals should be low. template int do_matrix (int kind, FILE *file, cholmod_common *cc) { cholmod_sparse *A ; int nfail0 = 0 ; int nfail1 = 0 ; int nfail2 = 0 ; int nfail3 = 0 ; // ------------------------------------------------------------------------- // read in the matrix // ------------------------------------------------------------------------- A = spqr_read_sparse (file, cc) ; if (A == NULL) { fprintf (stderr, "Unable to read matrix\n") ; return (1) ; } Int m = A->nrow ; Int n = A->ncol ; fprintf (stderr, "%5d by %5d : ", (int) m, (int) n) ; if (sizeof (int64_t) > sizeof (int) && (m > 10000 || n > 10000)) { fprintf (stderr, "(test skipped on 64-bit systems)\n") ; spqr_free_sparse (&A, cc) ; return (0) ; } // defaults cc->SPQR_grain = 1 ; // no parallel analysis printf ("\nBeginning CPU tests [\n") ; fprintf (stderr, " CPU ") ; nfail0 = do_matrix2 (kind, A, cc) ; // non-defaults (will not use the GPU) cc->SPQR_grain = 4 ; // grain size relative to total work nfail2 = do_matrix2 (kind, A, cc) ; cc->SPQR_grain = 1 ; // no parallel analysis printf ("\nCPU tests done ]\n") ; // test the GPU, if installed #ifdef SPQR_HAS_CUDA cc->useGPU = TRUE ; // was 3.5 * ((size_t) 1024 * 1024 * 1024) ; size_t totmem, availmem ; double t = SUITESPARSE_TIME ; spqr_gpu_memorysize (&totmem, &availmem, cc) ; t = SUITESPARSE_TIME - t ; cc->gpuMemorySize = availmem ; printf ("\nBeginning GPU tests, GPU memory %g MB warmup time %g[\n", (double) (cc->gpuMemorySize) / (1024*1024), t) ; fprintf (stderr, " GPU ") ; nfail1 = do_matrix2 (kind, A, cc) ; printf ("\nGPU tests done ]\n") ; if (m > 200) { // try with a tiny GPU memory size, but only for a few matrices // in the test set. Each front will go in its own stage. printf ("\nBeginning GPU tests with tiny GPU memory [\n") ; cc->gpuMemorySize = 0 ; nfail3 = do_matrix2 (kind, A, cc) ; // restore defaults cc->useGPU = FALSE ; printf ("\nGPU tests done (tiny memory) ]\n") ; } #endif spqr_free_sparse (&A, cc) ; printf ("\n") ; fprintf (stderr, "\n") ; return (nfail0 + nfail1 + nfail2 + nfail3) ; }