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