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