xref: /petsc/src/snes/impls/qn/qn.c (revision e4094ef18e7e53fda86cf35f3a47fda48a8e77d8)
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   PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm));
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 
170     /* set parameter for default relative tolerance convergence test */
171     PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm));
172     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
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   PetscFunctionReturn(PETSC_SUCCESS);
227 }
228 
229 static PetscErrorCode SNESSetUp_QN(SNES snes)
230 {
231   SNES_QN *qn = (SNES_QN *)snes->data;
232   DM       dm;
233   PetscInt n, N;
234 
235   PetscFunctionBegin;
236 
237   if (!snes->vec_sol) {
238     PetscCall(SNESGetDM(snes, &dm));
239     PetscCall(DMCreateGlobalVector(dm, &snes->vec_sol));
240   }
241   PetscCall(SNESSetWorkVecs(snes, 4));
242 
243   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) PetscCall(SNESSetUpMatrices(snes));
244   if (snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_UNPRECONDITIONED;
245 
246   /* set method defaults */
247   if (qn->scale_type == SNES_QN_SCALE_DEFAULT) {
248     if (qn->type == SNES_QN_BADBROYDEN) {
249       qn->scale_type = SNES_QN_SCALE_NONE;
250     } else {
251       qn->scale_type = SNES_QN_SCALE_SCALAR;
252     }
253   }
254   if (qn->restart_type == SNES_QN_RESTART_DEFAULT) {
255     if (qn->type == SNES_QN_LBFGS) {
256       qn->restart_type = SNES_QN_RESTART_POWELL;
257     } else {
258       qn->restart_type = SNES_QN_RESTART_PERIODIC;
259     }
260   }
261 
262   /* Set up the LMVM matrix */
263   switch (qn->type) {
264   case SNES_QN_BROYDEN:
265     PetscCall(MatSetType(qn->B, MATLMVMBROYDEN));
266     qn->scale_type = SNES_QN_SCALE_NONE;
267     break;
268   case SNES_QN_BADBROYDEN:
269     PetscCall(MatSetType(qn->B, MATLMVMBADBROYDEN));
270     qn->scale_type = SNES_QN_SCALE_NONE;
271     break;
272   default:
273     PetscCall(MatSetType(qn->B, MATLMVMBFGS));
274     switch (qn->scale_type) {
275     case SNES_QN_SCALE_NONE:
276       PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_NONE));
277       break;
278     case SNES_QN_SCALE_SCALAR:
279       PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_SCALAR));
280       break;
281     case SNES_QN_SCALE_JACOBIAN:
282       PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_USER));
283       break;
284     case SNES_QN_SCALE_DIAGONAL:
285     case SNES_QN_SCALE_DEFAULT:
286     default:
287       break;
288     }
289     break;
290   }
291   PetscCall(VecGetLocalSize(snes->vec_sol, &n));
292   PetscCall(VecGetSize(snes->vec_sol, &N));
293   PetscCall(MatSetSizes(qn->B, n, n, N, N));
294   PetscCall(MatSetUp(qn->B));
295   PetscCall(MatLMVMReset(qn->B, PETSC_TRUE));
296   PetscCall(MatLMVMSetHistorySize(qn->B, qn->m));
297   PetscCall(MatLMVMAllocate(qn->B, snes->vec_sol, snes->vec_func));
298   PetscFunctionReturn(PETSC_SUCCESS);
299 }
300 
301 static PetscErrorCode SNESReset_QN(SNES snes)
302 {
303   SNES_QN *qn;
304 
305   PetscFunctionBegin;
306   if (snes->data) {
307     qn = (SNES_QN *)snes->data;
308     PetscCall(MatDestroy(&qn->B));
309   }
310   PetscFunctionReturn(PETSC_SUCCESS);
311 }
312 
313 static PetscErrorCode SNESDestroy_QN(SNES snes)
314 {
315   PetscFunctionBegin;
316   PetscCall(SNESReset_QN(snes));
317   PetscCall(PetscFree(snes->data));
318   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetScaleType_C", NULL));
319   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetRestartType_C", NULL));
320   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetType_C", NULL));
321   PetscFunctionReturn(PETSC_SUCCESS);
322 }
323 
324 static PetscErrorCode SNESSetFromOptions_QN(SNES snes, PetscOptionItems *PetscOptionsObject)
325 {
326   SNES_QN          *qn = (SNES_QN *)snes->data;
327   PetscBool         flg;
328   SNESLineSearch    linesearch;
329   SNESQNRestartType rtype = qn->restart_type;
330   SNESQNScaleType   stype = qn->scale_type;
331   SNESQNType        qtype = qn->type;
332 
333   PetscFunctionBegin;
334   PetscOptionsHeadBegin(PetscOptionsObject, "SNES QN options");
335   PetscCall(PetscOptionsInt("-snes_qn_m", "Number of past states saved for L-BFGS methods", "SNESQN", qn->m, &qn->m, NULL));
336   PetscCall(PetscOptionsReal("-snes_qn_powell_gamma", "Powell angle tolerance", "SNESQN", qn->powell_gamma, &qn->powell_gamma, NULL));
337   PetscCall(PetscOptionsBool("-snes_qn_monitor", "Monitor for the QN methods", "SNESQN", qn->monflg, &qn->monflg, NULL));
338   PetscCall(PetscOptionsEnum("-snes_qn_scale_type", "Scaling type", "SNESQNSetScaleType", SNESQNScaleTypes, (PetscEnum)stype, (PetscEnum *)&stype, &flg));
339   if (flg) PetscCall(SNESQNSetScaleType(snes, stype));
340 
341   PetscCall(PetscOptionsEnum("-snes_qn_restart_type", "Restart type", "SNESQNSetRestartType", SNESQNRestartTypes, (PetscEnum)rtype, (PetscEnum *)&rtype, &flg));
342   if (flg) PetscCall(SNESQNSetRestartType(snes, rtype));
343 
344   PetscCall(PetscOptionsEnum("-snes_qn_type", "Quasi-Newton update type", "", SNESQNTypes, (PetscEnum)qtype, (PetscEnum *)&qtype, &flg));
345   if (flg) PetscCall(SNESQNSetType(snes, qtype));
346   PetscCall(MatSetFromOptions(qn->B));
347   PetscOptionsHeadEnd();
348   if (!snes->linesearch) {
349     PetscCall(SNESGetLineSearch(snes, &linesearch));
350     if (!((PetscObject)linesearch)->type_name) {
351       if (qn->type == SNES_QN_LBFGS) {
352         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHCP));
353       } else if (qn->type == SNES_QN_BROYDEN) {
354         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBASIC));
355       } else {
356         PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHL2));
357       }
358     }
359   }
360   if (qn->monflg) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)snes), &qn->monitor));
361   PetscFunctionReturn(PETSC_SUCCESS);
362 }
363 
364 static PetscErrorCode SNESView_QN(SNES snes, PetscViewer viewer)
365 {
366   SNES_QN  *qn = (SNES_QN *)snes->data;
367   PetscBool iascii;
368 
369   PetscFunctionBegin;
370   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
371   if (iascii) {
372     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]));
373     PetscCall(PetscViewerASCIIPrintf(viewer, "  Stored subspace size: %" PetscInt_FMT "\n", qn->m));
374   }
375   PetscFunctionReturn(PETSC_SUCCESS);
376 }
377 
378 /*@
379   SNESQNSetRestartType - Sets the restart type for `SNESQN`.
380 
381   Logically Collective
382 
383   Input Parameters:
384 + snes  - the iterative context
385 - rtype - restart type
386 
387   Options Database Keys:
388 + -snes_qn_restart_type <powell,periodic,none> - set the restart type
389 - -snes_qn_m <m>                               - sets the number of stored updates and the restart period for periodic
390 
391   Level: intermediate
392 
393   `SNESQNRestartType`s:
394 +   `SNES_QN_RESTART_NONE` - never restart
395 .   `SNES_QN_RESTART_POWELL` - restart based upon descent criteria
396 -   `SNES_QN_RESTART_PERIODIC` - restart after a fixed number of iterations
397 
398 .seealso: `SNESQN`, `SNESQNRestartType`, `SNES_QN_RESTART_NONE`, `SNES_QN_RESTART_POWELL`, `SNES_QN_RESTART_PERIODIC`
399 @*/
400 PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype)
401 {
402   PetscFunctionBegin;
403   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
404   PetscTryMethod(snes, "SNESQNSetRestartType_C", (SNES, SNESQNRestartType), (snes, rtype));
405   PetscFunctionReturn(PETSC_SUCCESS);
406 }
407 
408 /*@
409   SNESQNSetScaleType - Sets the scaling type for the inner inverse Jacobian in `SNESQN`.
410 
411   Logically Collective
412 
413   Input Parameters:
414 + snes  - the nonlinear solver context
415 - stype - scale type
416 
417   Options Database Key:
418 . -snes_qn_scale_type <diagonal,none,scalar,jacobian> - Scaling type
419 
420   Level: intermediate
421 
422   `SNESQNScaleType`s:
423 +   `SNES_QN_SCALE_NONE` - don't scale the problem
424 .   `SNES_QN_SCALE_SCALAR` - use Shanno scaling
425 .   `SNES_QN_SCALE_DIAGONAL` - scale with a diagonalized BFGS formula (see Gilbert and Lemarechal 1989), available
426 -   `SNES_QN_SCALE_JACOBIAN` - scale by solving a linear system coming from the Jacobian you provided with SNESSetJacobian() computed at the first iteration
427   of QN and at ever restart.
428 
429 .seealso: `SNES`, `SNESQN`, `SNESLineSearch`, `SNESQNScaleType`, `SNESSetJacobian()`
430 @*/
431 PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype)
432 {
433   PetscFunctionBegin;
434   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
435   PetscTryMethod(snes, "SNESQNSetScaleType_C", (SNES, SNESQNScaleType), (snes, stype));
436   PetscFunctionReturn(PETSC_SUCCESS);
437 }
438 
439 PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype)
440 {
441   SNES_QN *qn = (SNES_QN *)snes->data;
442 
443   PetscFunctionBegin;
444   qn->scale_type = stype;
445   if (stype == SNES_QN_SCALE_JACOBIAN) snes->usesksp = PETSC_TRUE;
446   PetscFunctionReturn(PETSC_SUCCESS);
447 }
448 
449 PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype)
450 {
451   SNES_QN *qn = (SNES_QN *)snes->data;
452 
453   PetscFunctionBegin;
454   qn->restart_type = rtype;
455   PetscFunctionReturn(PETSC_SUCCESS);
456 }
457 
458 /*@
459   SNESQNSetType - Sets the quasi-Newton variant to be used in `SNESQN`.
460 
461   Logically Collective
462 
463   Input Parameters:
464 + snes  - the iterative context
465 - qtype - variant type
466 
467   Options Database Key:
468 . -snes_qn_type <lbfgs,broyden,badbroyden> - quasi-Newton type
469 
470   Level: beginner
471 
472   `SNESQNType`s:
473 +   `SNES_QN_LBFGS` - LBFGS variant
474 .   `SNES_QN_BROYDEN` - Broyden variant
475 -   `SNES_QN_BADBROYDEN` - Bad Broyden variant
476 
477 .seealso: `SNESQN`, `SNES_QN_LBFGS`, `SNES_QN_BROYDEN`, `SNES_QN_BADBROYDEN`, `SNESQNType`, `TAOLMVM`, `TAOBLMVM`
478 @*/
479 PetscErrorCode SNESQNSetType(SNES snes, SNESQNType qtype)
480 {
481   PetscFunctionBegin;
482   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
483   PetscTryMethod(snes, "SNESQNSetType_C", (SNES, SNESQNType), (snes, qtype));
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 PetscErrorCode SNESQNSetType_QN(SNES snes, SNESQNType qtype)
488 {
489   SNES_QN *qn = (SNES_QN *)snes->data;
490 
491   PetscFunctionBegin;
492   qn->type = qtype;
493   PetscFunctionReturn(PETSC_SUCCESS);
494 }
495 
496 /*MC
497       SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems.
498 
499       Options Database Keys:
500 +     -snes_qn_m <m> - Number of past states saved for the L-Broyden methods.
501 .     -snes_qn_restart_type <powell,periodic,none> - set the restart type
502 .     -snes_qn_powell_gamma - Angle condition for restart.
503 .     -snes_qn_powell_descent - Descent condition for restart.
504 .     -snes_qn_type <lbfgs,broyden,badbroyden> - QN type
505 .     -snes_qn_scale_type <diagonal,none,scalar,jacobian> - scaling performed on inner Jacobian
506 .     -snes_linesearch_type <cp, l2, basic> - Type of line search.
507 -     -snes_qn_monitor - Monitors the quasi-newton Jacobian.
508 
509       References:
510 +   * -   Kelley, C.T., Iterative Methods for Linear and Nonlinear Equations, Chapter 8, SIAM, 1995.
511 .   * -   R. Byrd, J. Nocedal, R. Schnabel, Representations of Quasi Newton Matrices and their use in Limited Memory Methods,
512       Technical Report, Northwestern University, June 1992.
513 .   * -   Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker, Experiments with Quasi-Newton Methods in Solving Stiff ODE
514       Systems, SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985.
515 .   * -   Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers",
516        SIAM Review, 57(4), 2015
517 .   * -   Griewank, Andreas. "Broyden updating, the good and the bad!." Doc. Math (2012): 301-315.
518 .   * -   Gilbert, Jean Charles, and Claude Lemar{\'e}chal. "Some numerical experiments with variable-storage quasi-Newton algorithms."
519       Mathematical programming 45.1-3 (1989): 407-435.
520 -   * -   Dener A., Munson T. "Accelerating Limited-Memory Quasi-Newton Convergence for Large-Scale Optimization"
521       Computational Science - ICCS 2019. ICCS 2019. Lecture Notes in Computer Science, vol 11538. Springer, Cham
522 
523       Level: beginner
524 
525       Notes:
526       This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using
527       previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one
528       updates.
529 
530       When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied.  The first of
531       these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this
532       iteration as the current iteration's values when constructing the approximate Jacobian.  The second, composed,
533       perturbs the problem the Jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner.
534 
535       Uses left nonlinear preconditioning by default.
536 
537 .seealso: `SNESQNRestartType`, `SNESQNSetRestartType()`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESNEWTONTR`,
538           `SNESQNScaleType`, `SNESQNSetScaleType()`, `SNESQNSetType`, `SNESQNSetType()`
539 M*/
540 PETSC_EXTERN PetscErrorCode SNESCreate_QN(SNES snes)
541 {
542   SNES_QN    *qn;
543   const char *optionsprefix;
544 
545   PetscFunctionBegin;
546   snes->ops->setup          = SNESSetUp_QN;
547   snes->ops->solve          = SNESSolve_QN;
548   snes->ops->destroy        = SNESDestroy_QN;
549   snes->ops->setfromoptions = SNESSetFromOptions_QN;
550   snes->ops->view           = SNESView_QN;
551   snes->ops->reset          = SNESReset_QN;
552 
553   snes->npcside = PC_LEFT;
554 
555   snes->usesnpc = PETSC_TRUE;
556   snes->usesksp = PETSC_FALSE;
557 
558   snes->alwayscomputesfinalresidual = PETSC_TRUE;
559 
560   if (!snes->tolerancesset) {
561     snes->max_funcs = 30000;
562     snes->max_its   = 10000;
563   }
564 
565   PetscCall(PetscNew(&qn));
566   snes->data       = (void *)qn;
567   qn->m            = 10;
568   qn->scaling      = 1.0;
569   qn->monitor      = NULL;
570   qn->monflg       = PETSC_FALSE;
571   qn->powell_gamma = 0.9999;
572   qn->scale_type   = SNES_QN_SCALE_DEFAULT;
573   qn->restart_type = SNES_QN_RESTART_DEFAULT;
574   qn->type         = SNES_QN_LBFGS;
575 
576   PetscCall(MatCreate(PetscObjectComm((PetscObject)snes), &qn->B));
577   PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix));
578   PetscCall(MatSetOptionsPrefix(qn->B, optionsprefix));
579 
580   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetScaleType_C", SNESQNSetScaleType_QN));
581   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetRestartType_C", SNESQNSetRestartType_QN));
582   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetType_C", SNESQNSetType_QN));
583   PetscFunctionReturn(PETSC_SUCCESS);
584 }
585