xref: /petsc/src/snes/linesearch/impls/bt/linesearchbt.c (revision d2522c19e8fa9bca20aaca277941d9a63e71db6a)
1 #include <petsc/private/linesearchimpl.h> /*I  "petscsnes.h"  I*/
2 #include <petsc/private/snesimpl.h>
3 
4 typedef struct {
5   PetscReal alpha; /* sufficient decrease parameter */
6 } SNESLineSearch_BT;
7 
8 /*@
9    SNESLineSearchBTSetAlpha - Sets the descent parameter, alpha, in the BT linesearch variant.
10 
11    Input Parameters:
12 +  linesearch - linesearch context
13 -  alpha - The descent parameter
14 
15    Level: intermediate
16 
17 .seealso: `SNESLineSearchSetLambda()`, `SNESLineSearchGetTolerances()` `SNESLINESEARCHBT`
18 @*/
19 PetscErrorCode SNESLineSearchBTSetAlpha(SNESLineSearch linesearch, PetscReal alpha) {
20   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
21 
22   PetscFunctionBegin;
23   PetscValidHeaderSpecific(linesearch, SNESLINESEARCH_CLASSID, 1);
24   bt->alpha = alpha;
25   PetscFunctionReturn(0);
26 }
27 
28 /*@
29    SNESLineSearchBTGetAlpha - Gets the descent parameter, alpha, in the BT linesearch variant.
30 
31    Input Parameters:
32 .  linesearch - linesearch context
33 
34    Output Parameters:
35 .  alpha - The descent parameter
36 
37    Level: intermediate
38 
39 .seealso: `SNESLineSearchGetLambda()`, `SNESLineSearchGetTolerances()` `SNESLINESEARCHBT`
40 @*/
41 PetscErrorCode SNESLineSearchBTGetAlpha(SNESLineSearch linesearch, PetscReal *alpha) {
42   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
43 
44   PetscFunctionBegin;
45   PetscValidHeaderSpecific(linesearch, SNESLINESEARCH_CLASSID, 1);
46   *alpha = bt->alpha;
47   PetscFunctionReturn(0);
48 }
49 
50 static PetscErrorCode SNESLineSearchApply_BT(SNESLineSearch linesearch) {
51   PetscBool          changed_y, changed_w;
52   Vec                X, F, Y, W, G;
53   SNES               snes;
54   PetscReal          fnorm, xnorm, ynorm, gnorm;
55   PetscReal          lambda, lambdatemp, lambdaprev, minlambda, maxstep, initslope, alpha, stol;
56   PetscReal          t1, t2, a, b, d;
57   PetscReal          f;
58   PetscReal          g, gprev;
59   PetscViewer        monitor;
60   PetscInt           max_its, count;
61   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
62   Mat                jac;
63   PetscErrorCode (*objective)(SNES, Vec, PetscReal *, void *);
64 
65   PetscFunctionBegin;
66   PetscCall(SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, &G));
67   PetscCall(SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm));
68   PetscCall(SNESLineSearchGetLambda(linesearch, &lambda));
69   PetscCall(SNESLineSearchGetSNES(linesearch, &snes));
70   PetscCall(SNESLineSearchGetDefaultMonitor(linesearch, &monitor));
71   PetscCall(SNESLineSearchGetTolerances(linesearch, &minlambda, &maxstep, NULL, NULL, NULL, &max_its));
72   PetscCall(SNESGetTolerances(snes, NULL, NULL, &stol, NULL, NULL));
73   PetscCall(SNESGetObjective(snes, &objective, NULL));
74   alpha = bt->alpha;
75 
76   PetscCall(SNESGetJacobian(snes, &jac, NULL, NULL, NULL));
77   PetscCheck(jac || objective, PetscObjectComm((PetscObject)linesearch), PETSC_ERR_USER, "SNESLineSearchBT requires a Jacobian matrix");
78 
79   PetscCall(SNESLineSearchPreCheck(linesearch, X, Y, &changed_y));
80   PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED));
81 
82   PetscCall(VecNormBegin(Y, NORM_2, &ynorm));
83   PetscCall(VecNormBegin(X, NORM_2, &xnorm));
84   PetscCall(VecNormEnd(Y, NORM_2, &ynorm));
85   PetscCall(VecNormEnd(X, NORM_2, &xnorm));
86 
87   if (ynorm == 0.0) {
88     if (monitor) {
89       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
90       PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Initial direction and size is 0\n"));
91       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
92     }
93     PetscCall(VecCopy(X, W));
94     PetscCall(VecCopy(F, G));
95     PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, fnorm, ynorm));
96     PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT));
97     PetscFunctionReturn(0);
98   }
99   if (ynorm > maxstep) { /* Step too big, so scale back */
100     if (monitor) {
101       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
102       PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Scaling step by %14.12e old ynorm %14.12e\n", (double)(maxstep / ynorm), (double)ynorm));
103       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
104     }
105     PetscCall(VecScale(Y, maxstep / (ynorm)));
106     ynorm = maxstep;
107   }
108 
109   /* if the SNES has an objective set, use that instead of the function value */
110   if (objective) {
111     PetscCall(SNESComputeObjective(snes, X, &f));
112   } else {
113     f = fnorm * fnorm;
114   }
115 
116   /* compute the initial slope */
117   if (objective) {
118     /* slope comes from the function (assumed to be the gradient of the objective */
119     PetscCall(VecDotRealPart(Y, F, &initslope));
120   } else {
121     /* slope comes from the normal equations */
122     PetscCall(MatMult(jac, Y, W));
123     PetscCall(VecDotRealPart(F, W, &initslope));
124     if (initslope > 0.0) initslope = -initslope;
125     if (initslope == 0.0) initslope = -1.0;
126   }
127 
128   while (PETSC_TRUE) {
129     PetscCall(VecWAXPY(W, -lambda, Y, X));
130     if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
131     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
132       PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations, while checking full step length!\n"));
133       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
134       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION));
135       PetscFunctionReturn(0);
136     }
137 
138     if (objective) {
139       PetscCall(SNESComputeObjective(snes, W, &g));
140     } else {
141       PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
142       if (linesearch->ops->vinorm) {
143         gnorm = fnorm;
144         PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
145       } else {
146         PetscCall(VecNorm(G, NORM_2, &gnorm));
147       }
148       g = PetscSqr(gnorm);
149     }
150     PetscCall(SNESLineSearchMonitor(linesearch));
151 
152     if (!PetscIsInfOrNanReal(g)) break;
153     if (monitor) {
154       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
155       PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: objective function at lambdas = %g is Inf or Nan, cutting lambda\n", (double)lambda));
156       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
157     }
158     if (lambda <= minlambda) { SNESCheckFunctionNorm(snes, g); }
159     lambda = .5 * lambda;
160   }
161 
162   if (!objective) { PetscCall(PetscInfo(snes, "Initial fnorm %14.12e gnorm %14.12e\n", (double)fnorm, (double)gnorm)); }
163   if (.5 * g <= .5 * f + lambda * alpha * initslope) { /* Sufficient reduction or step tolerance convergence */
164     if (monitor) {
165       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
166       if (!objective) {
167         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Using full step: fnorm %14.12e gnorm %14.12e\n", (double)fnorm, (double)gnorm));
168       } else {
169         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Using full step: obj %14.12e obj %14.12e\n", (double)f, (double)g));
170       }
171       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
172     }
173   } else {
174     /* Since the full step didn't work and the step is tiny, quit */
175     if (stol * xnorm > ynorm) {
176       PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, fnorm, ynorm));
177       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED));
178       if (monitor) {
179         PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
180         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Ended due to ynorm < stol*xnorm (%14.12e < %14.12e).\n", (double)ynorm, (double)(stol * xnorm)));
181         PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
182       }
183       PetscFunctionReturn(0);
184     }
185     /* Fit points with quadratic */
186     lambdatemp = -initslope / (g - f - 2.0 * lambda * initslope);
187     lambdaprev = lambda;
188     gprev      = g;
189     if (lambdatemp > .5 * lambda) lambdatemp = .5 * lambda;
190     if (lambdatemp <= .1 * lambda) lambda = .1 * lambda;
191     else lambda = lambdatemp;
192 
193     PetscCall(VecWAXPY(W, -lambda, Y, X));
194     if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
195     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
196       PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations, while attempting quadratic backtracking! %" PetscInt_FMT " \n", snes->nfuncs));
197       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
198       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION));
199       PetscFunctionReturn(0);
200     }
201     if (objective) {
202       PetscCall(SNESComputeObjective(snes, W, &g));
203     } else {
204       PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
205       if (linesearch->ops->vinorm) {
206         gnorm = fnorm;
207         PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
208       } else {
209         PetscCall(VecNorm(G, NORM_2, &gnorm));
210       }
211       g = PetscSqr(gnorm);
212     }
213     if (PetscIsInfOrNanReal(g)) {
214       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
215       PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
216       PetscFunctionReturn(0);
217     }
218     if (monitor) {
219       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
220       if (!objective) {
221         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: gnorm after quadratic fit %14.12e\n", (double)gnorm));
222       } else {
223         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: obj after quadratic fit %14.12e\n", (double)g));
224       }
225       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
226     }
227     if (.5 * g < .5 * f + lambda * alpha * initslope) { /* sufficient reduction */
228       if (monitor) {
229         PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
230         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratically determined step, lambda=%18.16e\n", (double)lambda));
231         PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
232       }
233     } else {
234       /* Fit points with cubic */
235       for (count = 0; count < max_its; count++) {
236         if (lambda <= minlambda) {
237           if (monitor) {
238             PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
239             PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: unable to find good step length! After %" PetscInt_FMT " tries \n", count));
240             if (!objective) {
241               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, minlambda=%18.16e, lambda=%18.16e, initial slope=%18.16e\n", (double)fnorm, (double)gnorm, (double)ynorm, (double)minlambda, (double)lambda, (double)initslope));
242             } else {
243               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: obj(0)=%18.16e, obj=%18.16e, ynorm=%18.16e, minlambda=%18.16e, lambda=%18.16e, initial slope=%18.16e\n", (double)f, (double)g, (double)ynorm, (double)minlambda, (double)lambda, (double)initslope));
244             }
245             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
246           }
247           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT));
248           PetscFunctionReturn(0);
249         }
250         if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
251           t1 = .5 * (g - f) - lambda * initslope;
252           t2 = .5 * (gprev - f) - lambdaprev * initslope;
253           a  = (t1 / (lambda * lambda) - t2 / (lambdaprev * lambdaprev)) / (lambda - lambdaprev);
254           b  = (-lambdaprev * t1 / (lambda * lambda) + lambda * t2 / (lambdaprev * lambdaprev)) / (lambda - lambdaprev);
255           d  = b * b - 3 * a * initslope;
256           if (d < 0.0) d = 0.0;
257           if (a == 0.0) lambdatemp = -initslope / (2.0 * b);
258           else lambdatemp = (-b + PetscSqrtReal(d)) / (3.0 * a);
259 
260         } else if (linesearch->order == SNES_LINESEARCH_ORDER_QUADRATIC) {
261           lambdatemp = -initslope / (g - f - 2.0 * initslope);
262         } else SETERRQ(PetscObjectComm((PetscObject)linesearch), PETSC_ERR_SUP, "unsupported line search order for type bt");
263         lambdaprev = lambda;
264         gprev      = g;
265         if (lambdatemp > .5 * lambda) lambdatemp = .5 * lambda;
266         if (lambdatemp <= .1 * lambda) lambda = .1 * lambda;
267         else lambda = lambdatemp;
268         PetscCall(VecWAXPY(W, -lambda, Y, X));
269         if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
270         if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
271           PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations, while looking for good step length! %" PetscInt_FMT " \n", count));
272           if (!objective) { PetscCall(PetscInfo(snes, "fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n", (double)fnorm, (double)gnorm, (double)ynorm, (double)lambda, (double)initslope)); }
273           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION));
274           snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
275           PetscFunctionReturn(0);
276         }
277         if (objective) {
278           PetscCall(SNESComputeObjective(snes, W, &g));
279         } else {
280           PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
281           if (linesearch->ops->vinorm) {
282             gnorm = fnorm;
283             PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
284           } else {
285             PetscCall(VecNorm(G, NORM_2, &gnorm));
286           }
287           g = PetscSqr(gnorm);
288         }
289         if (PetscIsInfOrNanReal(g)) {
290           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
291           PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
292           PetscFunctionReturn(0);
293         }
294         if (.5 * g < .5 * f + lambda * alpha * initslope) { /* is reduction enough? */
295           if (monitor) {
296             PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
297             if (!objective) {
298               if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
299                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubically determined step, current gnorm %14.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
300               } else {
301                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratically determined step, current gnorm %14.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
302               }
303               PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
304             } else {
305               if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
306                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubically determined step, obj %14.12e lambda=%18.16e\n", (double)g, (double)lambda));
307               } else {
308                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratically determined step, obj %14.12e lambda=%18.16e\n", (double)g, (double)lambda));
309               }
310               PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
311             }
312           }
313           break;
314         } else if (monitor) {
315           PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
316           if (!objective) {
317             if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
318               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubic step no good, shrinking lambda, current gnorm %12.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
319             } else {
320               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratic step no good, shrinking lambda, current gnorm %12.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
321             }
322             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
323           } else {
324             if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
325               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubic step no good, shrinking lambda, obj %12.12e lambda=%18.16e\n", (double)g, (double)lambda));
326             } else {
327               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratic step no good, shrinking lambda, obj %12.12e lambda=%18.16e\n", (double)g, (double)lambda));
328             }
329             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
330           }
331         }
332       }
333     }
334   }
335 
336   /* postcheck */
337   /* update Y to lambda*Y so that W is consistent with  X - lambda*Y */
338   PetscCall(VecScale(Y, lambda));
339   PetscCall(SNESLineSearchPostCheck(linesearch, X, Y, W, &changed_y, &changed_w));
340   if (changed_y) {
341     PetscCall(VecWAXPY(W, -1.0, Y, X));
342     if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
343   }
344   if (changed_y || changed_w || objective) { /* recompute the function norm if the step has changed or the objective isn't the norm */
345     PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
346     if (linesearch->ops->vinorm) {
347       gnorm = fnorm;
348       PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
349     } else {
350       PetscCall(VecNorm(G, NORM_2, &gnorm));
351     }
352     PetscCall(VecNorm(Y, NORM_2, &ynorm));
353     if (PetscIsInfOrNanReal(gnorm)) {
354       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
355       PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
356       PetscFunctionReturn(0);
357     }
358   }
359 
360   /* copy the solution over */
361   PetscCall(VecCopy(W, X));
362   PetscCall(VecCopy(G, F));
363   PetscCall(VecNorm(X, NORM_2, &xnorm));
364   PetscCall(SNESLineSearchSetLambda(linesearch, lambda));
365   PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, gnorm, ynorm));
366   PetscFunctionReturn(0);
367 }
368 
369 PetscErrorCode SNESLineSearchView_BT(SNESLineSearch linesearch, PetscViewer viewer) {
370   PetscBool          iascii;
371   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
372 
373   PetscFunctionBegin;
374   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
375   if (iascii) {
376     if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
377       PetscCall(PetscViewerASCIIPrintf(viewer, "  interpolation: cubic\n"));
378     } else if (linesearch->order == SNES_LINESEARCH_ORDER_QUADRATIC) {
379       PetscCall(PetscViewerASCIIPrintf(viewer, "  interpolation: quadratic\n"));
380     }
381     PetscCall(PetscViewerASCIIPrintf(viewer, "  alpha=%e\n", (double)bt->alpha));
382   }
383   PetscFunctionReturn(0);
384 }
385 
386 static PetscErrorCode SNESLineSearchDestroy_BT(SNESLineSearch linesearch) {
387   PetscFunctionBegin;
388   PetscCall(PetscFree(linesearch->data));
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode SNESLineSearchSetFromOptions_BT(SNESLineSearch linesearch, PetscOptionItems *PetscOptionsObject) {
393   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
394 
395   PetscFunctionBegin;
396   PetscOptionsHeadBegin(PetscOptionsObject, "SNESLineSearch BT options");
397   PetscCall(PetscOptionsReal("-snes_linesearch_alpha", "Descent tolerance", "SNESLineSearchBT", bt->alpha, &bt->alpha, NULL));
398   PetscOptionsHeadEnd();
399   PetscFunctionReturn(0);
400 }
401 
402 /*MC
403    SNESLINESEARCHBT - Backtracking line search.
404 
405    This line search finds the minimum of a polynomial fitting of the L2 norm of the
406    function or the objective function if it is provided with SNESSetObjective(). If this fit does not satisfy the conditions for progress, the interval shrinks
407    and the fit is reattempted at most max_it times or until lambda is below minlambda.
408 
409    Options Database Keys:
410 +  -snes_linesearch_alpha <1e\-4> - slope descent parameter
411 .  -snes_linesearch_damping <1.0> - initial step length
412 .  -snes_linesearch_maxstep <length> - if the length the initial step is larger than this then the
413                                        step is scaled back to be of this length at the beginning of the line search
414 .  -snes_linesearch_max_it <40> - maximum number of shrinking step
415 .  -snes_linesearch_minlambda <1e\-12> - minimum step length allowed
416 -  -snes_linesearch_order <cubic,quadratic> - order of the approximation
417 
418    Level: advanced
419 
420    Notes:
421    This line search is taken from "Numerical Methods for Unconstrained
422    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
423 
424    This line search will always produce a step that is less than or equal to, in length, the full step size.
425 
426 .seealso: `SNESLineSearchCreate()`, `SNESLineSearchSetType()`
427 M*/
428 PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_BT(SNESLineSearch linesearch) {
429   SNESLineSearch_BT *bt;
430 
431   PetscFunctionBegin;
432   linesearch->ops->apply          = SNESLineSearchApply_BT;
433   linesearch->ops->destroy        = SNESLineSearchDestroy_BT;
434   linesearch->ops->setfromoptions = SNESLineSearchSetFromOptions_BT;
435   linesearch->ops->reset          = NULL;
436   linesearch->ops->view           = SNESLineSearchView_BT;
437   linesearch->ops->setup          = NULL;
438 
439   PetscCall(PetscNewLog(linesearch, &bt));
440 
441   linesearch->data    = (void *)bt;
442   linesearch->max_its = 40;
443   linesearch->order   = SNES_LINESEARCH_ORDER_CUBIC;
444   bt->alpha           = 1e-4;
445   PetscFunctionReturn(0);
446 }
447