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