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 */
MatWrapCholmod_SPQR_seqaij(Mat A,PetscBool values,cholmod_sparse * C,PetscBool * aijalloc,PetscBool * valloc)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
MatFactorGetSolverType_seqaij_SPQR(Mat A,MatSolverType * type)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
MatSolve_SPQR_Internal(Mat F,cholmod_dense * cholB,cholmod_dense ** _Y_handle)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
MatSolve_SPQR(Mat F,Vec B,Vec X)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
MatMatSolve_SPQR(Mat F,Mat B,Mat X)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
MatSolveTranspose_SPQR_Internal(Mat F,cholmod_dense * cholB,cholmod_dense ** _Y_handle)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
MatSolveTranspose_SPQR(Mat F,Vec B,Vec X)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
MatMatSolveTranspose_SPQR(Mat F,Mat B,Mat X)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
MatQRFactorNumeric_SPQR(Mat F,Mat A,const MatFactorInfo * info)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
MatQRFactorSymbolic_SPQR(Mat F,Mat A,IS perm,const MatFactorInfo * info)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
MatGetFactor_seqaij_spqr(Mat A,MatFactorType ftype,Mat * F)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