1 2 #include <petscsys.h> 3 #include <../src/mat/impls/aij/seq/aij.h> 4 #include <../src/mat/impls/sbaij/seq/cholmod/cholmodimpl.h> 5 6 EXTERN_C_BEGIN 7 #include <SuiteSparseQR_C.h> 8 EXTERN_C_END 9 10 static PetscErrorCode MatWrapCholmod_SPQR_seqaij(Mat A, PetscBool values, cholmod_sparse *C, PetscBool *aijalloc, PetscBool *valloc) 11 { 12 Mat_SeqAIJ *aij; 13 Mat AT; 14 const PetscScalar *aa; 15 PetscScalar *ca; 16 const PetscInt *ai, *aj; 17 PetscInt n = A->cmap->n, i, j, k, nz; 18 SuiteSparse_long *ci, *cj; /* SuiteSparse_long is the only choice for SPQR */ 19 PetscBool vain = PETSC_FALSE, flg; 20 21 PetscFunctionBegin; 22 PetscCall(PetscObjectTypeCompare((PetscObject)A, MATNORMALHERMITIAN, &flg)); 23 if (flg) { 24 PetscCall(MatNormalHermitianGetMat(A, &A)); 25 } else if (!PetscDefined(USE_COMPLEX)) { 26 PetscCall(PetscObjectTypeCompare((PetscObject)A, MATNORMAL, &flg)); 27 if (flg) PetscCall(MatNormalGetMat(A, &A)); 28 } 29 /* cholmod_sparse is compressed sparse column */ 30 PetscCall(MatIsSymmetric(A, 0.0, &flg)); 31 if (flg) { 32 PetscCall(PetscObjectReference((PetscObject)A)); 33 AT = A; 34 } else { 35 PetscCall(MatTranspose(A, MAT_INITIAL_MATRIX, &AT)); 36 } 37 aij = (Mat_SeqAIJ *)AT->data; 38 ai = aij->j; 39 aj = aij->i; 40 for (j = 0, nz = 0; j < n; j++) nz += aj[j + 1] - aj[j]; 41 PetscCall(PetscMalloc2(n + 1, &cj, nz, &ci)); 42 if (values) { 43 vain = PETSC_TRUE; 44 PetscCall(PetscMalloc1(nz, &ca)); 45 PetscCall(MatSeqAIJGetArrayRead(AT, &aa)); 46 } 47 for (j = 0, k = 0; j < n; j++) { 48 cj[j] = k; 49 for (i = aj[j]; i < aj[j + 1]; i++, k++) { 50 ci[k] = ai[i]; 51 if (values) ca[k] = aa[i]; 52 } 53 } 54 cj[j] = k; 55 *aijalloc = PETSC_TRUE; 56 *valloc = vain; 57 if (values) PetscCall(MatSeqAIJRestoreArrayRead(AT, &aa)); 58 59 PetscCall(PetscMemzero(C, sizeof(*C))); 60 61 C->nrow = (size_t)AT->cmap->n; 62 C->ncol = (size_t)AT->rmap->n; 63 C->nzmax = (size_t)nz; 64 C->p = cj; 65 C->i = ci; 66 C->x = values ? ca : 0; 67 C->stype = 0; 68 C->itype = CHOLMOD_LONG; 69 C->xtype = values ? CHOLMOD_SCALAR_TYPE : CHOLMOD_PATTERN; 70 C->dtype = CHOLMOD_DOUBLE; 71 C->sorted = 1; 72 C->packed = 1; 73 74 PetscCall(MatDestroy(&AT)); 75 PetscFunctionReturn(PETSC_SUCCESS); 76 } 77 78 static PetscErrorCode MatFactorGetSolverType_seqaij_SPQR(Mat A, MatSolverType *type) 79 { 80 PetscFunctionBegin; 81 *type = MATSOLVERSPQR; 82 PetscFunctionReturn(PETSC_SUCCESS); 83 } 84 85 #define GET_ARRAY_READ 0 86 #define GET_ARRAY_WRITE 1 87 88 static PetscErrorCode MatSolve_SPQR_Internal(Mat F, cholmod_dense *cholB, cholmod_dense **_Y_handle) 89 { 90 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 91 cholmod_dense *Y_handle = NULL, *QTB_handle = NULL, *Z_handle = NULL; 92 93 PetscFunctionBegin; 94 if (!chol->normal) { 95 QTB_handle = SuiteSparseQR_C_qmult(SPQR_QTX, chol->spqrfact, cholB, chol->common); 96 PetscCheck(QTB_handle, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "SuiteSparseQR_C_qmult failed"); 97 Y_handle = SuiteSparseQR_C_solve(SPQR_RETX_EQUALS_B, chol->spqrfact, QTB_handle, chol->common); 98 PetscCheck(Y_handle, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "SuiteSparseQR_C_solve failed"); 99 } else { 100 Z_handle = SuiteSparseQR_C_solve(SPQR_RTX_EQUALS_ETB, chol->spqrfact, cholB, chol->common); 101 PetscCheck(Z_handle, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "SuiteSparseQR_C_solve failed"); 102 Y_handle = SuiteSparseQR_C_solve(SPQR_RETX_EQUALS_B, chol->spqrfact, Z_handle, chol->common); 103 PetscCheck(Y_handle, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "SuiteSparseQR_C_solve failed"); 104 PetscCallExternal(!cholmod_l_free_dense, &Z_handle, chol->common); 105 } 106 *_Y_handle = Y_handle; 107 PetscCallExternal(!cholmod_l_free_dense, &QTB_handle, chol->common); 108 PetscFunctionReturn(PETSC_SUCCESS); 109 } 110 111 static PetscErrorCode MatSolve_SPQR(Mat F, Vec B, Vec X) 112 { 113 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 114 cholmod_dense cholB, *Y_handle = NULL; 115 PetscInt n; 116 PetscScalar *v; 117 118 PetscFunctionBegin; 119 PetscCall(VecWrapCholmod(B, GET_ARRAY_READ, &cholB)); 120 PetscCall(MatSolve_SPQR_Internal(F, &cholB, &Y_handle)); 121 PetscCall(VecGetLocalSize(X, &n)); 122 PetscCall(VecGetArrayWrite(X, &v)); 123 PetscCall(PetscArraycpy(v, (PetscScalar *)(Y_handle->x), n)); 124 PetscCall(VecRestoreArrayWrite(X, &v)); 125 PetscCallExternal(!cholmod_l_free_dense, &Y_handle, chol->common); 126 PetscCall(VecUnWrapCholmod(B, GET_ARRAY_READ, &cholB)); 127 PetscFunctionReturn(PETSC_SUCCESS); 128 } 129 130 static PetscErrorCode MatMatSolve_SPQR(Mat F, Mat B, Mat X) 131 { 132 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 133 cholmod_dense cholB, *Y_handle = NULL; 134 PetscScalar *v; 135 PetscInt lda; 136 137 PetscFunctionBegin; 138 PetscCall(MatDenseWrapCholmod(B, GET_ARRAY_READ, &cholB)); 139 PetscCall(MatSolve_SPQR_Internal(F, &cholB, &Y_handle)); 140 PetscCall(MatDenseGetArrayWrite(X, &v)); 141 PetscCall(MatDenseGetLDA(X, &lda)); 142 if ((size_t)lda == Y_handle->d) { 143 PetscCall(PetscArraycpy(v, (PetscScalar *)(Y_handle->x), lda * Y_handle->ncol)); 144 } else { 145 for (size_t j = 0; j < Y_handle->ncol; j++) PetscCall(PetscArraycpy(&v[j * lda], &(((PetscScalar *)Y_handle->x)[j * Y_handle->d]), Y_handle->nrow)); 146 } 147 PetscCall(MatDenseRestoreArrayWrite(X, &v)); 148 PetscCallExternal(!cholmod_l_free_dense, &Y_handle, chol->common); 149 PetscCall(MatDenseUnWrapCholmod(B, GET_ARRAY_READ, &cholB)); 150 PetscFunctionReturn(PETSC_SUCCESS); 151 } 152 153 static PetscErrorCode MatSolveTranspose_SPQR_Internal(Mat F, cholmod_dense *cholB, cholmod_dense **_Y_handle) 154 { 155 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 156 cholmod_dense *Y_handle = NULL, *RTB_handle = NULL; 157 158 PetscFunctionBegin; 159 RTB_handle = SuiteSparseQR_C_solve(SPQR_RTX_EQUALS_ETB, chol->spqrfact, cholB, chol->common); 160 PetscCheck(RTB_handle, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "SuiteSparseQR_C_solve failed"); 161 Y_handle = SuiteSparseQR_C_qmult(SPQR_QX, chol->spqrfact, RTB_handle, chol->common); 162 PetscCheck(Y_handle, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "SuiteSparseQR_C_qmult failed"); 163 *_Y_handle = Y_handle; 164 PetscCallExternal(!cholmod_l_free_dense, &RTB_handle, chol->common); 165 PetscFunctionReturn(PETSC_SUCCESS); 166 } 167 168 static PetscErrorCode MatSolveTranspose_SPQR(Mat F, Vec B, Vec X) 169 { 170 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 171 cholmod_dense cholB, *Y_handle = NULL; 172 PetscInt n; 173 PetscScalar *v; 174 175 PetscFunctionBegin; 176 PetscCall(VecWrapCholmod(B, GET_ARRAY_READ, &cholB)); 177 PetscCall(MatSolveTranspose_SPQR_Internal(F, &cholB, &Y_handle)); 178 PetscCall(VecGetLocalSize(X, &n)); 179 PetscCall(VecGetArrayWrite(X, &v)); 180 PetscCall(PetscArraycpy(v, (PetscScalar *)Y_handle->x, n)); 181 PetscCall(VecRestoreArrayWrite(X, &v)); 182 PetscCallExternal(!cholmod_l_free_dense, &Y_handle, chol->common); 183 PetscCall(VecUnWrapCholmod(B, GET_ARRAY_READ, &cholB)); 184 PetscFunctionReturn(PETSC_SUCCESS); 185 } 186 187 static PetscErrorCode MatMatSolveTranspose_SPQR(Mat F, Mat B, Mat X) 188 { 189 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 190 cholmod_dense cholB, *Y_handle = NULL; 191 PetscScalar *v; 192 PetscInt lda; 193 194 PetscFunctionBegin; 195 PetscCall(MatDenseWrapCholmod(B, GET_ARRAY_READ, &cholB)); 196 PetscCall(MatSolveTranspose_SPQR_Internal(F, &cholB, &Y_handle)); 197 PetscCall(MatDenseGetArrayWrite(X, &v)); 198 PetscCall(MatDenseGetLDA(X, &lda)); 199 if ((size_t)lda == Y_handle->d) { 200 PetscCall(PetscArraycpy(v, (PetscScalar *)Y_handle->x, lda * Y_handle->ncol)); 201 } else { 202 for (size_t j = 0; j < Y_handle->ncol; j++) PetscCall(PetscArraycpy(&v[j * lda], &(((PetscScalar *)Y_handle->x)[j * Y_handle->d]), Y_handle->nrow)); 203 } 204 PetscCall(MatDenseRestoreArrayWrite(X, &v)); 205 PetscCallExternal(!cholmod_l_free_dense, &Y_handle, chol->common); 206 PetscCall(MatDenseUnWrapCholmod(B, GET_ARRAY_READ, &cholB)); 207 PetscFunctionReturn(PETSC_SUCCESS); 208 } 209 210 static PetscErrorCode MatQRFactorNumeric_SPQR(Mat F, Mat A, const MatFactorInfo *info) 211 { 212 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 213 cholmod_sparse cholA; 214 PetscBool aijalloc, valloc; 215 int err; 216 217 PetscFunctionBegin; 218 PetscCall(PetscObjectTypeCompare((PetscObject)A, MATNORMALHERMITIAN, &chol->normal)); 219 if (!chol->normal && !PetscDefined(USE_COMPLEX)) PetscCall(PetscObjectTypeCompare((PetscObject)A, MATNORMAL, &chol->normal)); 220 PetscCall((*chol->Wrap)(A, PETSC_TRUE, &cholA, &aijalloc, &valloc)); 221 err = !SuiteSparseQR_C_numeric(PETSC_SMALL, &cholA, chol->spqrfact, chol->common); 222 PetscCheck(!err, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "SPQR factorization failed with status %d", chol->common->status); 223 224 if (aijalloc) PetscCall(PetscFree2(cholA.p, cholA.i)); 225 if (valloc) PetscCall(PetscFree(cholA.x)); 226 227 F->ops->solve = MatSolve_SPQR; 228 F->ops->matsolve = MatMatSolve_SPQR; 229 if (chol->normal) { 230 F->ops->solvetranspose = MatSolve_SPQR; 231 F->ops->matsolvetranspose = MatMatSolve_SPQR; 232 } else if (A->cmap->n == A->rmap->n) { 233 F->ops->solvetranspose = MatSolveTranspose_SPQR; 234 F->ops->matsolvetranspose = MatMatSolveTranspose_SPQR; 235 } 236 PetscFunctionReturn(PETSC_SUCCESS); 237 } 238 239 PETSC_INTERN PetscErrorCode MatQRFactorSymbolic_SPQR(Mat F, Mat A, IS perm, const MatFactorInfo *info) 240 { 241 Mat_CHOLMOD *chol = (Mat_CHOLMOD *)F->data; 242 cholmod_sparse cholA; 243 PetscBool aijalloc, valloc; 244 245 PetscFunctionBegin; 246 PetscCall(PetscObjectTypeCompare((PetscObject)A, MATNORMALHERMITIAN, &chol->normal)); 247 if (!chol->normal && !PetscDefined(USE_COMPLEX)) PetscCall(PetscObjectTypeCompare((PetscObject)A, MATNORMAL, &chol->normal)); 248 PetscCall((*chol->Wrap)(A, PETSC_TRUE, &cholA, &aijalloc, &valloc)); 249 if (PetscDefined(USE_DEBUG)) PetscCallExternal(!cholmod_l_check_sparse, &cholA, chol->common); 250 if (chol->spqrfact) PetscCallExternal(!SuiteSparseQR_C_free, &chol->spqrfact, chol->common); 251 chol->spqrfact = SuiteSparseQR_C_symbolic(SPQR_ORDERING_DEFAULT, 1, &cholA, chol->common); 252 PetscCheck(chol->spqrfact, PetscObjectComm((PetscObject)F), PETSC_ERR_LIB, "CHOLMOD analysis failed using internal ordering with status %d", chol->common->status); 253 254 if (aijalloc) PetscCall(PetscFree2(cholA.p, cholA.i)); 255 if (valloc) PetscCall(PetscFree(cholA.x)); 256 257 PetscCall(PetscObjectComposeFunction((PetscObject)F, "MatQRFactorNumeric_C", MatQRFactorNumeric_SPQR)); 258 PetscFunctionReturn(PETSC_SUCCESS); 259 } 260 261 /*MC 262 MATSOLVERSPQR 263 264 A matrix type providing direct solvers, QR factorizations, for sequential matrices 265 via the external package SPQR. 266 267 Use `./configure --download-suitesparse` to install PETSc to use SPQR 268 269 Consult SPQR documentation for more information about the common parameters 270 which correspond to the options database keys below. 271 272 Level: beginner 273 274 Note: 275 SPQR is part of SuiteSparse http://faculty.cse.tamu.edu/davis/suitesparse.html 276 277 .seealso: [](ch_matrices), `Mat`, `PCQR`, `PCFactorSetMatSolverType()`, `MatSolverType` 278 M*/ 279 280 PETSC_INTERN PetscErrorCode MatGetFactor_seqaij_spqr(Mat A, MatFactorType ftype, Mat *F) 281 { 282 Mat B; 283 Mat_CHOLMOD *chol; 284 PetscInt m = A->rmap->n, n = A->cmap->n; 285 const char *prefix; 286 287 PetscFunctionBegin; 288 /* Create the factorization matrix F */ 289 PetscCall(MatCreate(PetscObjectComm((PetscObject)A), &B)); 290 PetscCall(MatSetSizes(B, PETSC_DECIDE, PETSC_DECIDE, m, n)); 291 PetscCall(PetscStrallocpy("spqr", &((PetscObject)B)->type_name)); 292 PetscCall(MatGetOptionsPrefix(A, &prefix)); 293 PetscCall(MatSetOptionsPrefix(B, prefix)); 294 PetscCall(MatSetUp(B)); 295 PetscCall(PetscNew(&chol)); 296 297 chol->Wrap = MatWrapCholmod_SPQR_seqaij; 298 B->data = chol; 299 300 B->ops->getinfo = MatGetInfo_CHOLMOD; 301 B->ops->view = MatView_CHOLMOD; 302 B->ops->destroy = MatDestroy_CHOLMOD; 303 304 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatFactorGetSolverType_C", MatFactorGetSolverType_seqaij_SPQR)); 305 PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatQRFactorSymbolic_C", MatQRFactorSymbolic_SPQR)); 306 307 B->factortype = MAT_FACTOR_QR; 308 B->assembled = PETSC_TRUE; 309 B->preallocated = PETSC_TRUE; 310 311 PetscCall(PetscFree(B->solvertype)); 312 PetscCall(PetscStrallocpy(MATSOLVERCHOLMOD, &B->solvertype)); 313 B->canuseordering = PETSC_FALSE; 314 PetscCall(CholmodStart(B)); 315 chol->common->itype = CHOLMOD_LONG; 316 *F = B; 317 PetscFunctionReturn(PETSC_SUCCESS); 318 } 319