xref: /petsc/src/ksp/ksp/utils/lmvm/lmvmutils.c (revision 8577b683712d1cca1e9b8fdaa9ae028364224dad)
1 #include <petscdevice.h>
2 #include <../src/ksp/ksp/utils/lmvm/lmvm.h> /*I "petscksp.h" I*/
3 #include <petsc/private/deviceimpl.h>
4 #include <petscblaslapack.h>
5 
6 /*@
7   MatLMVMUpdate - Adds (X-Xprev) and (F-Fprev) updates to a `MATLMVM` matrix.
8 
9   Input Parameters:
10 + B - A `MATLMVM` matrix
11 . X - Solution vector
12 - F - Function vector
13 
14   Level: intermediate
15 
16   Notes:
17 
18   The first time this function is called for a `MATLMVM` matrix, no update is applied, but the given X and F vectors
19   are stored for use as Xprev and Fprev in the next update.
20 
21   If the user has provided another `MATLMVM` matrix for the reference Jacobian (using `MatLMVMSetJ0()`, for example),
22   that matrix is also updated recursively.
23 
24   If the sizes of `B` have not been specified (using `MatSetSizes()` or `MatSetLayouts()`) before `MatLMVMUpdate()` is
25   called, the row size and layout of `B` will be set to match `F` and the column size and layout of `B` will be set to
26   match `X`, and these sizes will be final.
27 
28 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMReset()`, `MatLMVMAllocate()`
29 @*/
MatLMVMUpdate(Mat B,Vec X,Vec F)30 PetscErrorCode MatLMVMUpdate(Mat B, Vec X, Vec F)
31 {
32   Mat_LMVM *lmvm;
33   PetscBool same;
34 
35   PetscFunctionBegin;
36   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
37   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
38   PetscValidHeaderSpecific(F, VEC_CLASSID, 3);
39   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
40   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
41   /* If B has specified layouts, this will check X and F are compatible;
42      if B does not have specified layouts, this will adopt them, so that
43      this pattern is okay
44 
45        MatCreate(comm, &B);
46        MatLMVMSetType(B, MATLMVMBFGS);
47        MatLMVMUpdate(B, X, F);
48    */
49   PetscCall(MatLMVMUseVecLayoutsIfCompatible(B, X, F));
50   MatCheckPreallocated(B, 1);
51   PetscCall(PetscLogEventBegin(MATLMVM_Update, NULL, NULL, NULL, NULL));
52   lmvm = (Mat_LMVM *)B->data;
53   PetscCall(MatLMVMUpdate(lmvm->J0, X, F));
54   PetscCall((*lmvm->ops->update)(B, X, F));
55   PetscCall(PetscLogEventEnd(MATLMVM_Update, NULL, NULL, NULL, NULL));
56   PetscFunctionReturn(PETSC_SUCCESS);
57 }
58 
MatLMVMCreateJ0(Mat B,Mat * J0)59 static PetscErrorCode MatLMVMCreateJ0(Mat B, Mat *J0)
60 {
61   PetscLayout rmap, cmap;
62   VecType     vec_type;
63   const char *prefix;
64 
65   PetscFunctionBegin;
66   PetscCall(MatCreate(PetscObjectComm((PetscObject)B), J0));
67   PetscCall(MatGetLayouts(B, &rmap, &cmap));
68   PetscCall(MatSetLayouts(*J0, rmap, cmap));
69   PetscCall(MatGetVecType(B, &vec_type));
70   PetscCall(MatSetVecType(*J0, vec_type));
71   PetscCall(MatGetOptionsPrefix(B, &prefix));
72   PetscCall(MatSetOptionsPrefix(*J0, prefix));
73   PetscCall(MatAppendOptionsPrefix(*J0, "mat_lmvm_J0_"));
74   PetscFunctionReturn(PETSC_SUCCESS);
75 }
76 
MatLMVMCreateJ0KSP(Mat B,KSP * ksp)77 static PetscErrorCode MatLMVMCreateJ0KSP(Mat B, KSP *ksp)
78 {
79   Mat_LMVM   *lmvm = (Mat_LMVM *)B->data;
80   const char *prefix;
81 
82   PetscFunctionBegin;
83   PetscCall(KSPCreate(PetscObjectComm((PetscObject)B), ksp));
84   PetscCall(KSPSetOperators(*ksp, lmvm->J0, lmvm->J0));
85   PetscCall(PetscObjectIncrementTabLevel((PetscObject)B, (PetscObject)*ksp, 1));
86   PetscCall(MatGetOptionsPrefix(B, &prefix));
87   PetscCall(KSPSetOptionsPrefix(*ksp, prefix));
88   PetscCall(KSPAppendOptionsPrefix(*ksp, "mat_lmvm_J0_"));
89   PetscFunctionReturn(PETSC_SUCCESS);
90 }
91 
MatLMVMCreateJ0KSP_ExactInverse(Mat B,KSP * ksp)92 static PetscErrorCode MatLMVMCreateJ0KSP_ExactInverse(Mat B, KSP *ksp)
93 {
94   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
95   PC        pc;
96 
97   PetscFunctionBegin;
98   PetscCall(MatLMVMCreateJ0KSP(B, ksp));
99   PetscCall(KSPSetType(*ksp, KSPPREONLY));
100   PetscCall(KSPGetPC(*ksp, &pc));
101   PetscCall(PCSetType(pc, PCMAT));
102   PetscCall(PCMatSetApplyOperation(pc, MATOP_SOLVE));
103   lmvm->disable_ksp_viewers = PETSC_TRUE;
104   PetscFunctionReturn(PETSC_SUCCESS);
105 }
106 
107 /*@
108   MatLMVMClearJ0 - Removes all definitions of J0 and reverts to
109   an identity matrix (scale = 1.0).
110 
111   Input Parameter:
112 . B - A `MATLMVM` matrix
113 
114   Level: advanced
115 
116 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetJ0()`
117 @*/
MatLMVMClearJ0(Mat B)118 PetscErrorCode MatLMVMClearJ0(Mat B)
119 {
120   Mat_LMVM *lmvm;
121   PetscBool same;
122 
123   PetscFunctionBegin;
124   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
125   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
126   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
127   lmvm = (Mat_LMVM *)B->data;
128   PetscCall(MatDestroy(&lmvm->J0));
129   PetscCall(KSPDestroy(&lmvm->J0ksp));
130   PetscCall(MatLMVMCreateJ0(B, &lmvm->J0));
131   PetscCall(MatSetType(lmvm->J0, MATCONSTANTDIAGONAL));
132   PetscCall(MatZeroEntries(lmvm->J0));
133   PetscCall(MatShift(lmvm->J0, 1.0));
134   PetscCall(MatLMVMCreateJ0KSP_ExactInverse(B, &lmvm->J0ksp));
135   lmvm->created_J0    = PETSC_TRUE;
136   lmvm->created_J0ksp = PETSC_TRUE;
137   PetscFunctionReturn(PETSC_SUCCESS);
138 }
139 
140 /*@
141   MatLMVMSetJ0Scale - Allows the user to define a scalar value
142   mu such that J0 = mu*I.
143 
144   Input Parameters:
145 + B     - A `MATLMVM` matrix
146 - scale - Scalar value mu that defines the initial Jacobian
147 
148   Level: advanced
149 
150 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetDiagScale()`, `MatLMVMSetJ0()`
151 @*/
MatLMVMSetJ0Scale(Mat B,PetscReal scale)152 PetscErrorCode MatLMVMSetJ0Scale(Mat B, PetscReal scale)
153 {
154   Mat_LMVM *lmvm;
155   PetscBool same;
156   PetscBool isconstant;
157 
158   PetscFunctionBegin;
159   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
160   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
161   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
162   lmvm = (Mat_LMVM *)B->data;
163   PetscCall(PetscObjectTypeCompare((PetscObject)lmvm->J0, MATCONSTANTDIAGONAL, &isconstant));
164   if (!isconstant) PetscCall(MatLMVMClearJ0(B));
165   PetscCall(MatZeroEntries(lmvm->J0));
166   PetscCall(MatShift(lmvm->J0, scale));
167   PetscFunctionReturn(PETSC_SUCCESS);
168 }
169 
170 #if PetscDefined(USE_DEBUG)
171   #define PetscValidLogicalCollectiveLayout(layout, v) \
172     do { \
173       if (!(layout)->setupcalled) { \
174         PetscMPIInt global[2]; \
175         global[0] = (PetscMPIInt)(v); \
176         global[1] = -global[0]; \
177         PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &global[0], 2, MPI_INT, MPI_MIN, ((layout)->comm))); \
178         PetscCheck(global[1] == -global[0], ((layout)->comm), PETSC_ERR_ARG_WRONGSTATE, "PetscLayout has size == PETSC_DECIDE and local size == PETSC_DETERMINE on only some processes"); \
179       } \
180     } while (0)
181 #else
182   #define PetscValidLogicalCollectiveLayout(comm, v) \
183     do { \
184       (void)(comm); \
185       (void)(v); \
186     } while (0)
187 #endif
188 
MatLMVMCheckArgumentLayout(PetscLayout b,PetscLayout a)189 static PetscErrorCode MatLMVMCheckArgumentLayout(PetscLayout b, PetscLayout a)
190 {
191   PetscBool   b_is_unspecified, a_is_specified, are_compatible;
192   PetscLayout b_setup = NULL, a_setup = NULL;
193 
194   PetscFunctionBegin;
195   if (b == a) PetscFunctionReturn(PETSC_SUCCESS); // a layout is compatible with itself
196   if (b->setupcalled && a->setupcalled) {
197     // run the standard checks that are guaranteed to error on at least one process if the layouts are incompatible
198     PetscCheck(b->N == a->N, b->comm, PETSC_ERR_ARG_SIZ, "argument layout (size %" PetscInt_FMT ") is incompatible with MatLMVM layout (size %" PetscInt_FMT ")", a->N, b->N);
199     PetscCheck(b->n == a->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "argument layout (local size %" PetscInt_FMT ") is incompatible with MatLMVM layout (local size %" PetscInt_FMT ")", a->n, b->n);
200     PetscFunctionReturn(PETSC_SUCCESS);
201   }
202   a_is_specified = (a->n >= 0) || (a->N >= 0) ? PETSC_TRUE : PETSC_FALSE;
203   PetscValidLogicalCollectiveLayout(a, a_is_specified);
204   PetscCheck(a_is_specified, a->comm, PETSC_ERR_ARG_WRONGSTATE, "argument layout has n == PETSC_DETERMINE and N == PETSC_DECIDE, size must be specified first");
205   b_is_unspecified = (b->n < 0) && (b->N < 0) ? PETSC_TRUE : PETSC_FALSE;
206   PetscValidLogicalCollectiveLayout(b, b_is_unspecified);
207   if (b_is_unspecified) PetscFunctionReturn(PETSC_SUCCESS); // any layout can replace an unspecified layout
208   // we don't want to change the setup states in this check, so make duplicates if they have not been setup
209   if (!b->setupcalled) {
210     PetscCall(PetscLayoutDuplicate(b, &b_setup));
211     PetscCall(PetscLayoutSetUp(b_setup));
212   } else PetscCall(PetscLayoutReference(b, &b_setup));
213   if (!a->setupcalled) {
214     PetscCall(PetscLayoutDuplicate(a, &a_setup));
215     PetscCall(PetscLayoutSetUp(a_setup));
216   } else PetscCall(PetscLayoutReference(a, &a_setup));
217   PetscCall(PetscLayoutCompare(b_setup, a_setup, &are_compatible));
218   PetscCall(PetscLayoutDestroy(&a_setup));
219   PetscCall(PetscLayoutDestroy(&b_setup));
220   PetscCheck(are_compatible, b->comm, PETSC_ERR_ARG_SIZ, "argument layout (size %" PetscInt_FMT ") is incompatible with MatLMVM layout (size %" PetscInt_FMT ")", a->N, b->N);
221   PetscFunctionReturn(PETSC_SUCCESS);
222 }
223 
MatLMVMUseJ0LayoutsIfCompatible(Mat B,Mat J0)224 static PetscErrorCode MatLMVMUseJ0LayoutsIfCompatible(Mat B, Mat J0)
225 {
226   PetscFunctionBegin;
227   PetscCall(MatLMVMCheckArgumentLayout(B->rmap, J0->rmap));
228   PetscCall(MatLMVMCheckArgumentLayout(B->cmap, J0->cmap));
229   PetscCall(PetscLayoutSetUp(J0->rmap));
230   PetscCall(PetscLayoutSetUp(J0->cmap));
231   PetscCall(PetscLayoutReference(J0->rmap, &B->rmap));
232   PetscCall(PetscLayoutReference(J0->cmap, &B->cmap));
233   PetscFunctionReturn(PETSC_SUCCESS);
234 }
235 
MatLMVMUseJ0DiagLayoutsIfCompatible(Mat B,Vec J0_diag)236 static PetscErrorCode MatLMVMUseJ0DiagLayoutsIfCompatible(Mat B, Vec J0_diag)
237 {
238   PetscFunctionBegin;
239   PetscCall(MatLMVMCheckArgumentLayout(B->rmap, J0_diag->map));
240   PetscCall(MatLMVMCheckArgumentLayout(B->cmap, J0_diag->map));
241   PetscCall(PetscLayoutSetUp(J0_diag->map));
242   PetscCall(PetscLayoutReference(J0_diag->map, &B->rmap));
243   PetscCall(PetscLayoutReference(J0_diag->map, &B->cmap));
244   PetscFunctionReturn(PETSC_SUCCESS);
245 }
246 
MatLMVMUseVecLayoutsIfCompatible(Mat B,Vec X,Vec F)247 PETSC_INTERN PetscErrorCode MatLMVMUseVecLayoutsIfCompatible(Mat B, Vec X, Vec F)
248 {
249   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
250 
251   PetscFunctionBegin;
252   PetscCall(MatLMVMCheckArgumentLayout(B->rmap, F->map));
253   PetscCall(MatLMVMCheckArgumentLayout(B->cmap, X->map));
254   PetscCall(PetscLayoutSetUp(F->map));
255   PetscCall(PetscLayoutSetUp(X->map));
256   PetscCall(PetscLayoutReference(F->map, &B->rmap));
257   PetscCall(PetscLayoutReference(X->map, &B->cmap));
258   if (lmvm->created_J0) {
259     PetscCall(PetscLayoutReference(B->rmap, &lmvm->J0->rmap));
260     PetscCall(PetscLayoutReference(B->cmap, &lmvm->J0->cmap));
261   }
262   PetscFunctionReturn(PETSC_SUCCESS);
263 }
264 
265 /*@
266   MatLMVMSetJ0Diag - Allows the user to define a vector
267   V such that J0 = diag(V).
268 
269   Input Parameters:
270 + B - An LMVM-type matrix
271 - V - Vector that defines the diagonal of the initial Jacobian: values are copied, V is not referenced
272 
273   Level: advanced
274 
275   Note:
276   If the sizes of `B` have not been specified (using `MatSetSizes()` or `MatSetLayouts()`) before `MatLMVMSetJ0Diag()` is
277   called, the rows and columns of `B` will each have the size and layout of `V`, and these sizes will be final.
278 
279 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetScale()`, `MatLMVMSetJ0()`
280 @*/
MatLMVMSetJ0Diag(Mat B,Vec V)281 PetscErrorCode MatLMVMSetJ0Diag(Mat B, Vec V)
282 {
283   Mat       J0diag;
284   PetscBool same;
285   VecType   vec_type;
286 
287   PetscFunctionBegin;
288   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
289   PetscValidHeaderSpecific(V, VEC_CLASSID, 2);
290   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
291   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
292   PetscCheckSameComm(B, 1, V, 2);
293   PetscCall(MatLMVMUseJ0DiagLayoutsIfCompatible(B, V));
294   PetscCall(MatCreate(PetscObjectComm((PetscObject)B), &J0diag));
295   PetscCall(MatSetLayouts(J0diag, V->map, V->map));
296   PetscCall(VecGetType(V, &vec_type));
297   PetscCall(MatSetVecType(J0diag, vec_type));
298   PetscCall(MatSetType(J0diag, MATDIAGONAL));
299   PetscCall(MatDiagonalSet(J0diag, V, INSERT_VALUES));
300   PetscCall(MatLMVMSetJ0(B, J0diag));
301   PetscCall(MatDestroy(&J0diag));
302   PetscFunctionReturn(PETSC_SUCCESS);
303 }
304 
MatLMVMGetJ0InvDiag(Mat B,Vec * V)305 PETSC_INTERN PetscErrorCode MatLMVMGetJ0InvDiag(Mat B, Vec *V)
306 {
307   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
308   PetscBool isvdiag;
309 
310   PetscFunctionBegin;
311   PetscCall(PetscObjectTypeCompare((PetscObject)lmvm->J0, MATDIAGONAL, &isvdiag));
312   if (!isvdiag) {
313     PetscCall(MatLMVMClearJ0(B));
314     PetscCall(MatSetType(lmvm->J0, MATDIAGONAL));
315     PetscCall(MatZeroEntries(lmvm->J0));
316     PetscCall(MatShift(lmvm->J0, 1.0));
317   }
318   PetscCall(MatDiagonalGetInverseDiagonal(lmvm->J0, V));
319   PetscFunctionReturn(PETSC_SUCCESS);
320 }
321 
MatLMVMRestoreJ0InvDiag(Mat B,Vec * V)322 PETSC_INTERN PetscErrorCode MatLMVMRestoreJ0InvDiag(Mat B, Vec *V)
323 {
324   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
325 
326   PetscFunctionBegin;
327   PetscCall(MatDiagonalRestoreInverseDiagonal(lmvm->J0, V));
328   PetscFunctionReturn(PETSC_SUCCESS);
329 }
330 
MatLMVMJ0KSPIsExact(Mat B,PetscBool * is_exact)331 PETSC_INTERN PetscErrorCode MatLMVMJ0KSPIsExact(Mat B, PetscBool *is_exact)
332 {
333   PetscBool    is_preonly, is_pcmat, has_pmat;
334   Mat_LMVM    *lmvm = (Mat_LMVM *)B->data;
335   Mat          pc_pmat;
336   PC           pc;
337   MatOperation matop;
338 
339   PetscFunctionBegin;
340   *is_exact = PETSC_FALSE;
341   PetscCall(PetscObjectTypeCompare((PetscObject)lmvm->J0ksp, KSPPREONLY, &is_preonly));
342   if (!is_preonly) PetscFunctionReturn(PETSC_SUCCESS);
343   PetscCall(KSPGetPC(lmvm->J0ksp, &pc));
344   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCMAT, &is_pcmat));
345   if (!is_pcmat) PetscFunctionReturn(PETSC_SUCCESS);
346   PetscCall(PCGetOperatorsSet(pc, NULL, &has_pmat));
347   if (!has_pmat) PetscFunctionReturn(PETSC_SUCCESS);
348   PetscCall(PCGetOperators(pc, NULL, &pc_pmat));
349   if (pc_pmat != lmvm->J0) PetscFunctionReturn(PETSC_SUCCESS);
350   PetscCall(PCMatGetApplyOperation(pc, &matop));
351   *is_exact = (matop == MATOP_SOLVE) ? PETSC_TRUE : PETSC_FALSE;
352   PetscFunctionReturn(PETSC_SUCCESS);
353 }
354 
355 /*@
356   MatLMVMSetJ0 - Allows the user to define the initial Jacobian matrix from which the LMVM-type approximation is built
357   up.
358 
359   Input Parameters:
360 + B  - An LMVM-type matrix
361 - J0 - The initial Jacobian matrix, will be referenced by B.
362 
363   Level: advanced
364 
365   Notes:
366   A KSP is created for inverting J0 with prefix `-mat_lmvm_J0_` and J0 is set to both operators in `KSPSetOperators()`.
367   If you want to use a separate preconditioning matrix, use `MatLMVMSetJ0KSP()` directly.
368 
369   If the sizes of `B` have not been specified (using `MatSetSizes()` or `MatSetLayouts()`) before `MatLMVMSetJ0()` is
370   called, then `B` will adopt the sizes and layouts of `J0`, and these sizes will be final.
371 
372 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetJ0PC()`, `MatLMVMSetJ0KSP()`
373 @*/
MatLMVMSetJ0(Mat B,Mat J0)374 PetscErrorCode MatLMVMSetJ0(Mat B, Mat J0)
375 {
376   Mat_LMVM *lmvm;
377   PetscBool same;
378   PetscBool J0_has_solve;
379 
380   PetscFunctionBegin;
381   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
382   PetscValidHeaderSpecific(J0, MAT_CLASSID, 2);
383   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
384   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
385   lmvm = (Mat_LMVM *)B->data;
386   if (J0 == lmvm->J0) PetscFunctionReturn(PETSC_SUCCESS);
387   PetscCheckSameComm(B, 1, J0, 2);
388   PetscCall(MatLMVMUseJ0LayoutsIfCompatible(B, J0));
389   PetscCall(PetscObjectReference((PetscObject)J0));
390   PetscCall(MatDestroy(&lmvm->J0));
391   lmvm->J0         = J0;
392   lmvm->created_J0 = PETSC_FALSE;
393   PetscCall(MatHasOperation(J0, MATOP_SOLVE, &J0_has_solve));
394   if (J0_has_solve) {
395     PetscCall(KSPDestroy(&lmvm->J0ksp));
396     PetscCall(MatLMVMCreateJ0KSP_ExactInverse(B, &lmvm->J0ksp));
397     lmvm->created_J0ksp = PETSC_TRUE;
398   } else {
399     if (lmvm->created_J0ksp) {
400       PetscBool is_preonly, is_pcmat = PETSC_FALSE, is_pcmat_solve = PETSC_FALSE;
401       PC        pc;
402 
403       PetscCall(PetscObjectTypeCompare((PetscObject)lmvm->J0ksp, KSPPREONLY, &is_preonly));
404       PetscCall(KSPGetPC(lmvm->J0ksp, &pc));
405       if (pc) {
406         PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCMAT, &is_pcmat));
407         if (is_pcmat) {
408           MatOperation matop;
409 
410           PetscCall(PCMatGetApplyOperation(pc, &matop));
411           if (matop == MATOP_SOLVE) is_pcmat_solve = PETSC_TRUE;
412         }
413       }
414       if (is_preonly && is_pcmat_solve) {
415         /* The KSP is one created by LMVM for a mat that has a MatSolve() implementation.  Because this new J0 doesn't, change it to
416            a default KSP */
417         PetscCall(KSPDestroy(&lmvm->J0ksp));
418         PetscCall(MatLMVMCreateJ0KSP(B, &lmvm->J0ksp));
419       }
420     }
421     PetscCall(KSPSetOperators(lmvm->J0ksp, J0, J0));
422   }
423   PetscFunctionReturn(PETSC_SUCCESS);
424 }
425 
426 /*@
427   MatLMVMSetJ0PC - Allows the user to define a `PC` object that acts as the initial inverse-Jacobian matrix.
428 
429   Input Parameters:
430 + B    - A `MATLMVM` matrix
431 - J0pc - `PC` object where `PCApply()` defines an inverse application for J0
432 
433   Level: advanced
434 
435   Notes:
436   `J0pc` should already contain all the operators necessary for its application.  The `MATLMVM` matrix only calls
437   `PCApply()` without changing any other options.
438 
439   If the sizes of `B` have not been specified (using `MatSetSizes()` or `MatSetLayouts()`) before `MatLMVMSetJ0PC()` is
440   called, then `B` will adopt the sizes and layouts of the operators of `J0pc`, and these sizes will be final.
441 
442 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMGetJ0PC()`
443 @*/
MatLMVMSetJ0PC(Mat B,PC J0pc)444 PetscErrorCode MatLMVMSetJ0PC(Mat B, PC J0pc)
445 {
446   Mat_LMVM *lmvm;
447   PetscBool same, mat_set, pmat_set;
448   PC        current_pc;
449   Mat       J0 = NULL;
450 
451   PetscFunctionBegin;
452   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
453   PetscValidHeaderSpecific(J0pc, PC_CLASSID, 2);
454   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
455   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
456   lmvm = (Mat_LMVM *)B->data;
457   PetscCall(PCGetOperatorsSet(J0pc, &mat_set, &pmat_set));
458   PetscCheck(mat_set || pmat_set, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "PC has not operators, call PCSetOperators() before MatLMVMSetJ0PC()");
459   if (mat_set) PetscCall(PCGetOperators(J0pc, &J0, NULL));
460   else PetscCall(PCGetOperators(J0pc, NULL, &J0));
461   PetscCall(KSPGetPC(lmvm->J0ksp, &current_pc));
462   if (J0pc == current_pc && J0 == lmvm->J0) PetscFunctionReturn(PETSC_SUCCESS);
463   PetscCall(MatLMVMSetJ0(B, J0));
464   PetscCall(KSPSetPC(lmvm->J0ksp, J0pc));
465   PetscFunctionReturn(PETSC_SUCCESS);
466 }
467 
468 /*@
469   MatLMVMSetJ0KSP - Allows the user to provide a pre-configured KSP solver for the initial inverse-Jacobian
470   approximation.
471 
472   Input Parameters:
473 + B     - A `MATLMVM` matrix
474 - J0ksp - `KSP` solver for the initial inverse-Jacobian application
475 
476   Level: advanced
477 
478   Note:
479   The `KSP` solver should already contain all the operators necessary to perform the inversion. The `MATLMVM` matrix
480   only calls `KSPSolve()` without changing any other options.
481 
482   If the sizes of `B` have not been specified (using `MatSetSizes()` or `MatSetLayouts()`) before `MatLMVMSetJ0KSP()` is
483   called, then `B` will adopt the sizes and layouts of the operators of `J0ksp`, and these sizes will be final.
484 
485 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMGetJ0KSP()`
486 @*/
MatLMVMSetJ0KSP(Mat B,KSP J0ksp)487 PetscErrorCode MatLMVMSetJ0KSP(Mat B, KSP J0ksp)
488 {
489   Mat_LMVM *lmvm;
490   PetscBool same, mat_set, pmat_set;
491   Mat       J0;
492 
493   PetscFunctionBegin;
494   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
495   PetscValidHeaderSpecific(J0ksp, KSP_CLASSID, 2);
496   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
497   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
498   lmvm = (Mat_LMVM *)B->data;
499   PetscCall(KSPGetOperatorsSet(J0ksp, &mat_set, &pmat_set));
500   PetscCheck(mat_set || pmat_set, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "PC has not operators, call PCSetOperators() before MatLMVMSetJ0PC()");
501   if (mat_set) PetscCall(KSPGetOperators(J0ksp, &J0, NULL));
502   else PetscCall(KSPGetOperators(J0ksp, NULL, &J0));
503   if (J0ksp == lmvm->J0ksp && lmvm->J0 == J0) PetscFunctionReturn(PETSC_SUCCESS);
504   PetscCall(MatLMVMSetJ0(B, J0));
505   if (J0ksp != lmvm->J0ksp) {
506     lmvm->created_J0ksp       = PETSC_FALSE;
507     lmvm->disable_ksp_viewers = PETSC_FALSE; // if the user supplies a more complicated KSP, don't turn off viewers
508   }
509   PetscCall(PetscObjectReference((PetscObject)J0ksp));
510   PetscCall(KSPDestroy(&lmvm->J0ksp));
511   lmvm->J0ksp = J0ksp;
512   PetscFunctionReturn(PETSC_SUCCESS);
513 }
514 
515 /*@
516   MatLMVMGetJ0 - Returns a pointer to the internal `J0` matrix.
517 
518   Input Parameter:
519 . B - A `MATLMVM` matrix
520 
521   Output Parameter:
522 . J0 - `Mat` object for defining the initial Jacobian
523 
524   Level: advanced
525 
526   Note:
527 
528   If `J0` was created by `B` it will have the options prefix `-mat_lmvm_J0_`.
529 
530 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetJ0()`
531 @*/
MatLMVMGetJ0(Mat B,Mat * J0)532 PetscErrorCode MatLMVMGetJ0(Mat B, Mat *J0)
533 {
534   Mat_LMVM *lmvm;
535   PetscBool same;
536 
537   PetscFunctionBegin;
538   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
539   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
540   PetscCheck(same, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "Matrix must be an LMVM-type.");
541   lmvm = (Mat_LMVM *)B->data;
542   *J0  = lmvm->J0;
543   PetscFunctionReturn(PETSC_SUCCESS);
544 }
545 
546 /*@
547   MatLMVMGetJ0PC - Returns a pointer to the internal `PC` object
548   associated with the initial Jacobian.
549 
550   Input Parameter:
551 . B - A `MATLMVM` matrix
552 
553   Output Parameter:
554 . J0pc - `PC` object for defining the initial inverse-Jacobian
555 
556   Level: advanced
557 
558 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetJ0PC()`
559 @*/
MatLMVMGetJ0PC(Mat B,PC * J0pc)560 PetscErrorCode MatLMVMGetJ0PC(Mat B, PC *J0pc)
561 {
562   Mat_LMVM *lmvm;
563   PetscBool same;
564 
565   PetscFunctionBegin;
566   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
567   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
568   PetscCheck(same, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "Matrix must be an LMVM-type.");
569   lmvm = (Mat_LMVM *)B->data;
570   PetscCall(KSPGetPC(lmvm->J0ksp, J0pc));
571   PetscFunctionReturn(PETSC_SUCCESS);
572 }
573 
574 /*@
575   MatLMVMGetJ0KSP - Returns a pointer to the internal `KSP` solver
576   associated with the initial Jacobian.
577 
578   Input Parameter:
579 . B - A `MATLMVM` matrix
580 
581   Output Parameter:
582 . J0ksp - `KSP` solver for defining the initial inverse-Jacobian
583 
584   Level: advanced
585 
586 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetJ0KSP()`
587 @*/
MatLMVMGetJ0KSP(Mat B,KSP * J0ksp)588 PetscErrorCode MatLMVMGetJ0KSP(Mat B, KSP *J0ksp)
589 {
590   Mat_LMVM *lmvm;
591   PetscBool same;
592 
593   PetscFunctionBegin;
594   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
595   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
596   PetscCheck(same, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "Matrix must be an LMVM-type.");
597   lmvm   = (Mat_LMVM *)B->data;
598   *J0ksp = lmvm->J0ksp;
599   PetscFunctionReturn(PETSC_SUCCESS);
600 }
601 
602 /*@
603   MatLMVMApplyJ0Fwd - Applies an approximation of the forward
604   matrix-vector product with the initial Jacobian.
605 
606   Input Parameters:
607 + B - A `MATLMVM` matrix
608 - X - vector to multiply with J0
609 
610   Output Parameter:
611 . Y - resulting vector for the operation
612 
613   Level: advanced
614 
615 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetJ0()`, `MatLMVMSetJ0Scale()`, `MatLMVMSetJ0ScaleDiag()`,
616           `MatLMVMSetJ0PC()`, `MatLMVMSetJ0KSP()`, `MatLMVMApplyJ0Inv()`
617 @*/
MatLMVMApplyJ0Fwd(Mat B,Vec X,Vec Y)618 PetscErrorCode MatLMVMApplyJ0Fwd(Mat B, Vec X, Vec Y)
619 {
620   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
621 
622   PetscFunctionBegin;
623   PetscCall(MatMult(lmvm->J0, X, Y));
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
MatLMVMApplyJ0HermitianTranspose(Mat B,Vec X,Vec Y)627 PETSC_INTERN PetscErrorCode MatLMVMApplyJ0HermitianTranspose(Mat B, Vec X, Vec Y)
628 {
629   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
630 
631   PetscFunctionBegin;
632   PetscCall(MatMultHermitianTranspose(lmvm->J0, X, Y));
633   PetscFunctionReturn(PETSC_SUCCESS);
634 }
635 
636 /*@
637   MatLMVMApplyJ0Inv - Applies some estimation of the initial Jacobian
638   inverse to the given vector.
639 
640   Input Parameters:
641 + B - A `MATLMVM` matrix
642 - X - vector to "multiply" with J0^{-1}
643 
644   Output Parameter:
645 . Y - resulting vector for the operation
646 
647   Level: advanced
648 
649   Note:
650   The specific form of the application
651   depends on whether the user provided a scaling factor, a J0 matrix,
652   a J0 `PC`, or a J0 `KSP` object. If no form of the initial Jacobian is
653   provided, the function simply does an identity matrix application
654   (vector copy).
655 
656 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMSetJ0()`, `MatLMVMSetJ0Scale()`, `MatLMVMSetJ0ScaleDiag()`,
657           `MatLMVMSetJ0PC()`, `MatLMVMSetJ0KSP()`, `MatLMVMApplyJ0Fwd()`
658 @*/
MatLMVMApplyJ0Inv(Mat B,Vec X,Vec Y)659 PetscErrorCode MatLMVMApplyJ0Inv(Mat B, Vec X, Vec Y)
660 {
661   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
662 
663   PetscFunctionBegin;
664   if (lmvm->disable_ksp_viewers) PetscCall(PetscOptionsPushCreateViewerOff(PETSC_TRUE));
665   PetscCall(KSPSolve(lmvm->J0ksp, X, Y));
666   if (lmvm->disable_ksp_viewers) PetscCall(PetscOptionsPopCreateViewerOff());
667   PetscFunctionReturn(PETSC_SUCCESS);
668 }
669 
MatLMVMApplyJ0InvTranspose(Mat B,Vec X,Vec Y)670 PETSC_INTERN PetscErrorCode MatLMVMApplyJ0InvTranspose(Mat B, Vec X, Vec Y)
671 {
672   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
673 
674   PetscFunctionBegin;
675   if (lmvm->disable_ksp_viewers) PetscCall(PetscOptionsPushCreateViewerOff(PETSC_TRUE));
676   PetscCall(KSPSolveTranspose(lmvm->J0ksp, X, Y));
677   if (lmvm->disable_ksp_viewers) PetscCall(PetscOptionsPopCreateViewerOff());
678   PetscFunctionReturn(PETSC_SUCCESS);
679 }
680 
MatLMVMApplyJ0InvHermitianTranspose(Mat B,Vec X,Vec Y)681 PETSC_INTERN PetscErrorCode MatLMVMApplyJ0InvHermitianTranspose(Mat B, Vec X, Vec Y)
682 {
683   PetscFunctionBegin;
684   if (!PetscDefined(USE_COMPLEX)) {
685     PetscCall(MatLMVMApplyJ0InvTranspose(B, X, Y));
686   } else {
687     Vec X_conj;
688 
689     PetscCall(VecDuplicate(X, &X_conj));
690     PetscCall(VecCopy(X, X_conj));
691     PetscCall(VecConjugate(X_conj));
692     PetscCall(MatLMVMApplyJ0InvTranspose(B, X_conj, Y));
693     PetscCall(VecConjugate(Y));
694     PetscCall(VecDestroy(&X_conj));
695   }
696   PetscFunctionReturn(PETSC_SUCCESS);
697 }
698 
699 /*@
700   MatLMVMIsAllocated - Returns a boolean flag that shows whether
701   the necessary data structures for the underlying matrix is allocated.
702 
703   Input Parameter:
704 . B - A `MATLMVM` matrix
705 
706   Output Parameter:
707 . flg - `PETSC_TRUE` if allocated, `PETSC_FALSE` otherwise
708 
709   Level: intermediate
710 
711 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMAllocate()`, `MatLMVMReset()`
712 @*/
MatLMVMIsAllocated(Mat B,PetscBool * flg)713 PetscErrorCode MatLMVMIsAllocated(Mat B, PetscBool *flg)
714 {
715   PetscBool same;
716 
717   PetscFunctionBegin;
718   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
719   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
720   PetscCheck(same, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "Matrix must be an LMVM-type.");
721   *flg = B->preallocated;
722   PetscFunctionReturn(PETSC_SUCCESS);
723 }
724 
725 /*@
726   MatLMVMAllocate - Produces all necessary common memory for
727   LMVM approximations based on the solution and function vectors
728   provided.
729 
730   Input Parameters:
731 + B - A `MATLMVM` matrix
732 . X - Solution vector
733 - F - Function vector
734 
735   Level: intermediate
736 
737   Note:
738   If `MatSetSizes()` and `MatSetUp()` have not been called
739   before `MatLMVMAllocate()`, the row layout of `B` will be set to match `F`
740   and the column layout of `B` will be set to match `X`.
741 
742 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMReset()`, `MatLMVMUpdate()`
743 @*/
MatLMVMAllocate(Mat B,Vec X,Vec F)744 PetscErrorCode MatLMVMAllocate(Mat B, Vec X, Vec F)
745 {
746   Mat_LMVM *lmvm;
747   PetscBool same;
748 
749   PetscFunctionBegin;
750   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
751   PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
752   PetscValidHeaderSpecific(F, VEC_CLASSID, 3);
753   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
754   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
755   lmvm = (Mat_LMVM *)B->data;
756   PetscCall(MatAllocate_LMVM(B, X, F));
757   PetscCall(MatLMVMAllocate(lmvm->J0, X, F));
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 /*@
762   MatLMVMResetShift - Zero the shift factor for a `MATLMVM`.
763 
764   Input Parameter:
765 . B - A `MATLMVM` matrix
766 
767   Level: intermediate
768 
769 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMAllocate()`, `MatLMVMUpdate()`
770 @*/
MatLMVMResetShift(Mat B)771 PetscErrorCode MatLMVMResetShift(Mat B)
772 {
773   Mat_LMVM *lmvm;
774   PetscBool same;
775 
776   PetscFunctionBegin;
777   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
778   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
779   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
780   lmvm        = (Mat_LMVM *)B->data;
781   lmvm->shift = 0.0;
782   PetscFunctionReturn(PETSC_SUCCESS);
783 }
784 
MatLMVMReset_Internal(Mat B,MatLMVMResetMode mode)785 PETSC_INTERN PetscErrorCode MatLMVMReset_Internal(Mat B, MatLMVMResetMode mode)
786 {
787   Mat_LMVM *lmvm;
788   PetscBool same;
789 
790   PetscFunctionBegin;
791   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
792   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
793   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
794   lmvm = (Mat_LMVM *)B->data;
795   PetscCall(MatLMVMReset_Internal(lmvm->J0, mode));
796   if (lmvm->ops->reset) PetscCall((*lmvm->ops->reset)(B, mode));
797   PetscCall(MatReset_LMVM(B, mode));
798   PetscFunctionReturn(PETSC_SUCCESS);
799 }
800 
801 /*@
802   MatLMVMReset - Flushes all of the accumulated updates out of
803   the `MATLMVM` approximation.
804 
805   Input Parameters:
806 + B           - A `MATLMVM` matrix
807 - destructive - flag for enabling destruction of data structures
808 
809   Level: intermediate
810 
811   Note:
812   In practice, this will not actually
813   destroy the data associated with the updates. It simply resets
814   counters, which leads to existing data being overwritten, and
815   `MatSolve()` being applied as if there are no updates. A boolean
816   flag is available to force destruction of the update vectors.
817 
818   If the user has provided another LMVM matrix as J0, the J0
819   matrix is also reset to the identity matrix in this function.
820 
821 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMAllocate()`, `MatLMVMUpdate()`
822 @*/
MatLMVMReset(Mat B,PetscBool destructive)823 PetscErrorCode MatLMVMReset(Mat B, PetscBool destructive)
824 {
825   Mat_LMVM *lmvm;
826   PetscBool same;
827 
828   PetscFunctionBegin;
829   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
830   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
831   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
832   lmvm = (Mat_LMVM *)B->data;
833   PetscCall(PetscInfo(B, "Resetting %s after %" PetscInt_FMT " iterations\n", ((PetscObject)B)->type_name, lmvm->k));
834   PetscCall(MatLMVMReset_Internal(B, destructive ? MAT_LMVM_RESET_ALL : MAT_LMVM_RESET_HISTORY));
835   ++lmvm->nresets;
836   PetscFunctionReturn(PETSC_SUCCESS);
837 }
838 
839 /*@
840   MatLMVMSetHistorySize - Set the number of past iterates to be
841   stored for the construction of the limited-memory quasi-Newton update.
842 
843   Input Parameters:
844 + B         - A `MATLMVM` matrix
845 - hist_size - number of past iterates (default 5)
846 
847   Options Database Key:
848 . -mat_lmvm_hist_size <m> - set number of past iterates
849 
850   Level: beginner
851 
852 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMGetUpdateCount()`
853 @*/
MatLMVMSetHistorySize(Mat B,PetscInt hist_size)854 PetscErrorCode MatLMVMSetHistorySize(Mat B, PetscInt hist_size)
855 {
856   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
857   PetscBool same;
858 
859   PetscFunctionBegin;
860   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
861   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
862   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
863   PetscCheck(hist_size >= 0, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "QN history size must be a non-negative integer.");
864   if (lmvm->m != hist_size) PetscCall(MatLMVMReset_Internal(B, MAT_LMVM_RESET_BASES));
865   lmvm->m = hist_size;
866   PetscFunctionReturn(PETSC_SUCCESS);
867 }
868 
MatLMVMGetHistorySize(Mat B,PetscInt * hist_size)869 PetscErrorCode MatLMVMGetHistorySize(Mat B, PetscInt *hist_size)
870 {
871   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
872   PetscBool same;
873 
874   PetscFunctionBegin;
875   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
876   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
877   if (!same) PetscFunctionReturn(PETSC_SUCCESS);
878   *hist_size = lmvm->m;
879   PetscFunctionReturn(PETSC_SUCCESS);
880 }
881 
882 /*@
883   MatLMVMGetUpdateCount - Returns the number of accepted updates.
884 
885   Input Parameter:
886 . B - A `MATLMVM` matrix
887 
888   Output Parameter:
889 . nupdates - number of accepted updates
890 
891   Level: intermediate
892 
893   Note:
894   This number may be greater than the total number of update vectors
895   stored in the matrix (`MatLMVMGetHistorySize()`). The counters are reset when `MatLMVMReset()`
896   is called.
897 
898 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMGetRejectCount()`, `MatLMVMReset()`
899 @*/
MatLMVMGetUpdateCount(Mat B,PetscInt * nupdates)900 PetscErrorCode MatLMVMGetUpdateCount(Mat B, PetscInt *nupdates)
901 {
902   Mat_LMVM *lmvm;
903   PetscBool same;
904 
905   PetscFunctionBegin;
906   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
907   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
908   PetscCheck(same, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "Matrix must be an LMVM-type.");
909   lmvm      = (Mat_LMVM *)B->data;
910   *nupdates = lmvm->nupdates;
911   PetscFunctionReturn(PETSC_SUCCESS);
912 }
913 
914 /*@
915   MatLMVMGetRejectCount - Returns the number of rejected updates.
916   The counters are reset when `MatLMVMReset()` is called.
917 
918   Input Parameter:
919 . B - A `MATLMVM` matrix
920 
921   Output Parameter:
922 . nrejects - number of rejected updates
923 
924   Level: intermediate
925 
926 .seealso: [](ch_ksp), [LMVM Matrices](sec_matlmvm), `MATLMVM`, `MatLMVMReset()`
927 @*/
MatLMVMGetRejectCount(Mat B,PetscInt * nrejects)928 PetscErrorCode MatLMVMGetRejectCount(Mat B, PetscInt *nrejects)
929 {
930   Mat_LMVM *lmvm;
931   PetscBool same;
932 
933   PetscFunctionBegin;
934   PetscValidHeaderSpecific(B, MAT_CLASSID, 1);
935   PetscCall(PetscObjectBaseTypeCompare((PetscObject)B, MATLMVM, &same));
936   PetscCheck(same, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "Matrix must be an LMVM-type.");
937   lmvm      = (Mat_LMVM *)B->data;
938   *nrejects = lmvm->nrejects;
939   PetscFunctionReturn(PETSC_SUCCESS);
940 }
941 
MatLMVMGetJ0Scalar(Mat B,PetscBool * is_scalar,PetscScalar * scale)942 PETSC_INTERN PetscErrorCode MatLMVMGetJ0Scalar(Mat B, PetscBool *is_scalar, PetscScalar *scale)
943 {
944   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
945 
946   PetscFunctionBegin;
947   PetscCall(PetscObjectTypeCompare((PetscObject)lmvm->J0, MATCONSTANTDIAGONAL, is_scalar));
948   if (*is_scalar) PetscCall(MatConstantDiagonalGetConstant(lmvm->J0, scale));
949   PetscFunctionReturn(PETSC_SUCCESS);
950 }
951 
MatLMVMUpdateOpVecs(Mat B,LMBasis X,LMBasis OpX,PetscErrorCode (* op)(Mat,Vec,Vec))952 static PetscErrorCode MatLMVMUpdateOpVecs(Mat B, LMBasis X, LMBasis OpX, PetscErrorCode (*op)(Mat, Vec, Vec))
953 {
954   Mat_LMVM        *lmvm = (Mat_LMVM *)B->data;
955   PetscObjectId    J0_id;
956   PetscObjectState J0_state;
957   PetscInt         oldest, next;
958 
959   PetscFunctionBegin;
960   PetscCall(PetscObjectGetId((PetscObject)lmvm->J0, &J0_id));
961   PetscCall(PetscObjectStateGet((PetscObject)lmvm->J0, &J0_state));
962   PetscCall(LMBasisGetRange(X, &oldest, &next));
963   if (OpX->operator_id != J0_id || OpX->operator_state != J0_state) {
964     // invalidate OpX
965     OpX->k              = oldest;
966     OpX->operator_id    = J0_id;
967     OpX->operator_state = J0_state;
968     PetscCall(LMBasisSetCachedProduct(OpX, NULL, NULL));
969   }
970   OpX->k = PetscMax(OpX->k, oldest);
971   for (PetscInt i = OpX->k; i < next; i++) {
972     Vec x_i, op_x_i;
973 
974     PetscCall(LMBasisGetVecRead(X, i, &x_i));
975     PetscCall(LMBasisGetNextVec(OpX, &op_x_i));
976     PetscCall(op(B, x_i, op_x_i));
977     PetscCall(LMBasisRestoreNextVec(OpX, &op_x_i));
978     PetscCall(LMBasisRestoreVecRead(X, i, &x_i));
979   }
980   PetscAssert(OpX->k == X->k && OpX->operator_id == J0_id && OpX->operator_state == J0_state, PetscObjectComm((PetscObject)B), PETSC_ERR_PLIB, "Invalid state for operator-updated LMBasis");
981   PetscFunctionReturn(PETSC_SUCCESS);
982 }
983 
MatLMVMUpdateOpDiffVecs(Mat B,LMBasis Y,PetscScalar alpha,LMBasis OpX,LMBasis YmalphaOpX)984 static PetscErrorCode MatLMVMUpdateOpDiffVecs(Mat B, LMBasis Y, PetscScalar alpha, LMBasis OpX, LMBasis YmalphaOpX)
985 {
986   Mat_LMVM        *lmvm = (Mat_LMVM *)B->data;
987   PetscInt         start;
988   PetscObjectId    J0_id;
989   PetscObjectState J0_state;
990   PetscInt         oldest, next;
991 
992   PetscFunctionBegin;
993   PetscCall(PetscObjectGetId((PetscObject)lmvm->J0, &J0_id));
994   PetscCall(PetscObjectStateGet((PetscObject)lmvm->J0, &J0_state));
995   PetscAssert(Y->m == OpX->m, PetscObjectComm((PetscObject)B), PETSC_ERR_PLIB, "Incompatible Y and OpX in MatLMVMUpdateOpDiffVecs()");
996   PetscAssert(Y->k == OpX->k, PetscObjectComm((PetscObject)B), PETSC_ERR_PLIB, "Stale OpX in MatLMVMUpdateOpDiffVecs()");
997   PetscCall(LMBasisGetRange(Y, &oldest, &next));
998   if (YmalphaOpX->operator_id != J0_id || YmalphaOpX->operator_state != J0_state) {
999     // invalidate OpX
1000     YmalphaOpX->k              = oldest;
1001     YmalphaOpX->operator_id    = J0_id;
1002     YmalphaOpX->operator_state = J0_state;
1003     PetscCall(LMBasisSetCachedProduct(YmalphaOpX, NULL, NULL));
1004   }
1005   YmalphaOpX->k = PetscMax(YmalphaOpX->k, oldest);
1006   start         = YmalphaOpX->k;
1007   if (next - start == Y->m) { // full matrix AXPY
1008     PetscCall(MatCopy(Y->vecs, YmalphaOpX->vecs, SAME_NONZERO_PATTERN));
1009     PetscCall(MatAXPY(YmalphaOpX->vecs, -alpha, OpX->vecs, SAME_NONZERO_PATTERN));
1010     YmalphaOpX->k = Y->k;
1011   } else {
1012     for (PetscInt i = start; i < next; i++) {
1013       Vec y_i, op_x_i, y_m_op_x_i;
1014 
1015       PetscCall(LMBasisGetVecRead(Y, i, &y_i));
1016       PetscCall(LMBasisGetVecRead(OpX, i, &op_x_i));
1017       PetscCall(LMBasisGetNextVec(YmalphaOpX, &y_m_op_x_i));
1018       PetscCall(VecAXPBYPCZ(y_m_op_x_i, 1.0, -alpha, 0.0, y_i, op_x_i));
1019       PetscCall(LMBasisRestoreNextVec(YmalphaOpX, &y_m_op_x_i));
1020       PetscCall(LMBasisRestoreVecRead(OpX, i, &op_x_i));
1021       PetscCall(LMBasisRestoreVecRead(Y, i, &y_i));
1022     }
1023   }
1024   PetscAssert(YmalphaOpX->k == Y->k && YmalphaOpX->operator_id == J0_id && YmalphaOpX->operator_state == J0_state, PetscObjectComm((PetscObject)B), PETSC_ERR_PLIB, "Invalid state for operator-updated LMBasis");
1025   PetscFunctionReturn(PETSC_SUCCESS);
1026 }
1027 
MatLMVMGetUpdatedBasis(Mat B,MatLMVMBasisType type,LMBasis * basis_p,MatLMVMBasisType * returned_type,PetscScalar * scale)1028 PETSC_INTERN PetscErrorCode MatLMVMGetUpdatedBasis(Mat B, MatLMVMBasisType type, LMBasis *basis_p, MatLMVMBasisType *returned_type, PetscScalar *scale)
1029 {
1030   Mat_LMVM   *lmvm = (Mat_LMVM *)B->data;
1031   LMBasis     basis;
1032   PetscBool   is_scalar;
1033   PetscScalar scale_;
1034 
1035   PetscFunctionBegin;
1036   switch (type) {
1037   case LMBASIS_S:
1038   case LMBASIS_Y:
1039     *basis_p = lmvm->basis[type];
1040     if (returned_type) *returned_type = type;
1041     if (scale) *scale = 1.0;
1042     break;
1043   case LMBASIS_B0S:
1044   case LMBASIS_H0Y:
1045     // if B_0 = gamma * I, do not actually compute these bases
1046     PetscAssertPointer(returned_type, 4);
1047     PetscAssertPointer(scale, 5);
1048     PetscCall(MatLMVMGetJ0Scalar(B, &is_scalar, &scale_));
1049     if (is_scalar) {
1050       *basis_p       = lmvm->basis[type == LMBASIS_B0S ? LMBASIS_S : LMBASIS_Y];
1051       *returned_type = (type == LMBASIS_B0S) ? LMBASIS_S : LMBASIS_Y;
1052       *scale         = (type == LMBASIS_B0S) ? scale_ : (1.0 / scale_);
1053     } else {
1054       LMBasis orig_basis = (type == LMBASIS_B0S) ? lmvm->basis[LMBASIS_S] : lmvm->basis[LMBASIS_Y];
1055 
1056       *returned_type = type;
1057       *scale         = 1.0;
1058       if (!lmvm->basis[type]) PetscCall(LMBasisCreate(MatLMVMBasisSizeOf(type) == LMBASIS_S ? lmvm->Xprev : lmvm->Fprev, lmvm->m, &lmvm->basis[type]));
1059       basis = lmvm->basis[type];
1060       PetscCall(MatLMVMUpdateOpVecs(B, orig_basis, basis, (type == LMBASIS_B0S) ? MatLMVMApplyJ0Fwd : MatLMVMApplyJ0Inv));
1061       *basis_p = basis;
1062     }
1063     break;
1064   case LMBASIS_S_MINUS_H0Y:
1065   case LMBASIS_Y_MINUS_B0S: {
1066     MatLMVMBasisType op_basis_t = (type == LMBASIS_S_MINUS_H0Y) ? LMBASIS_H0Y : LMBASIS_B0S;
1067     LMBasis          op_basis;
1068 
1069     if (returned_type) *returned_type = type;
1070     if (scale) *scale = 1.0;
1071     if (!lmvm->basis[type]) PetscCall(LMBasisCreate(MatLMVMBasisSizeOf(type) == LMBASIS_S ? lmvm->Xprev : lmvm->Fprev, lmvm->m, &lmvm->basis[type]));
1072     basis = lmvm->basis[type];
1073     PetscCall(MatLMVMGetUpdatedBasis(B, op_basis_t, &op_basis, &op_basis_t, &scale_));
1074     PetscCall(MatLMVMUpdateOpDiffVecs(B, lmvm->basis[MatLMVMBasisSizeOf(type)], scale_, op_basis, basis));
1075     *basis_p = basis;
1076   } break;
1077   default:
1078     PetscUnreachable();
1079   }
1080   basis = *basis_p;
1081   PetscFunctionReturn(PETSC_SUCCESS);
1082 }
1083 
MatLMVMBasisGetVecRead(Mat B,MatLMVMBasisType type,PetscInt i,Vec * y,PetscScalar * scale)1084 PETSC_INTERN PetscErrorCode MatLMVMBasisGetVecRead(Mat B, MatLMVMBasisType type, PetscInt i, Vec *y, PetscScalar *scale)
1085 {
1086   Mat_LMVM   *lmvm = (Mat_LMVM *)B->data;
1087   PetscBool   is_scalar;
1088   PetscScalar scale_;
1089 
1090   PetscFunctionBegin;
1091   switch (type) {
1092   case LMBASIS_B0S:
1093   case LMBASIS_H0Y:
1094     // if B_0 = gamma * I, do not actually compute these bases
1095     PetscCall(MatLMVMGetJ0Scalar(B, &is_scalar, &scale_));
1096     if (is_scalar) {
1097       *scale = (type == LMBASIS_B0S) ? scale_ : (1.0 / scale_);
1098       PetscCall(LMBasisGetVecRead(lmvm->basis[type == LMBASIS_B0S ? LMBASIS_S : LMBASIS_Y], i, y));
1099     } else if (lmvm->do_not_cache_J0_products) {
1100       Vec     tmp;
1101       Vec     w;
1102       LMBasis orig_basis = (type == LMBASIS_B0S) ? lmvm->basis[LMBASIS_S] : lmvm->basis[LMBASIS_Y];
1103       LMBasis size_basis = lmvm->basis[MatLMVMBasisSizeOf(type)];
1104 
1105       PetscCall(LMBasisGetVecRead(orig_basis, i, &w));
1106       PetscCall(LMBasisGetWorkVec(size_basis, &tmp));
1107       if (type == LMBASIS_B0S) {
1108         PetscCall(MatLMVMApplyJ0Fwd(B, w, tmp));
1109       } else {
1110         PetscCall(MatLMVMApplyJ0Inv(B, w, tmp));
1111       }
1112       PetscCall(LMBasisRestoreVecRead(orig_basis, i, &w));
1113       *scale = 1.0;
1114       *y     = tmp;
1115     } else {
1116       LMBasis     basis;
1117       PetscScalar dummy;
1118 
1119       PetscCall(MatLMVMGetUpdatedBasis(B, type, &basis, &type, &dummy));
1120       PetscCall(LMBasisGetVecRead(basis, i, y));
1121       *scale = 1.0;
1122     }
1123     break;
1124   default:
1125     SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "MatLMVMBasisGetVecRead() is only for LMBASIS_B0S and LMBASIS_H0Y.  Use MatLMVMGetUpdatedBasis() and LMBasisGetVecRead().");
1126   }
1127   PetscFunctionReturn(PETSC_SUCCESS);
1128 }
1129 
MatLMVMBasisRestoreVecRead(Mat B,MatLMVMBasisType type,PetscInt i,Vec * y,PetscScalar * scale)1130 PETSC_INTERN PetscErrorCode MatLMVMBasisRestoreVecRead(Mat B, MatLMVMBasisType type, PetscInt i, Vec *y, PetscScalar *scale)
1131 {
1132   Mat_LMVM   *lmvm = (Mat_LMVM *)B->data;
1133   PetscBool   is_scalar;
1134   PetscScalar scale_;
1135 
1136   PetscFunctionBegin;
1137   switch (type) {
1138   case LMBASIS_B0S:
1139   case LMBASIS_H0Y:
1140     // if B_0 = gamma * I, do not actually compute these bases
1141     PetscCall(MatLMVMGetJ0Scalar(B, &is_scalar, &scale_));
1142     if (is_scalar) {
1143       PetscCall(LMBasisRestoreVecRead(lmvm->basis[type == LMBASIS_B0S ? LMBASIS_S : LMBASIS_Y], i, y));
1144     } else if (lmvm->do_not_cache_J0_products) {
1145       LMBasis size_basis = lmvm->basis[MatLMVMBasisSizeOf(type)];
1146 
1147       PetscCall(LMBasisRestoreWorkVec(size_basis, y));
1148     } else {
1149       PetscCall(LMBasisRestoreVecRead(lmvm->basis[type], i, y));
1150     }
1151     break;
1152   default:
1153     SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONG, "MatLMVMBasisRestoreVecRead() is only for LMBASIS_B0S and LMBASIS_H0Y.  Use MatLMVMGetUpdatedBasis() and LMBasisRestoreVecRead().");
1154   }
1155   PetscFunctionReturn(PETSC_SUCCESS);
1156 }
1157 
MatLMVMGetRange(Mat B,PetscInt * oldest,PetscInt * next)1158 PETSC_INTERN PetscErrorCode MatLMVMGetRange(Mat B, PetscInt *oldest, PetscInt *next)
1159 {
1160   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
1161 
1162   PetscFunctionBegin;
1163   PetscCall(LMBasisGetRange(lmvm->basis[LMBASIS_S], oldest, next));
1164   PetscFunctionReturn(PETSC_SUCCESS);
1165 }
1166 
MatLMVMGetWorkRow(Mat B,Vec * array_p)1167 PETSC_INTERN PetscErrorCode MatLMVMGetWorkRow(Mat B, Vec *array_p)
1168 {
1169   LMBasis basis;
1170 
1171   PetscFunctionBegin;
1172   PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_Y, &basis, NULL, NULL));
1173   PetscCall(LMBasisGetWorkRow(basis, array_p));
1174   PetscFunctionReturn(PETSC_SUCCESS);
1175 }
1176 
MatLMVMRestoreWorkRow(Mat B,Vec * array_p)1177 PETSC_INTERN PetscErrorCode MatLMVMRestoreWorkRow(Mat B, Vec *array_p)
1178 {
1179   LMBasis basis;
1180 
1181   PetscFunctionBegin;
1182   PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_Y, &basis, NULL, NULL));
1183   PetscCall(LMBasisRestoreWorkRow(basis, array_p));
1184   PetscFunctionReturn(PETSC_SUCCESS);
1185 }
1186 
MatLMVMApplyOpThenVecs(PetscScalar alpha,Mat B,PetscInt oldest,PetscInt next,MatLMVMBasisType type_S,PetscErrorCode (* op)(Mat,Vec,Vec),Vec x,PetscScalar beta,Vec y)1187 static PetscErrorCode MatLMVMApplyOpThenVecs(PetscScalar alpha, Mat B, PetscInt oldest, PetscInt next, MatLMVMBasisType type_S, PetscErrorCode (*op)(Mat, Vec, Vec), Vec x, PetscScalar beta, Vec y)
1188 {
1189   LMBasis S;
1190   Vec     B0x;
1191 
1192   PetscFunctionBegin;
1193   PetscCall(MatLMVMGetUpdatedBasis(B, type_S, &S, NULL, NULL));
1194   PetscCall(LMBasisGetWorkVec(S, &B0x));
1195   PetscCall(op(B, x, B0x));
1196   PetscCall(LMBasisGEMVH(S, oldest, next, alpha, B0x, beta, y));
1197   PetscCall(LMBasisRestoreWorkVec(S, &B0x));
1198   PetscFunctionReturn(PETSC_SUCCESS);
1199 }
1200 
MatLMVMApplyVecsThenOp(PetscScalar alpha,Mat B,PetscInt oldest,PetscInt next,MatLMVMBasisType type_S,MatLMVMBasisType type_Y,PetscErrorCode (* op)(Mat,Vec,Vec),Vec x,PetscScalar beta,Vec y)1201 static PetscErrorCode MatLMVMApplyVecsThenOp(PetscScalar alpha, Mat B, PetscInt oldest, PetscInt next, MatLMVMBasisType type_S, MatLMVMBasisType type_Y, PetscErrorCode (*op)(Mat, Vec, Vec), Vec x, PetscScalar beta, Vec y)
1202 {
1203   LMBasis S, Y;
1204   Vec     S_x;
1205   Vec     B0S_x;
1206 
1207   PetscFunctionBegin;
1208   PetscCall(MatLMVMGetUpdatedBasis(B, type_S, &S, NULL, NULL));
1209   PetscCall(MatLMVMGetUpdatedBasis(B, type_Y, &Y, NULL, NULL));
1210   PetscCall(LMBasisGetWorkVec(S, &S_x));
1211   PetscCall(LMBasisGEMV(S, oldest, next, alpha, x, 0.0, S_x));
1212   PetscCall(LMBasisGetWorkVec(Y, &B0S_x));
1213   PetscCall(op(B, S_x, B0S_x));
1214   PetscCall(VecAYPX(y, beta, B0S_x));
1215   PetscCall(LMBasisRestoreWorkVec(Y, &B0S_x));
1216   PetscCall(LMBasisRestoreWorkVec(S, &S_x));
1217   PetscFunctionReturn(PETSC_SUCCESS);
1218 }
1219 
MatLMVMBasisGEMVH(Mat B,MatLMVMBasisType type,PetscInt oldest,PetscInt next,PetscScalar alpha,Vec v,PetscScalar beta,Vec array)1220 PETSC_INTERN PetscErrorCode MatLMVMBasisGEMVH(Mat B, MatLMVMBasisType type, PetscInt oldest, PetscInt next, PetscScalar alpha, Vec v, PetscScalar beta, Vec array)
1221 {
1222   Mat_LMVM        *lmvm              = (Mat_LMVM *)B->data;
1223   PetscBool        cache_J0_products = lmvm->do_not_cache_J0_products ? PETSC_FALSE : PETSC_TRUE;
1224   LMBasis          basis;
1225   MatLMVMBasisType basis_t;
1226   PetscScalar      gamma;
1227 
1228   PetscFunctionBegin;
1229   if (cache_J0_products || type == LMBASIS_S || type == LMBASIS_Y) {
1230     PetscCall(MatLMVMGetUpdatedBasis(B, type, &basis, &basis_t, &gamma));
1231     PetscCall(LMBasisGEMVH(basis, oldest, next, alpha * gamma, v, beta, array));
1232   } else {
1233     switch (type) {
1234     case LMBASIS_B0S:
1235       PetscCall(MatLMVMApplyOpThenVecs(alpha, B, oldest, next, LMBASIS_S, MatLMVMApplyJ0HermitianTranspose, v, beta, array));
1236       break;
1237     case LMBASIS_H0Y:
1238       PetscCall(MatLMVMApplyOpThenVecs(alpha, B, oldest, next, LMBASIS_Y, MatLMVMApplyJ0InvHermitianTranspose, v, beta, array));
1239       break;
1240     case LMBASIS_Y_MINUS_B0S:
1241       PetscCall(LMBasisGEMVH(lmvm->basis[LMBASIS_Y], oldest, next, alpha, v, beta, array));
1242       PetscCall(MatLMVMBasisGEMVH(B, LMBASIS_B0S, oldest, next, -alpha, v, 1.0, array));
1243       break;
1244     case LMBASIS_S_MINUS_H0Y:
1245       PetscCall(LMBasisGEMVH(lmvm->basis[LMBASIS_S], oldest, next, alpha, v, beta, array));
1246       PetscCall(MatLMVMBasisGEMVH(B, LMBASIS_H0Y, oldest, next, -alpha, v, 1.0, array));
1247       break;
1248     default:
1249       PetscUnreachable();
1250     }
1251   }
1252   PetscFunctionReturn(PETSC_SUCCESS);
1253 }
1254 
1255 // x must come from MatLMVMGetRowWork()
MatLMVMBasisGEMV(Mat B,MatLMVMBasisType type,PetscInt oldest,PetscInt next,PetscScalar alpha,Vec x,PetscScalar beta,Vec y)1256 PETSC_INTERN PetscErrorCode MatLMVMBasisGEMV(Mat B, MatLMVMBasisType type, PetscInt oldest, PetscInt next, PetscScalar alpha, Vec x, PetscScalar beta, Vec y)
1257 {
1258   Mat_LMVM *lmvm              = (Mat_LMVM *)B->data;
1259   PetscBool cache_J0_products = lmvm->do_not_cache_J0_products ? PETSC_FALSE : PETSC_TRUE;
1260   LMBasis   basis;
1261 
1262   PetscFunctionBegin;
1263   if (cache_J0_products || type == LMBASIS_S || type == LMBASIS_Y) {
1264     PetscScalar      gamma;
1265     MatLMVMBasisType base_type;
1266 
1267     PetscCall(MatLMVMGetUpdatedBasis(B, type, &basis, &base_type, &gamma));
1268     PetscCall(LMBasisGEMV(basis, oldest, next, alpha * gamma, x, beta, y));
1269   } else {
1270     switch (type) {
1271     case LMBASIS_B0S:
1272       PetscCall(MatLMVMApplyVecsThenOp(alpha, B, oldest, next, LMBASIS_S, LMBASIS_Y, MatLMVMApplyJ0Fwd, x, beta, y));
1273       break;
1274     case LMBASIS_H0Y:
1275       PetscCall(MatLMVMApplyVecsThenOp(alpha, B, oldest, next, LMBASIS_Y, LMBASIS_S, MatLMVMApplyJ0Inv, x, beta, y));
1276       break;
1277     case LMBASIS_Y_MINUS_B0S:
1278       PetscCall(LMBasisGEMV(lmvm->basis[LMBASIS_Y], oldest, next, alpha, x, beta, y));
1279       PetscCall(MatLMVMBasisGEMV(B, LMBASIS_B0S, oldest, next, -alpha, x, 1.0, y));
1280       break;
1281     case LMBASIS_S_MINUS_H0Y:
1282       PetscCall(LMBasisGEMV(lmvm->basis[LMBASIS_S], oldest, next, alpha, x, beta, y));
1283       PetscCall(MatLMVMBasisGEMV(B, LMBASIS_H0Y, oldest, next, -alpha, x, 1.0, y));
1284       break;
1285     default:
1286       PetscUnreachable();
1287     }
1288   }
1289   PetscFunctionReturn(PETSC_SUCCESS);
1290 }
1291 
MatLMVMCreateProducts(Mat B,LMBlockType block_type,LMProducts * products)1292 PETSC_INTERN PetscErrorCode MatLMVMCreateProducts(Mat B, LMBlockType block_type, LMProducts *products)
1293 {
1294   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
1295 
1296   PetscFunctionBegin;
1297   PetscCall(LMProductsCreate(lmvm->basis[LMBASIS_S], block_type, products));
1298   (*products)->debug = lmvm->debug;
1299   PetscFunctionReturn(PETSC_SUCCESS);
1300 }
1301 
MatLMVMProductsUpdate(Mat B,MatLMVMBasisType type_X,MatLMVMBasisType type_Y,LMBlockType block_type)1302 static PetscErrorCode MatLMVMProductsUpdate(Mat B, MatLMVMBasisType type_X, MatLMVMBasisType type_Y, LMBlockType block_type)
1303 {
1304   Mat_LMVM        *lmvm = (Mat_LMVM *)B->data;
1305   LMBasis          X, Y;
1306   MatLMVMBasisType true_type_X, true_type_Y;
1307   PetscScalar      alpha_X, alpha_Y;
1308   PetscInt         oldest, next;
1309   LMProducts       G;
1310 
1311   PetscFunctionBegin;
1312   PetscCall(MatLMVMGetUpdatedBasis(B, type_X, &X, &true_type_X, &alpha_X));
1313   PetscCall(MatLMVMGetUpdatedBasis(B, type_Y, &Y, &true_type_Y, &alpha_Y));
1314   if (!lmvm->products[block_type][true_type_X][true_type_Y]) PetscCall(MatLMVMCreateProducts(B, block_type, &lmvm->products[block_type][true_type_X][true_type_Y]));
1315   PetscCall(LMProductsUpdate(lmvm->products[block_type][true_type_X][true_type_Y], X, Y));
1316   if (true_type_X == type_X && true_type_Y == type_Y) PetscFunctionReturn(PETSC_SUCCESS);
1317   if (!lmvm->products[block_type][type_X][type_Y]) PetscCall(MatLMVMCreateProducts(B, block_type, &lmvm->products[block_type][type_X][type_Y]));
1318   G = lmvm->products[block_type][type_X][type_Y];
1319   PetscCall(MatLMVMGetRange(B, &oldest, &next));
1320   PetscCall(LMProductsPrepare(G, lmvm->J0, oldest, next));
1321   if (G->k < lmvm->k) {
1322     PetscCall(LMProductsCopy(lmvm->products[block_type][true_type_X][true_type_Y], lmvm->products[block_type][type_X][type_Y]));
1323     if (alpha_X * alpha_Y != 1.0) PetscCall(LMProductsScale(lmvm->products[block_type][type_X][type_Y], alpha_X * alpha_Y));
1324   }
1325   PetscFunctionReturn(PETSC_SUCCESS);
1326 }
1327 
MatLMVMGetUpdatedProducts(Mat B,MatLMVMBasisType type_X,MatLMVMBasisType type_Y,LMBlockType block_type,LMProducts * lmwd)1328 PETSC_INTERN PetscErrorCode MatLMVMGetUpdatedProducts(Mat B, MatLMVMBasisType type_X, MatLMVMBasisType type_Y, LMBlockType block_type, LMProducts *lmwd)
1329 {
1330   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
1331 
1332   PetscFunctionBegin;
1333   PetscCall(MatLMVMProductsUpdate(B, type_X, type_Y, block_type));
1334   *lmwd = lmvm->products[block_type][type_X][type_Y];
1335   PetscFunctionReturn(PETSC_SUCCESS);
1336 }
1337 
MatLMVMProductsInsertDiagonalValue(Mat B,MatLMVMBasisType type_X,MatLMVMBasisType type_Y,PetscInt idx,PetscScalar v)1338 PETSC_INTERN PetscErrorCode MatLMVMProductsInsertDiagonalValue(Mat B, MatLMVMBasisType type_X, MatLMVMBasisType type_Y, PetscInt idx, PetscScalar v)
1339 {
1340   Mat_LMVM  *lmvm = (Mat_LMVM *)B->data;
1341   LMProducts products;
1342 
1343   PetscFunctionBegin;
1344   if (!lmvm->products[LMBLOCK_DIAGONAL][type_X][type_Y]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_DIAGONAL, &lmvm->products[LMBLOCK_DIAGONAL][type_X][type_Y]));
1345   products = lmvm->products[LMBLOCK_DIAGONAL][type_X][type_Y];
1346   PetscCall(LMProductsInsertNextDiagonalValue(products, idx, v));
1347   PetscFunctionReturn(PETSC_SUCCESS);
1348 }
1349 
MatLMVMProductsGetDiagonalValue(Mat B,MatLMVMBasisType type_X,MatLMVMBasisType type_Y,PetscInt idx,PetscScalar * v)1350 PETSC_INTERN PetscErrorCode MatLMVMProductsGetDiagonalValue(Mat B, MatLMVMBasisType type_X, MatLMVMBasisType type_Y, PetscInt idx, PetscScalar *v)
1351 {
1352   LMProducts products = NULL;
1353 
1354   PetscFunctionBegin;
1355   PetscCall(MatLMVMGetUpdatedProducts(B, type_X, type_Y, LMBLOCK_DIAGONAL, &products));
1356   PetscCall(LMProductsGetDiagonalValue(products, idx, v));
1357   PetscFunctionReturn(PETSC_SUCCESS);
1358 }
1359