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