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