xref: /petsc/src/snes/impls/qn/qn.c (revision bcee047adeeb73090d7e36cc71e39fc287cdbb97)
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 
432 PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype)
433 {
434   PetscFunctionBegin;
435   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
436   PetscTryMethod(snes, "SNESQNSetScaleType_C", (SNES, SNESQNScaleType), (snes, stype));
437   PetscFunctionReturn(PETSC_SUCCESS);
438 }
439 
440 PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype)
441 {
442   SNES_QN *qn = (SNES_QN *)snes->data;
443 
444   PetscFunctionBegin;
445   qn->scale_type = stype;
446   if (stype == SNES_QN_SCALE_JACOBIAN) snes->usesksp = PETSC_TRUE;
447   PetscFunctionReturn(PETSC_SUCCESS);
448 }
449 
450 PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype)
451 {
452   SNES_QN *qn = (SNES_QN *)snes->data;
453 
454   PetscFunctionBegin;
455   qn->restart_type = rtype;
456   PetscFunctionReturn(PETSC_SUCCESS);
457 }
458 
459 /*@
460     SNESQNSetType - Sets the quasi-Newton variant to be used in `SNESQN`.
461 
462     Logically Collective
463 
464     Input Parameters:
465 +   snes - the iterative context
466 -   qtype - variant type
467 
468     Options Database Key:
469 .   -snes_qn_type <lbfgs,broyden,badbroyden> - quasi-Newton type
470 
471     Level: beginner
472 
473     `SNESQNType`s:
474 +   `SNES_QN_LBFGS` - LBFGS variant
475 .   `SNES_QN_BROYDEN` - Broyden variant
476 -   `SNES_QN_BADBROYDEN` - Bad Broyden variant
477 
478 .seealso: `SNESQN`, `SNES_QN_LBFGS`, `SNES_QN_BROYDEN`, `SNES_QN_BADBROYDEN`, `SNESQNType`, `TAOLMVM`, `TAOBLMVM`
479 @*/
480 
481 PetscErrorCode SNESQNSetType(SNES snes, SNESQNType qtype)
482 {
483   PetscFunctionBegin;
484   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
485   PetscTryMethod(snes, "SNESQNSetType_C", (SNES, SNESQNType), (snes, qtype));
486   PetscFunctionReturn(PETSC_SUCCESS);
487 }
488 
489 PetscErrorCode SNESQNSetType_QN(SNES snes, SNESQNType qtype)
490 {
491   SNES_QN *qn = (SNES_QN *)snes->data;
492 
493   PetscFunctionBegin;
494   qn->type = qtype;
495   PetscFunctionReturn(PETSC_SUCCESS);
496 }
497 
498 /*MC
499       SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems.
500 
501       Options Database Keys:
502 +     -snes_qn_m <m> - Number of past states saved for the L-Broyden methods.
503 .     -snes_qn_restart_type <powell,periodic,none> - set the restart type
504 .     -snes_qn_powell_gamma - Angle condition for restart.
505 .     -snes_qn_powell_descent - Descent condition for restart.
506 .     -snes_qn_type <lbfgs,broyden,badbroyden> - QN type
507 .     -snes_qn_scale_type <diagonal,none,scalar,jacobian> - scaling performed on inner Jacobian
508 .     -snes_linesearch_type <cp, l2, basic> - Type of line search.
509 -     -snes_qn_monitor - Monitors the quasi-newton Jacobian.
510 
511       References:
512 +   * -   Kelley, C.T., Iterative Methods for Linear and Nonlinear Equations, Chapter 8, SIAM, 1995.
513 .   * -   R. Byrd, J. Nocedal, R. Schnabel, Representations of Quasi Newton Matrices and their use in Limited Memory Methods,
514       Technical Report, Northwestern University, June 1992.
515 .   * -   Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker, Experiments with Quasi-Newton Methods in Solving Stiff ODE
516       Systems, SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985.
517 .   * -   Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers",
518        SIAM Review, 57(4), 2015
519 .   * -   Griewank, Andreas. "Broyden updating, the good and the bad!." Doc. Math (2012): 301-315.
520 .   * -   Gilbert, Jean Charles, and Claude Lemar{\'e}chal. "Some numerical experiments with variable-storage quasi-Newton algorithms."
521       Mathematical programming 45.1-3 (1989): 407-435.
522 -   * -   Dener A., Munson T. "Accelerating Limited-Memory Quasi-Newton Convergence for Large-Scale Optimization"
523       Computational Science - ICCS 2019. ICCS 2019. Lecture Notes in Computer Science, vol 11538. Springer, Cham
524 
525       Level: beginner
526 
527       Notes:
528       This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using
529       previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one
530       updates.
531 
532       When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied.  The first of
533       these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this
534       iteration as the current iteration's values when constructing the approximate Jacobian.  The second, composed,
535       perturbs the problem the Jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner.
536 
537       Uses left nonlinear preconditioning by default.
538 
539 .seealso: `SNESQNRestartType`, `SNESQNSetRestartType()`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESNEWTONTR`,
540           `SNESQNScaleType`, `SNESQNSetScaleType()`, `SNESQNSetType`, `SNESQNSetType()`
541 M*/
542 PETSC_EXTERN PetscErrorCode SNESCreate_QN(SNES snes)
543 {
544   SNES_QN    *qn;
545   const char *optionsprefix;
546 
547   PetscFunctionBegin;
548   snes->ops->setup          = SNESSetUp_QN;
549   snes->ops->solve          = SNESSolve_QN;
550   snes->ops->destroy        = SNESDestroy_QN;
551   snes->ops->setfromoptions = SNESSetFromOptions_QN;
552   snes->ops->view           = SNESView_QN;
553   snes->ops->reset          = SNESReset_QN;
554 
555   snes->npcside = PC_LEFT;
556 
557   snes->usesnpc = PETSC_TRUE;
558   snes->usesksp = PETSC_FALSE;
559 
560   snes->alwayscomputesfinalresidual = PETSC_TRUE;
561 
562   if (!snes->tolerancesset) {
563     snes->max_funcs = 30000;
564     snes->max_its   = 10000;
565   }
566 
567   PetscCall(PetscNew(&qn));
568   snes->data       = (void *)qn;
569   qn->m            = 10;
570   qn->scaling      = 1.0;
571   qn->monitor      = NULL;
572   qn->monflg       = PETSC_FALSE;
573   qn->powell_gamma = 0.9999;
574   qn->scale_type   = SNES_QN_SCALE_DEFAULT;
575   qn->restart_type = SNES_QN_RESTART_DEFAULT;
576   qn->type         = SNES_QN_LBFGS;
577 
578   PetscCall(MatCreate(PetscObjectComm((PetscObject)snes), &qn->B));
579   PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix));
580   PetscCall(MatSetOptionsPrefix(qn->B, optionsprefix));
581 
582   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetScaleType_C", SNESQNSetScaleType_QN));
583   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetRestartType_C", SNESQNSetRestartType_QN));
584   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetType_C", SNESQNSetType_QN));
585   PetscFunctionReturn(PETSC_SUCCESS);
586 }
587