xref: /petsc/src/ksp/pc/impls/factor/cholesky/cholesky.c (revision b2ccae6bdc8edea944f1c160ca3b2eb32c69ecb2)
1 /*
2    Defines a direct factorization preconditioner for any Mat implementation
3    Note: this need not be considered a preconditioner since it supplies
4          a direct solver.
5 */
6 #include <../src/ksp/pc/impls/factor/factor.h> /*I "petscpc.h" I*/
7 
8 typedef struct {
9   PC_Factor hdr;
10   IS        row, col; /* index sets used for reordering */
11 } PC_Cholesky;
12 
13 static PetscErrorCode PCSetFromOptions_Cholesky(PC pc, PetscOptionItems PetscOptionsObject)
14 {
15   PetscFunctionBegin;
16   PetscOptionsHeadBegin(PetscOptionsObject, "Cholesky options");
17   PetscCall(PCSetFromOptions_Factor(pc, PetscOptionsObject));
18   PetscOptionsHeadEnd();
19   PetscFunctionReturn(PETSC_SUCCESS);
20 }
21 
22 static PetscErrorCode PCSetUp_Cholesky(PC pc)
23 {
24   PetscBool      flg;
25   PC_Cholesky   *dir = (PC_Cholesky *)pc->data;
26   MatSolverType  stype;
27   MatFactorError err;
28   const char    *prefix;
29 
30   PetscFunctionBegin;
31   pc->failedreason = PC_NOERROR;
32   if (dir->hdr.reusefill && pc->setupcalled) ((PC_Factor *)dir)->info.fill = dir->hdr.actualfill;
33 
34   PetscCall(PCGetOptionsPrefix(pc, &prefix));
35   PetscCall(MatSetOptionsPrefixFactor(pc->pmat, prefix));
36 
37   PetscCall(MatSetErrorIfFailure(pc->pmat, pc->erroriffailure));
38   if (dir->hdr.inplace) {
39     MatFactorType ftype;
40 
41     PetscCall(MatGetFactorType(pc->pmat, &ftype));
42     if (ftype == MAT_FACTOR_NONE) {
43       if (dir->row && dir->col && (dir->row != dir->col)) PetscCall(ISDestroy(&dir->row));
44       PetscCall(ISDestroy(&dir->col));
45       /* This should only get the ordering if needed, but since MatGetFactor() is not called we can't know if it is needed */
46       PetscCall(PCFactorSetDefaultOrdering_Factor(pc));
47       PetscCall(MatGetOrdering(pc->pmat, ((PC_Factor *)dir)->ordering, &dir->row, &dir->col));
48       if (dir->col && (dir->row != dir->col)) { /* only use row ordering for SBAIJ */
49         PetscCall(ISDestroy(&dir->col));
50       }
51       PetscCall(MatCholeskyFactor(pc->pmat, dir->row, &((PC_Factor *)dir)->info));
52       PetscCall(MatFactorGetError(pc->pmat, &err));
53       if (err) { /* Factor() fails */
54         pc->failedreason = (PCFailedReason)err;
55         PetscFunctionReturn(PETSC_SUCCESS);
56       }
57     }
58     ((PC_Factor *)dir)->fact = pc->pmat;
59   } else {
60     MatInfo info;
61 
62     if (!pc->setupcalled) {
63       PetscBool canuseordering;
64 
65       PetscCall(PCFactorSetUpMatSolverType(pc));
66       PetscCall(MatFactorGetCanUseOrdering(((PC_Factor *)dir)->fact, &canuseordering));
67       if (canuseordering) {
68         PetscCall(PCFactorSetDefaultOrdering_Factor(pc));
69         PetscCall(MatGetOrdering(pc->pmat, ((PC_Factor *)dir)->ordering, &dir->row, &dir->col));
70         /* check if dir->row == dir->col */
71         if (dir->row) {
72           PetscCall(ISEqual(dir->row, dir->col, &flg));
73           PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "row and column permutations must be equal");
74         }
75         PetscCall(ISDestroy(&dir->col)); /* only pass one ordering into CholeskyFactor */
76 
77         flg = PETSC_FALSE;
78         PetscCall(PetscOptionsGetBool(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_factor_nonzeros_along_diagonal", &flg, NULL));
79         if (flg) {
80           PetscReal tol = 1.e-10;
81           PetscCall(PetscOptionsGetReal(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_factor_nonzeros_along_diagonal", &tol, NULL));
82           PetscCall(MatReorderForNonzeroDiagonal(pc->pmat, tol, dir->row, dir->row));
83         }
84       }
85       PetscCall(MatCholeskyFactorSymbolic(((PC_Factor *)dir)->fact, pc->pmat, dir->row, &((PC_Factor *)dir)->info));
86       PetscCall(MatGetInfo(((PC_Factor *)dir)->fact, MAT_LOCAL, &info));
87       dir->hdr.actualfill = info.fill_ratio_needed;
88     } else if (pc->flag != SAME_NONZERO_PATTERN) {
89       if (!dir->hdr.reuseordering) {
90         PetscBool canuseordering;
91 
92         PetscCall(MatDestroy(&((PC_Factor *)dir)->fact));
93         PetscCall(PCFactorSetUpMatSolverType(pc));
94         PetscCall(MatFactorGetCanUseOrdering(((PC_Factor *)dir)->fact, &canuseordering));
95         if (canuseordering) {
96           PetscCall(ISDestroy(&dir->row));
97           PetscCall(PCFactorSetDefaultOrdering_Factor(pc));
98           PetscCall(MatGetOrdering(pc->pmat, ((PC_Factor *)dir)->ordering, &dir->row, &dir->col));
99           PetscCall(ISDestroy(&dir->col)); /* only use dir->row ordering in CholeskyFactor */
100 
101           flg = PETSC_FALSE;
102           PetscCall(PetscOptionsGetBool(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_factor_nonzeros_along_diagonal", &flg, NULL));
103           if (flg) {
104             PetscReal tol = 1.e-10;
105             PetscCall(PetscOptionsGetReal(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_factor_nonzeros_along_diagonal", &tol, NULL));
106             PetscCall(MatReorderForNonzeroDiagonal(pc->pmat, tol, dir->row, dir->row));
107           }
108         }
109       }
110       PetscCall(MatCholeskyFactorSymbolic(((PC_Factor *)dir)->fact, pc->pmat, dir->row, &((PC_Factor *)dir)->info));
111       PetscCall(MatGetInfo(((PC_Factor *)dir)->fact, MAT_LOCAL, &info));
112       dir->hdr.actualfill = info.fill_ratio_needed;
113     } else {
114       PetscCall(MatFactorGetError(((PC_Factor *)dir)->fact, &err));
115       if (err == MAT_FACTOR_NUMERIC_ZEROPIVOT) {
116         PetscCall(MatFactorClearError(((PC_Factor *)dir)->fact));
117         pc->failedreason = PC_NOERROR;
118       }
119     }
120     PetscCall(MatFactorGetError(((PC_Factor *)dir)->fact, &err));
121     if (err) { /* FactorSymbolic() fails */
122       pc->failedreason = (PCFailedReason)err;
123       PetscFunctionReturn(PETSC_SUCCESS);
124     }
125 
126     PetscCall(MatCholeskyFactorNumeric(((PC_Factor *)dir)->fact, pc->pmat, &((PC_Factor *)dir)->info));
127     PetscCall(MatFactorGetError(((PC_Factor *)dir)->fact, &err));
128     if (err) { /* FactorNumeric() fails */
129       pc->failedreason = (PCFailedReason)err;
130     }
131   }
132 
133   PetscCall(PCFactorGetMatSolverType(pc, &stype));
134   if (!stype) {
135     MatSolverType solverpackage;
136     PetscCall(MatFactorGetSolverType(((PC_Factor *)dir)->fact, &solverpackage));
137     PetscCall(PCFactorSetMatSolverType(pc, solverpackage));
138   }
139   PetscFunctionReturn(PETSC_SUCCESS);
140 }
141 
142 static PetscErrorCode PCReset_Cholesky(PC pc)
143 {
144   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
145 
146   PetscFunctionBegin;
147   if (!dir->hdr.inplace && ((PC_Factor *)dir)->fact) PetscCall(MatDestroy(&((PC_Factor *)dir)->fact));
148   PetscCall(ISDestroy(&dir->row));
149   PetscCall(ISDestroy(&dir->col));
150   PetscFunctionReturn(PETSC_SUCCESS);
151 }
152 
153 static PetscErrorCode PCDestroy_Cholesky(PC pc)
154 {
155   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
156 
157   PetscFunctionBegin;
158   PetscCall(PCReset_Cholesky(pc));
159   PetscCall(PetscFree(((PC_Factor *)dir)->ordering));
160   PetscCall(PetscFree(((PC_Factor *)dir)->solvertype));
161   PetscCall(PCFactorClearComposedFunctions(pc));
162   PetscCall(PetscFree(pc->data));
163   PetscFunctionReturn(PETSC_SUCCESS);
164 }
165 
166 static PetscErrorCode PCApply_Cholesky(PC pc, Vec x, Vec y)
167 {
168   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
169 
170   PetscFunctionBegin;
171   if (dir->hdr.inplace) {
172     PetscCall(MatSolve(pc->pmat, x, y));
173   } else {
174     PetscCall(MatSolve(((PC_Factor *)dir)->fact, x, y));
175   }
176   PetscFunctionReturn(PETSC_SUCCESS);
177 }
178 
179 static PetscErrorCode PCMatApply_Cholesky(PC pc, Mat X, Mat Y)
180 {
181   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
182 
183   PetscFunctionBegin;
184   if (dir->hdr.inplace) {
185     PetscCall(MatMatSolve(pc->pmat, X, Y));
186   } else {
187     PetscCall(MatMatSolve(((PC_Factor *)dir)->fact, X, Y));
188   }
189   PetscFunctionReturn(PETSC_SUCCESS);
190 }
191 
192 static PetscErrorCode PCApplySymmetricLeft_Cholesky(PC pc, Vec x, Vec y)
193 {
194   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
195 
196   PetscFunctionBegin;
197   if (dir->hdr.inplace) {
198     PetscCall(MatForwardSolve(pc->pmat, x, y));
199   } else {
200     PetscCall(MatForwardSolve(((PC_Factor *)dir)->fact, x, y));
201   }
202   PetscFunctionReturn(PETSC_SUCCESS);
203 }
204 
205 static PetscErrorCode PCApplySymmetricRight_Cholesky(PC pc, Vec x, Vec y)
206 {
207   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
208 
209   PetscFunctionBegin;
210   if (dir->hdr.inplace) {
211     PetscCall(MatBackwardSolve(pc->pmat, x, y));
212   } else {
213     PetscCall(MatBackwardSolve(((PC_Factor *)dir)->fact, x, y));
214   }
215   PetscFunctionReturn(PETSC_SUCCESS);
216 }
217 
218 static PetscErrorCode PCApplyTranspose_Cholesky(PC pc, Vec x, Vec y)
219 {
220   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
221 
222   PetscFunctionBegin;
223   if (dir->hdr.inplace) {
224     PetscCall(MatSolveTranspose(pc->pmat, x, y));
225   } else {
226     PetscCall(MatSolveTranspose(((PC_Factor *)dir)->fact, x, y));
227   }
228   PetscFunctionReturn(PETSC_SUCCESS);
229 }
230 
231 static PetscErrorCode PCMatApplyTranspose_Cholesky(PC pc, Mat X, Mat Y)
232 {
233   PC_Cholesky *dir = (PC_Cholesky *)pc->data;
234 
235   PetscFunctionBegin;
236   if (dir->hdr.inplace) {
237     PetscCall(MatMatSolveTranspose(pc->pmat, X, Y));
238   } else {
239     PetscCall(MatMatSolveTranspose(((PC_Factor *)dir)->fact, X, Y));
240   }
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 /*@
245   PCFactorSetReuseOrdering - When similar matrices are factored, this
246   causes the ordering computed in the first factor to be used for all
247   following factors.
248 
249   Logically Collective
250 
251   Input Parameters:
252 + pc   - the preconditioner context
253 - flag - `PETSC_TRUE` to reuse else `PETSC_FALSE`
254 
255   Options Database Key:
256 . -pc_factor_reuse_ordering - Activate `PCFactorSetReuseOrdering()`
257 
258   Level: intermediate
259 
260 .seealso: [](ch_ksp), `PCLU`, `PCCHOLESKY`, `PCFactorSetReuseFill()`
261 @*/
262 PetscErrorCode PCFactorSetReuseOrdering(PC pc, PetscBool flag)
263 {
264   PetscFunctionBegin;
265   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
266   PetscValidLogicalCollectiveBool(pc, flag, 2);
267   PetscTryMethod(pc, "PCFactorSetReuseOrdering_C", (PC, PetscBool), (pc, flag));
268   PetscFunctionReturn(PETSC_SUCCESS);
269 }
270 
271 /*MC
272    PCCHOLESKY - Uses a direct solver, based on Cholesky factorization, as a preconditioner
273 
274    Options Database Keys:
275 +  -pc_factor_reuse_ordering                 - Activate `PCFactorSetReuseOrdering()`
276 .  -pc_factor_mat_solver_type                - Actives `PCFactorSetMatSolverType()` to choose the direct solver, like superlu
277 .  -pc_factor_reuse_fill                     - Activates `PCFactorSetReuseFill()`
278 .  -pc_factor_fill <fill>                    - Sets the explected fill amount
279 .  -pc_factor_in_place                       - Activates in-place factorization
280 -  -pc_factor_mat_ordering_type <nd,rcm,...> - Sets ordering routine used to determine the order the rows are used in the factorization to reduce fill
281                                                and thus be more effective
282 
283    Level: beginner
284 
285    Notes:
286    The Cholesky factorization direct solver, `PCCHOLESKY` is only for symmetric positive-definite (SPD) matrices. For such
287    SPD matrices it is more efficient than using the LU factorization direct solver, `PCLU`.
288 
289    Not all options work for all matrix formats
290 
291    Usually this will compute an "exact" solution in one iteration and does
292    not need a Krylov method (i.e. you can use -ksp_type preonly, or
293    `KSPSetType`(ksp,`KSPPREONLY`) for the Krylov method
294 
295 .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PC`,
296           `PCILU`, `PCLU`, `PCICC`, `PCFactorSetReuseOrdering()`, `PCFactorSetReuseFill()`, `PCFactorGetMatrix()`,
297           `PCFactorSetFill()`, `PCFactorSetShiftType()`, `PCFactorSetShiftAmount()`
298           `PCFactorSetUseInPlace()`, `PCFactorGetUseInPlace()`, `PCFactorSetMatOrderingType()`, `PCFactorSetReuseOrdering()`
299 M*/
300 
301 PETSC_EXTERN PetscErrorCode PCCreate_Cholesky(PC pc)
302 {
303   PC_Cholesky *dir;
304 
305   PetscFunctionBegin;
306   PetscCall(PetscNew(&dir));
307   pc->data = (void *)dir;
308   PetscCall(PCFactorInitialize(pc, MAT_FACTOR_CHOLESKY));
309 
310   ((PC_Factor *)dir)->info.fill = 5.0;
311 
312   pc->ops->destroy             = PCDestroy_Cholesky;
313   pc->ops->reset               = PCReset_Cholesky;
314   pc->ops->apply               = PCApply_Cholesky;
315   pc->ops->matapply            = PCMatApply_Cholesky;
316   pc->ops->applysymmetricleft  = PCApplySymmetricLeft_Cholesky;
317   pc->ops->applysymmetricright = PCApplySymmetricRight_Cholesky;
318   pc->ops->applytranspose      = PCApplyTranspose_Cholesky;
319   pc->ops->matapplytranspose   = PCMatApplyTranspose_Cholesky;
320   pc->ops->setup               = PCSetUp_Cholesky;
321   pc->ops->setfromoptions      = PCSetFromOptions_Cholesky;
322   pc->ops->view                = PCView_Factor;
323   pc->ops->applyrichardson     = NULL;
324   PetscFunctionReturn(PETSC_SUCCESS);
325 }
326