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