xref: /petsc/src/mat/impls/aij/seq/spqr/aijspqr.c (revision f13dfd9ea68e0ddeee984e65c377a1819eab8a8a)
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