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