xref: /petsc/src/snes/impls/qn/qn.c (revision 8d47944aebcd8a3a8a4a1aebd4b3f440b4d70fab)
1 #include <petsc/private/snesimpl.h> /*I "petscsnes.h" I*/
2 #include <petscdm.h>
3 
4 #define H(i, j) qn->dXdFmat[i * qn->m + j]
5 
6 const char *const SNESQNScaleTypes[]   = {"DEFAULT", "NONE", "SCALAR", "DIAGONAL", "JACOBIAN", "SNESQNScaleType", "SNES_QN_SCALING_", NULL};
7 const char *const SNESQNRestartTypes[] = {"DEFAULT", "NONE", "POWELL", "PERIODIC", "SNESQNRestartType", "SNES_QN_RESTART_", NULL};
8 const char *const SNESQNTypes[]        = {"LBFGS", "BROYDEN", "BADBROYDEN", "SNESQNType", "SNES_QN_", NULL};
9 
10 typedef struct {
11   Mat               B;      /* Quasi-Newton approximation Matrix (MATLMVM) */
12   PetscInt          m;      /* The number of kept previous steps */
13   PetscReal        *lambda; /* The line search history of the method */
14   PetscBool         monflg;
15   PetscViewer       monitor;
16   PetscReal         powell_gamma; /* Powell angle restart condition */
17   PetscReal         scaling;      /* scaling of H0 */
18   SNESQNType        type;         /* the type of quasi-newton method used */
19   SNESQNScaleType   scale_type;   /* the type of scaling used */
20   SNESQNRestartType restart_type; /* determine the frequency and type of restart conditions */
21 } SNES_QN;
22 
23 static PetscErrorCode SNESSolve_QN(SNES snes)
24 {
25   SNES_QN             *qn = (SNES_QN *)snes->data;
26   Vec                  X, Xold;
27   Vec                  F, W;
28   Vec                  Y, D, Dold;
29   PetscInt             i, i_r;
30   PetscReal            fnorm, xnorm, ynorm;
31   SNESLineSearchReason lssucceed;
32   PetscBool            badstep, powell, periodic;
33   PetscScalar          DolddotD, DolddotDold;
34   SNESConvergedReason  reason;
35 #if defined(PETSC_USE_INFO)
36   PetscReal gnorm;
37 #endif
38 
39   /* basically just a regular newton's method except for the application of the Jacobian */
40 
41   PetscFunctionBegin;
42   PetscCheck(!snes->xl && !snes->xu && !snes->ops->computevariablebounds, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "SNES solver %s does not support bounds", ((PetscObject)snes)->type_name);
43 
44   PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite));
45   F    = snes->vec_func;       /* residual vector */
46   Y    = snes->vec_sol_update; /* search direction generated by J^-1D*/
47   W    = snes->work[3];
48   X    = snes->vec_sol; /* solution vector */
49   Xold = snes->work[0];
50 
51   /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */
52   D    = snes->work[1];
53   Dold = snes->work[2];
54 
55   snes->reason = SNES_CONVERGED_ITERATING;
56 
57   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
58   snes->iter = 0;
59   snes->norm = 0.;
60   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
61 
62   if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_PRECONDITIONED) {
63     PetscCall(SNESApplyNPC(snes, X, NULL, F));
64     PetscCall(SNESGetConvergedReason(snes->npc, &reason));
65     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
66       snes->reason = SNES_DIVERGED_INNER;
67       PetscFunctionReturn(PETSC_SUCCESS);
68     }
69     PetscCall(VecNorm(F, NORM_2, &fnorm));
70   } else {
71     if (!snes->vec_func_init_set) {
72       PetscCall(SNESComputeFunction(snes, X, F));
73     } else snes->vec_func_init_set = PETSC_FALSE;
74 
75     PetscCall(VecNorm(F, NORM_2, &fnorm));
76     SNESCheckFunctionNorm(snes, fnorm);
77   }
78   if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
79     PetscCall(SNESApplyNPC(snes, X, F, D));
80     PetscCall(SNESGetConvergedReason(snes->npc, &reason));
81     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
82       snes->reason = SNES_DIVERGED_INNER;
83       PetscFunctionReturn(PETSC_SUCCESS);
84     }
85   } else {
86     PetscCall(VecCopy(F, D));
87   }
88 
89   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
90   snes->norm = fnorm;
91   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
92   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
93   PetscCall(SNESMonitor(snes, 0, fnorm));
94 
95   /* test convergence */
96   PetscUseTypeMethod(snes, converged, 0, 0.0, 0.0, fnorm, &snes->reason, snes->cnvP);
97   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
98 
99   if (snes->npc && snes->npcside == PC_RIGHT) {
100     PetscCall(PetscLogEventBegin(SNES_NPCSolve, snes->npc, X, 0, 0));
101     PetscCall(SNESSolve(snes->npc, snes->vec_rhs, X));
102     PetscCall(PetscLogEventEnd(SNES_NPCSolve, snes->npc, X, 0, 0));
103     PetscCall(SNESGetConvergedReason(snes->npc, &reason));
104     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
105       snes->reason = SNES_DIVERGED_INNER;
106       PetscFunctionReturn(PETSC_SUCCESS);
107     }
108     PetscCall(SNESGetNPCFunction(snes, F, &fnorm));
109     PetscCall(VecCopy(F, D));
110   }
111 
112   /* general purpose update */
113   PetscTryTypeMethod(snes, update, snes->iter);
114 
115   /* scale the initial update */
116   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
117     PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre));
118     SNESCheckJacobianDomainerror(snes);
119     PetscCall(KSPSetOperators(snes->ksp, snes->jacobian, snes->jacobian_pre));
120     PetscCall(MatLMVMSetJ0KSP(qn->B, snes->ksp));
121   }
122 
123   for (i = 0, i_r = 0; i < snes->max_its; i++, i_r++) {
124     /* update QN approx and calculate step */
125     PetscCall(MatLMVMUpdate(qn->B, X, D));
126     PetscCall(MatSolve(qn->B, D, Y));
127 
128     /* line search for lambda */
129     ynorm = 1;
130 #if defined(PETSC_USE_INFO)
131     gnorm = fnorm;
132 #endif
133     PetscCall(VecCopy(D, Dold));
134     PetscCall(VecCopy(X, Xold));
135     PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y));
136     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
137     PetscCall(SNESLineSearchGetReason(snes->linesearch, &lssucceed));
138     PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm));
139     badstep = PETSC_FALSE;
140     if (lssucceed) {
141       if (++snes->numFailures >= snes->maxFailures) {
142         snes->reason = SNES_DIVERGED_LINE_SEARCH;
143         break;
144       }
145       badstep = PETSC_TRUE;
146     }
147 
148     /* convergence monitoring */
149     PetscCall(PetscInfo(snes, "fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n", (double)fnorm, (double)gnorm, (double)ynorm, (int)lssucceed));
150 
151     if (snes->npc && snes->npcside == PC_RIGHT) {
152       PetscCall(PetscLogEventBegin(SNES_NPCSolve, snes->npc, X, 0, 0));
153       PetscCall(SNESSolve(snes->npc, snes->vec_rhs, X));
154       PetscCall(PetscLogEventEnd(SNES_NPCSolve, snes->npc, X, 0, 0));
155       PetscCall(SNESGetConvergedReason(snes->npc, &reason));
156       if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
157         snes->reason = SNES_DIVERGED_INNER;
158         PetscFunctionReturn(PETSC_SUCCESS);
159       }
160       PetscCall(SNESGetNPCFunction(snes, F, &fnorm));
161     }
162 
163     PetscCall(SNESSetIterationNumber(snes, i + 1));
164     snes->norm  = fnorm;
165     snes->xnorm = xnorm;
166     snes->ynorm = ynorm;
167 
168     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, snes->iter));
169     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
170 
171     /* set parameter for default relative tolerance convergence test */
172     PetscUseTypeMethod(snes, converged, snes->iter, xnorm, ynorm, fnorm, &snes->reason, snes->cnvP);
173     if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
174     if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
175       PetscCall(SNESApplyNPC(snes, X, F, D));
176       PetscCall(SNESGetConvergedReason(snes->npc, &reason));
177       if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
178         snes->reason = SNES_DIVERGED_INNER;
179         PetscFunctionReturn(PETSC_SUCCESS);
180       }
181     } else {
182       PetscCall(VecCopy(F, D));
183     }
184 
185     /* general purpose update */
186     PetscTryTypeMethod(snes, update, snes->iter);
187 
188     /* restart conditions */
189     powell = PETSC_FALSE;
190     if (qn->restart_type == SNES_QN_RESTART_POWELL && i_r > 1) {
191       /* check restart by Powell's Criterion: |F^T H_0 Fold| > powell_gamma * |Fold^T H_0 Fold| */
192       if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
193         PetscCall(MatMult(snes->jacobian, Dold, W));
194       } else {
195         PetscCall(VecCopy(Dold, W));
196       }
197       PetscCall(VecDotBegin(W, Dold, &DolddotDold));
198       PetscCall(VecDotBegin(W, D, &DolddotD));
199       PetscCall(VecDotEnd(W, Dold, &DolddotDold));
200       PetscCall(VecDotEnd(W, D, &DolddotD));
201       if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma * PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE;
202     }
203     periodic = PETSC_FALSE;
204     if (qn->restart_type == SNES_QN_RESTART_PERIODIC) {
205       if (i_r > qn->m - 1) periodic = PETSC_TRUE;
206     }
207     /* restart if either powell or periodic restart is satisfied. */
208     if (badstep || powell || periodic) {
209       if (qn->monflg) {
210         PetscCall(PetscViewerASCIIAddTab(qn->monitor, ((PetscObject)snes)->tablevel + 2));
211         if (powell) {
212           PetscCall(PetscViewerASCIIPrintf(qn->monitor, "Powell restart! |%14.12e| > %6.4f*|%14.12e| i_r = %" PetscInt_FMT "\n", (double)PetscRealPart(DolddotD), (double)qn->powell_gamma, (double)PetscRealPart(DolddotDold), i_r));
213         } else {
214           PetscCall(PetscViewerASCIIPrintf(qn->monitor, "Periodic restart! i_r = %" PetscInt_FMT "\n", i_r));
215         }
216         PetscCall(PetscViewerASCIISubtractTab(qn->monitor, ((PetscObject)snes)->tablevel + 2));
217       }
218       i_r = -1;
219       if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
220         PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre));
221         SNESCheckJacobianDomainerror(snes);
222       }
223       PetscCall(MatLMVMReset(qn->B, PETSC_FALSE));
224     }
225   }
226   if (i == snes->max_its) {
227     PetscCall(PetscInfo(snes, "Maximum number of iterations has been reached: %" PetscInt_FMT "\n", snes->max_its));
228     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
229   }
230   PetscFunctionReturn(PETSC_SUCCESS);
231 }
232 
233 static PetscErrorCode SNESSetUp_QN(SNES snes)
234 {
235   SNES_QN *qn = (SNES_QN *)snes->data;
236   DM       dm;
237   PetscInt n, N;
238 
239   PetscFunctionBegin;
240 
241   if (!snes->vec_sol) {
242     PetscCall(SNESGetDM(snes, &dm));
243     PetscCall(DMCreateGlobalVector(dm, &snes->vec_sol));
244   }
245   PetscCall(SNESSetWorkVecs(snes, 4));
246 
247   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) PetscCall(SNESSetUpMatrices(snes));
248   if (snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_UNPRECONDITIONED;
249 
250   /* set method defaults */
251   if (qn->scale_type == SNES_QN_SCALE_DEFAULT) {
252     if (qn->type == SNES_QN_BADBROYDEN) {
253       qn->scale_type = SNES_QN_SCALE_NONE;
254     } else {
255       qn->scale_type = SNES_QN_SCALE_SCALAR;
256     }
257   }
258   if (qn->restart_type == SNES_QN_RESTART_DEFAULT) {
259     if (qn->type == SNES_QN_LBFGS) {
260       qn->restart_type = SNES_QN_RESTART_POWELL;
261     } else {
262       qn->restart_type = SNES_QN_RESTART_PERIODIC;
263     }
264   }
265 
266   /* Set up the LMVM matrix */
267   switch (qn->type) {
268   case SNES_QN_BROYDEN:
269     PetscCall(MatSetType(qn->B, MATLMVMBROYDEN));
270     qn->scale_type = SNES_QN_SCALE_NONE;
271     break;
272   case SNES_QN_BADBROYDEN:
273     PetscCall(MatSetType(qn->B, MATLMVMBADBROYDEN));
274     qn->scale_type = SNES_QN_SCALE_NONE;
275     break;
276   default:
277     PetscCall(MatSetType(qn->B, MATLMVMBFGS));
278     switch (qn->scale_type) {
279     case SNES_QN_SCALE_NONE:
280       PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_NONE));
281       break;
282     case SNES_QN_SCALE_SCALAR:
283       PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_SCALAR));
284       break;
285     case SNES_QN_SCALE_JACOBIAN:
286       PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_USER));
287       break;
288     case SNES_QN_SCALE_DIAGONAL:
289     case SNES_QN_SCALE_DEFAULT:
290     default:
291       break;
292     }
293     break;
294   }
295   PetscCall(VecGetLocalSize(snes->vec_sol, &n));
296   PetscCall(VecGetSize(snes->vec_sol, &N));
297   PetscCall(MatSetSizes(qn->B, n, n, N, N));
298   PetscCall(MatSetUp(qn->B));
299   PetscCall(MatLMVMReset(qn->B, PETSC_TRUE));
300   PetscCall(MatLMVMSetHistorySize(qn->B, qn->m));
301   PetscCall(MatLMVMAllocate(qn->B, snes->vec_sol, snes->vec_func));
302   PetscFunctionReturn(PETSC_SUCCESS);
303 }
304 
305 static PetscErrorCode SNESReset_QN(SNES snes)
306 {
307   SNES_QN *qn;
308 
309   PetscFunctionBegin;
310   if (snes->data) {
311     qn = (SNES_QN *)snes->data;
312     PetscCall(MatDestroy(&qn->B));
313   }
314   PetscFunctionReturn(PETSC_SUCCESS);
315 }
316 
317 static PetscErrorCode SNESDestroy_QN(SNES snes)
318 {
319   PetscFunctionBegin;
320   PetscCall(SNESReset_QN(snes));
321   PetscCall(PetscFree(snes->data));
322   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetScaleType_C", NULL));
323   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetRestartType_C", NULL));
324   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetType_C", NULL));
325   PetscFunctionReturn(PETSC_SUCCESS);
326 }
327 
328 static PetscErrorCode SNESSetFromOptions_QN(SNES snes, PetscOptionItems *PetscOptionsObject)
329 {
330   SNES_QN          *qn = (SNES_QN *)snes->data;
331   PetscBool         flg;
332   SNESLineSearch    linesearch;
333   SNESQNRestartType rtype = qn->restart_type;
334   SNESQNScaleType   stype = qn->scale_type;
335   SNESQNType        qtype = qn->type;
336 
337   PetscFunctionBegin;
338   PetscOptionsHeadBegin(PetscOptionsObject, "SNES QN options");
339   PetscCall(PetscOptionsInt("-snes_qn_m", "Number of past states saved for L-BFGS methods", "SNESQN", qn->m, &qn->m, NULL));
340   PetscCall(PetscOptionsReal("-snes_qn_powell_gamma", "Powell angle tolerance", "SNESQN", qn->powell_gamma, &qn->powell_gamma, NULL));
341   PetscCall(PetscOptionsBool("-snes_qn_monitor", "Monitor for the QN methods", "SNESQN", qn->monflg, &qn->monflg, NULL));
342   PetscCall(PetscOptionsEnum("-snes_qn_scale_type", "Scaling type", "SNESQNSetScaleType", SNESQNScaleTypes, (PetscEnum)stype, (PetscEnum *)&stype, &flg));
343   if (flg) PetscCall(SNESQNSetScaleType(snes, stype));
344 
345   PetscCall(PetscOptionsEnum("-snes_qn_restart_type", "Restart type", "SNESQNSetRestartType", SNESQNRestartTypes, (PetscEnum)rtype, (PetscEnum *)&rtype, &flg));
346   if (flg) PetscCall(SNESQNSetRestartType(snes, rtype));
347 
348   PetscCall(PetscOptionsEnum("-snes_qn_type", "Quasi-Newton update type", "", SNESQNTypes, (PetscEnum)qtype, (PetscEnum *)&qtype, &flg));
349   if (flg) PetscCall(SNESQNSetType(snes, qtype));
350   PetscCall(MatSetFromOptions(qn->B));
351   PetscOptionsHeadEnd();
352   if (!snes->linesearch) {
353     PetscCall(SNESGetLineSearch(snes, &linesearch));
354     if (!((PetscObject)linesearch)->type_name) {
355       if (qn->type == SNES_QN_LBFGS) {
356         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHCP));
357       } else if (qn->type == SNES_QN_BROYDEN) {
358         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBASIC));
359       } else {
360         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHL2));
361       }
362     }
363   }
364   if (qn->monflg) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)snes), &qn->monitor));
365   PetscFunctionReturn(PETSC_SUCCESS);
366 }
367 
368 static PetscErrorCode SNESView_QN(SNES snes, PetscViewer viewer)
369 {
370   SNES_QN  *qn = (SNES_QN *)snes->data;
371   PetscBool iascii;
372 
373   PetscFunctionBegin;
374   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
375   if (iascii) {
376     PetscCall(PetscViewerASCIIPrintf(viewer, "  type is %s, restart type is %s, scale type is %s\n", SNESQNTypes[qn->type], SNESQNRestartTypes[qn->restart_type], SNESQNScaleTypes[qn->scale_type]));
377     PetscCall(PetscViewerASCIIPrintf(viewer, "  Stored subspace size: %" PetscInt_FMT "\n", qn->m));
378   }
379   PetscFunctionReturn(PETSC_SUCCESS);
380 }
381 
382 /*@
383     SNESQNSetRestartType - Sets the restart type for `SNESQN`.
384 
385     Logically Collective
386 
387     Input Parameters:
388 +   snes - the iterative context
389 -   rtype - restart type
390 
391     Options Database Keys:
392 +   -snes_qn_restart_type <powell,periodic,none> - set the restart type
393 -   -snes_qn_m <m> - sets the number of stored updates and the restart period for periodic
394 
395     Level: intermediate
396 
397     `SNESQNRestartType`s:
398 +   `SNES_QN_RESTART_NONE` - never restart
399 .   `SNES_QN_RESTART_POWELL` - restart based upon descent criteria
400 -   `SNES_QN_RESTART_PERIODIC` - restart after a fixed number of iterations
401 
402 .seealso: `SNESQN`, `SNESQNRestartType`, `SNES_QN_RESTART_NONE`, `SNES_QN_RESTART_POWELL`, `SNES_QN_RESTART_PERIODIC`
403 @*/
404 PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype)
405 {
406   PetscFunctionBegin;
407   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
408   PetscTryMethod(snes, "SNESQNSetRestartType_C", (SNES, SNESQNRestartType), (snes, rtype));
409   PetscFunctionReturn(PETSC_SUCCESS);
410 }
411 
412 /*@
413     SNESQNSetScaleType - Sets the scaling type for the inner inverse Jacobian in `SNESQN`.
414 
415     Logically Collective
416 
417     Input Parameters:
418 +   snes - the nonlinear solver context
419 -   stype - scale type
420 
421     Options Database Key:
422 .   -snes_qn_scale_type <diagonal,none,scalar,jacobian> - Scaling type
423 
424     Level: intermediate
425 
426     `SNESQNScaleType`s:
427 +   `SNES_QN_SCALE_NONE` - don't scale the problem
428 .   `SNES_QN_SCALE_SCALAR` - use Shanno scaling
429 .   `SNES_QN_SCALE_DIAGONAL` - scale with a diagonalized BFGS formula (see Gilbert and Lemarechal 1989), available
430 -   `SNES_QN_SCALE_JACOBIAN` - scale by solving a linear system coming from the Jacobian you provided with SNESSetJacobian() computed at the first iteration
431                              of QN and at ever restart.
432 
433 .seealso: `SNES`, `SNESQN`, `SNESLineSearch`, `SNESQNScaleType`, `SNESSetJacobian()`
434 @*/
435 
436 PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype)
437 {
438   PetscFunctionBegin;
439   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
440   PetscTryMethod(snes, "SNESQNSetScaleType_C", (SNES, SNESQNScaleType), (snes, stype));
441   PetscFunctionReturn(PETSC_SUCCESS);
442 }
443 
444 PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype)
445 {
446   SNES_QN *qn = (SNES_QN *)snes->data;
447 
448   PetscFunctionBegin;
449   qn->scale_type = stype;
450   if (stype == SNES_QN_SCALE_JACOBIAN) snes->usesksp = PETSC_TRUE;
451   PetscFunctionReturn(PETSC_SUCCESS);
452 }
453 
454 PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype)
455 {
456   SNES_QN *qn = (SNES_QN *)snes->data;
457 
458   PetscFunctionBegin;
459   qn->restart_type = rtype;
460   PetscFunctionReturn(PETSC_SUCCESS);
461 }
462 
463 /*@
464     SNESQNSetType - Sets the quasi-Newton variant to be used in `SNESQN`.
465 
466     Logically Collective
467 
468     Input Parameters:
469 +   snes - the iterative context
470 -   qtype - variant type
471 
472     Options Database Key:
473 .   -snes_qn_type <lbfgs,broyden,badbroyden> - quasi-Newton type
474 
475     Level: beginner
476 
477     `SNESQNType`s:
478 +   `SNES_QN_LBFGS` - LBFGS variant
479 .   `SNES_QN_BROYDEN` - Broyden variant
480 -   `SNES_QN_BADBROYDEN` - Bad Broyden variant
481 
482 .seealso: `SNESQN`, `SNES_QN_LBFGS`, `SNES_QN_BROYDEN`, `SNES_QN_BADBROYDEN`, `SNESQNType`, `TAOLMVM`, `TAOBLMVM`
483 @*/
484 
485 PetscErrorCode SNESQNSetType(SNES snes, SNESQNType qtype)
486 {
487   PetscFunctionBegin;
488   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
489   PetscTryMethod(snes, "SNESQNSetType_C", (SNES, SNESQNType), (snes, qtype));
490   PetscFunctionReturn(PETSC_SUCCESS);
491 }
492 
493 PetscErrorCode SNESQNSetType_QN(SNES snes, SNESQNType qtype)
494 {
495   SNES_QN *qn = (SNES_QN *)snes->data;
496 
497   PetscFunctionBegin;
498   qn->type = qtype;
499   PetscFunctionReturn(PETSC_SUCCESS);
500 }
501 
502 /*MC
503       SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems.
504 
505       Options Database Keys:
506 +     -snes_qn_m <m> - Number of past states saved for the L-Broyden methods.
507 .     -snes_qn_restart_type <powell,periodic,none> - set the restart type
508 .     -snes_qn_powell_gamma - Angle condition for restart.
509 .     -snes_qn_powell_descent - Descent condition for restart.
510 .     -snes_qn_type <lbfgs,broyden,badbroyden> - QN type
511 .     -snes_qn_scale_type <diagonal,none,scalar,jacobian> - scaling performed on inner Jacobian
512 .     -snes_linesearch_type <cp, l2, basic> - Type of line search.
513 -     -snes_qn_monitor - Monitors the quasi-newton Jacobian.
514 
515       References:
516 +   * -   Kelley, C.T., Iterative Methods for Linear and Nonlinear Equations, Chapter 8, SIAM, 1995.
517 .   * -   R. Byrd, J. Nocedal, R. Schnabel, Representations of Quasi Newton Matrices and their use in Limited Memory Methods,
518       Technical Report, Northwestern University, June 1992.
519 .   * -   Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker, Experiments with Quasi-Newton Methods in Solving Stiff ODE
520       Systems, SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985.
521 .   * -   Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers",
522        SIAM Review, 57(4), 2015
523 .   * -   Griewank, Andreas. "Broyden updating, the good and the bad!." Doc. Math (2012): 301-315.
524 .   * -   Gilbert, Jean Charles, and Claude Lemar{\'e}chal. "Some numerical experiments with variable-storage quasi-Newton algorithms."
525       Mathematical programming 45.1-3 (1989): 407-435.
526 -   * -   Dener A., Munson T. "Accelerating Limited-Memory Quasi-Newton Convergence for Large-Scale Optimization"
527       Computational Science - ICCS 2019. ICCS 2019. Lecture Notes in Computer Science, vol 11538. Springer, Cham
528 
529       Level: beginner
530 
531       Notes:
532       This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using
533       previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one
534       updates.
535 
536       When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied.  The first of
537       these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this
538       iteration as the current iteration's values when constructing the approximate Jacobian.  The second, composed,
539       perturbs the problem the Jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner.
540 
541       Uses left nonlinear preconditioning by default.
542 
543 .seealso: `SNESQNRestartType`, `SNESQNSetRestartType()`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESNEWTONTR`,
544           `SNESQNScaleType`, `SNESQNSetScaleType()`, `SNESQNSetType`, `SNESQNSetType()`
545 M*/
546 PETSC_EXTERN PetscErrorCode SNESCreate_QN(SNES snes)
547 {
548   SNES_QN    *qn;
549   const char *optionsprefix;
550 
551   PetscFunctionBegin;
552   snes->ops->setup          = SNESSetUp_QN;
553   snes->ops->solve          = SNESSolve_QN;
554   snes->ops->destroy        = SNESDestroy_QN;
555   snes->ops->setfromoptions = SNESSetFromOptions_QN;
556   snes->ops->view           = SNESView_QN;
557   snes->ops->reset          = SNESReset_QN;
558 
559   snes->npcside = PC_LEFT;
560 
561   snes->usesnpc = PETSC_TRUE;
562   snes->usesksp = PETSC_FALSE;
563 
564   snes->alwayscomputesfinalresidual = PETSC_TRUE;
565 
566   if (!snes->tolerancesset) {
567     snes->max_funcs = 30000;
568     snes->max_its   = 10000;
569   }
570 
571   PetscCall(PetscNew(&qn));
572   snes->data       = (void *)qn;
573   qn->m            = 10;
574   qn->scaling      = 1.0;
575   qn->monitor      = NULL;
576   qn->monflg       = PETSC_FALSE;
577   qn->powell_gamma = 0.9999;
578   qn->scale_type   = SNES_QN_SCALE_DEFAULT;
579   qn->restart_type = SNES_QN_RESTART_DEFAULT;
580   qn->type         = SNES_QN_LBFGS;
581 
582   PetscCall(MatCreate(PetscObjectComm((PetscObject)snes), &qn->B));
583   PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix));
584   PetscCall(MatSetOptionsPrefix(qn->B, optionsprefix));
585 
586   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetScaleType_C", SNESQNSetScaleType_QN));
587   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetRestartType_C", SNESQNSetRestartType_QN));
588   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetType_C", SNESQNSetType_QN));
589   PetscFunctionReturn(PETSC_SUCCESS);
590 }
591