Actual source code: linesearchbt.c

  1: #include <petsc/private/linesearchimpl.h>
  2: #include <petsc/private/snesimpl.h>

  4: typedef struct {
  5:   PetscReal alpha; /* sufficient decrease parameter */
  6: } SNESLineSearch_BT;

  8: /*@
  9:   SNESLineSearchBTSetAlpha - Sets the descent parameter, `alpha`, in the `SNESLINESEARCHBT` `SNESLineSearch` variant.

 11:   Input Parameters:
 12: + linesearch - linesearch context
 13: - alpha      - The descent parameter

 15:   Level: intermediate

 17: .seealso: [](ch_snes), `SNESLineSearch`, `SNESLineSearchSetLambda()`, `SNESLineSearchGetTolerances()`, `SNESLINESEARCHBT`, `SNESLineSearchBTGetAlpha()`
 18: @*/
 19: PetscErrorCode SNESLineSearchBTSetAlpha(SNESLineSearch linesearch, PetscReal alpha)
 20: {
 21:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
 22:   PetscBool          isbt;

 24:   PetscFunctionBegin;
 26:   PetscCall(PetscObjectTypeCompare((PetscObject)linesearch, SNESLINESEARCHBT, &isbt));
 27:   if (isbt) bt->alpha = alpha;
 28:   PetscFunctionReturn(PETSC_SUCCESS);
 29: }

 31: /*@
 32:   SNESLineSearchBTGetAlpha - Gets the descent parameter, `alpha`, in the `SNESLINESEARCHBT` variant that was set with `SNESLineSearchBTSetAlpha()`

 34:   Input Parameter:
 35: . linesearch - linesearch context

 37:   Output Parameter:
 38: . alpha - The descent parameter

 40:   Level: intermediate

 42: .seealso: [](ch_snes), `SNESLineSearch`, `SNESLineSearchGetLambda()`, `SNESLineSearchGetTolerances()`, `SNESLINESEARCHBT`, `SNESLineSearchBTSetAlpha()`
 43: @*/
 44: PetscErrorCode SNESLineSearchBTGetAlpha(SNESLineSearch linesearch, PetscReal *alpha)
 45: {
 46:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
 47:   PetscBool          isbt;

 49:   PetscFunctionBegin;
 51:   PetscCall(PetscObjectTypeCompare((PetscObject)linesearch, SNESLINESEARCHBT, &isbt));
 52:   PetscCheck(isbt, PetscObjectComm((PetscObject)linesearch), PETSC_ERR_USER, "Not for type %s", ((PetscObject)linesearch)->type_name);
 53:   *alpha = bt->alpha;
 54:   PetscFunctionReturn(PETSC_SUCCESS);
 55: }

 57: static PetscErrorCode SNESLineSearchApply_BT(SNESLineSearch linesearch)
 58: {
 59:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
 60:   PetscBool          changed_y, changed_w;
 61:   Vec                X, F, Y, W, G;
 62:   SNES               snes;
 63:   PetscReal          fnorm, xnorm, ynorm, gnorm;
 64:   PetscReal          lambda, lambdatemp, lambdaprev, minlambda, initslope, alpha, stol;
 65:   PetscReal          t1, t2, a, b, d;
 66:   PetscReal          f;
 67:   PetscReal          g, gprev;
 68:   PetscViewer        monitor;
 69:   PetscInt           max_it, count;
 70:   Mat                jac;
 71:   SNESObjectiveFn   *objective;
 72:   const char *const  ordStr[] = {"Linear", "Quadratic", "Cubic"};

 74:   PetscFunctionBegin;
 75:   PetscCall(SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, &G));
 76:   PetscCall(SNESLineSearchGetNorms(linesearch, NULL, &fnorm, NULL));
 77:   PetscCall(SNESLineSearchGetLambda(linesearch, &lambda));
 78:   PetscCall(SNESLineSearchGetSNES(linesearch, &snes));
 79:   PetscCall(SNESLineSearchGetDefaultMonitor(linesearch, &monitor));
 80:   PetscCall(SNESLineSearchGetTolerances(linesearch, &minlambda, NULL, NULL, NULL, NULL, &max_it));
 81:   PetscCall(SNESGetTolerances(snes, NULL, NULL, &stol, NULL, NULL));
 82:   PetscCall(SNESGetObjective(snes, &objective, NULL));
 83:   alpha = bt->alpha;

 85:   PetscCall(SNESGetJacobian(snes, &jac, NULL, NULL, NULL));
 86:   PetscCheck(jac || objective, PetscObjectComm((PetscObject)linesearch), PETSC_ERR_USER, "SNESLineSearchBT requires a Jacobian matrix");

 88:   PetscCall(SNESLineSearchPreCheck(linesearch, X, Y, &changed_y));
 89:   PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED));

 91:   PetscCall(VecNormBegin(Y, NORM_2, &ynorm));
 92:   PetscCall(VecNormBegin(X, NORM_2, &xnorm));
 93:   PetscCall(VecNormEnd(Y, NORM_2, &ynorm));
 94:   PetscCall(VecNormEnd(X, NORM_2, &xnorm));

 96:   if (ynorm == 0.0) {
 97:     if (monitor) {
 98:       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
 99:       PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Initial direction and size is 0\n"));
100:       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
101:     }
102:     PetscCall(VecCopy(X, W));
103:     PetscCall(VecCopy(F, G));
104:     PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, fnorm, ynorm));
105:     PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT));
106:     PetscFunctionReturn(PETSC_SUCCESS);
107:   }

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 = 0.5 * PetscSqr(fnorm);
114:   }

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:   }

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(PETSC_SUCCESS);
136:     }

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 = 0.5 * PetscSqr(gnorm);
149:     }
150:     PetscCall(SNESLineSearchMonitor(linesearch));

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;
160:   }

162:   if (!objective) PetscCall(PetscInfo(snes, "Initial fnorm %14.12e gnorm %14.12e\n", (double)fnorm, (double)gnorm));
163:   if (g <= 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: old obj %14.12e new obj %14.12e\n", (double)f, (double)g));
170:       }
171:       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
172:     }
173:   } else {
174:     if (stol * xnorm > ynorm) {
175:       /* Since the full step didn't give sufficient decrease and the step is tiny, exit */
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(PETSC_SUCCESS);
184:     }
185:     /* Here to avoid -Wmaybe-uninitiliazed warnings */
186:     lambdaprev = lambda;
187:     gprev      = g;
188:     if (linesearch->order != SNES_LINESEARCH_ORDER_LINEAR) {
189:       /* Fit points with quadratic */
190:       lambdatemp = -initslope * PetscSqr(lambda) / (2.0 * (g - f - lambda * initslope));
191:       lambda     = PetscClipInterval(lambdatemp, .1 * lambda, .5 * lambda);

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(PETSC_SUCCESS);
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 = 0.5 * 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(PETSC_SUCCESS);
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:     }
228:     if (linesearch->order != SNES_LINESEARCH_ORDER_LINEAR && g <= f + lambda * alpha * initslope) { /* sufficient reduction */
229:       if (monitor) {
230:         PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
231:         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratically determined step, lambda=%18.16e\n", (double)lambda));
232:         PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
233:       }
234:     } else {
235:       for (count = 0; count < max_it; 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(PETSC_SUCCESS);
249:         }
250:         if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
251:           /* Fit points with cubic */
252:           t1 = g - f - lambda * initslope;
253:           t2 = gprev - f - lambdaprev * initslope;
254:           a  = (t1 / (lambda * lambda) - t2 / (lambdaprev * lambdaprev)) / (lambda - lambdaprev);
255:           b  = (-lambdaprev * t1 / (lambda * lambda) + lambda * t2 / (lambdaprev * lambdaprev)) / (lambda - lambdaprev);
256:           d  = b * b - 3 * a * initslope;
257:           if (d < 0.0) d = 0.0;
258:           if (a == 0.0) lambdatemp = -initslope / (2.0 * b);
259:           else lambdatemp = (-b + PetscSqrtReal(d)) / (3.0 * a);
260:         } else if (linesearch->order == SNES_LINESEARCH_ORDER_QUADRATIC) {
261:           lambdatemp = -initslope * PetscSqr(lambda) / (2.0 * (g - f - lambda * initslope));
262:         } else if (linesearch->order == SNES_LINESEARCH_ORDER_LINEAR) { /* Just backtrack */
263:           lambdatemp = .5 * lambda;
264:         } else SETERRQ(PetscObjectComm((PetscObject)linesearch), PETSC_ERR_SUP, "Line search order %" PetscInt_FMT " for type bt", linesearch->order);
265:         lambdaprev = lambda;
266:         gprev      = g;

268:         lambda = PetscClipInterval(lambdatemp, .1 * lambda, .5 * lambda);
269:         PetscCall(VecWAXPY(W, -lambda, Y, X));
270:         if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
271:         if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
272:           PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations, while looking for good step length! %" PetscInt_FMT " \n", count));
273:           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));
274:           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION));
275:           snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
276:           PetscFunctionReturn(PETSC_SUCCESS);
277:         }
278:         if (objective) {
279:           PetscCall(SNESComputeObjective(snes, W, &g));
280:         } else {
281:           PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
282:           if (linesearch->ops->vinorm) {
283:             gnorm = fnorm;
284:             PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
285:           } else {
286:             PetscCall(VecNorm(G, NORM_2, &gnorm));
287:           }
288:           g = 0.5 * PetscSqr(gnorm);
289:         }
290:         if (PetscIsInfOrNanReal(g)) {
291:           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
292:           PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
293:           PetscFunctionReturn(PETSC_SUCCESS);
294:         }
295:         if (g <= f + lambda * alpha * initslope) { /* is reduction enough? */
296:           if (monitor) {
297:             PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
298:             if (!objective) {
299:               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: %s step, current gnorm %14.12e lambda=%18.16e\n", ordStr[linesearch->order - 1], (double)gnorm, (double)lambda));
300:               PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
301:             } else {
302:               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: %s step, obj %14.12e lambda=%18.16e\n", ordStr[linesearch->order - 1], (double)g, (double)lambda));
303:               PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
304:             }
305:           }
306:           break;
307:         } else if (monitor) {
308:           PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
309:           if (!objective) {
310:             PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: %s step no good, shrinking lambda, current gnorm %12.12e lambda=%18.16e\n", ordStr[linesearch->order - 1], (double)gnorm, (double)lambda));
311:             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
312:           } else {
313:             PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: %s step no good, shrinking lambda, obj %12.12e lambda=%18.16e\n", ordStr[linesearch->order - 1], (double)g, (double)lambda));
314:             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
315:           }
316:         }
317:       }
318:     }
319:   }

321:   /* postcheck */
322:   PetscCall(SNESLineSearchSetLambda(linesearch, lambda));
323:   PetscCall(SNESLineSearchPostCheck(linesearch, X, Y, W, &changed_y, &changed_w));
324:   if (changed_y) {
325:     if (!changed_w) PetscCall(VecWAXPY(W, -lambda, Y, X));
326:     if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
327:   }
328:   if (changed_y || changed_w || objective) { /* recompute the function norm if the step has changed or the objective isn't the norm */
329:     PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
330:     if (linesearch->ops->vinorm) {
331:       gnorm = fnorm;
332:       PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
333:     } else {
334:       PetscCall(VecNorm(G, NORM_2, &gnorm));
335:     }
336:     PetscCall(VecNorm(Y, NORM_2, &ynorm));
337:     if (PetscIsInfOrNanReal(gnorm)) {
338:       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
339:       PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
340:       PetscFunctionReturn(PETSC_SUCCESS);
341:     }
342:   }

344:   /* copy the solution over */
345:   PetscCall(VecCopy(W, X));
346:   PetscCall(VecCopy(G, F));
347:   PetscCall(VecNorm(X, NORM_2, &xnorm));
348:   PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, gnorm, ynorm));
349:   PetscFunctionReturn(PETSC_SUCCESS);
350: }

352: static PetscErrorCode SNESLineSearchView_BT(SNESLineSearch linesearch, PetscViewer viewer)
353: {
354:   PetscBool          isascii;
355:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;

357:   PetscFunctionBegin;
358:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
359:   if (isascii) {
360:     if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
361:       PetscCall(PetscViewerASCIIPrintf(viewer, "  interpolation: cubic\n"));
362:     } else if (linesearch->order == SNES_LINESEARCH_ORDER_QUADRATIC) {
363:       PetscCall(PetscViewerASCIIPrintf(viewer, "  interpolation: quadratic\n"));
364:     }
365:     PetscCall(PetscViewerASCIIPrintf(viewer, "  alpha=%e\n", (double)bt->alpha));
366:   }
367:   PetscFunctionReturn(PETSC_SUCCESS);
368: }

370: static PetscErrorCode SNESLineSearchDestroy_BT(SNESLineSearch linesearch)
371: {
372:   PetscFunctionBegin;
373:   PetscCall(PetscFree(linesearch->data));
374:   PetscFunctionReturn(PETSC_SUCCESS);
375: }

377: static PetscErrorCode SNESLineSearchSetFromOptions_BT(SNESLineSearch linesearch, PetscOptionItems PetscOptionsObject)
378: {
379:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;

381:   PetscFunctionBegin;
382:   PetscOptionsHeadBegin(PetscOptionsObject, "SNESLineSearch BT options");
383:   PetscCall(PetscOptionsReal("-snes_linesearch_alpha", "Descent tolerance", "SNESLineSearchBT", bt->alpha, &bt->alpha, NULL));
384:   PetscOptionsHeadEnd();
385:   PetscFunctionReturn(PETSC_SUCCESS);
386: }

388: /*MC
389:    SNESLINESEARCHBT - Backtracking line search {cite}`dennis:83`.

391:    This line search finds the minimum of a polynomial fitting either $1/2 ||F(x_k + \lambda Y_k)||_2^2$,
392:    or the objective function $G(x_k + \lambda Y_k)$ if it is provided with `SNESSetObjective()`.
393:    If this fit does not satisfy the sufficient decrease conditions, the interval shrinks
394:    and the fit is reattempted at most `max_it` times or until $\lambda$ is below `minlambda`.

396:    Options Database Keys:
397: +  -snes_linesearch_alpha <1e\-4>      - slope descent parameter
398: .  -snes_linesearch_damping <1.0>      - initial `lambda` on entry to the line search
399: .  -snes_linesearch_max_it <40>        - maximum number of shrinking iterations in the line search
400: .  -snes_linesearch_minlambda <1e\-12> - minimum `lambda` (scaling of solution update) allowed
401: -  -snes_linesearch_order <3>          - order of the polynomial fit, must be 1, 2, or 3. With order 1, it performs a simple backtracking without any curve fitting

403:    Level: advanced

405:    Note:
406:    This line search will always produce a step that is less than or equal to, in length, the full step size.

408: .seealso: [](ch_snes), `SNESLineSearch`, `SNESLineSearchType`, `SNESLineSearchCreate()`, `SNESLineSearchSetType()`
409: M*/
410: PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_BT(SNESLineSearch linesearch)
411: {
412:   SNESLineSearch_BT *bt;

414:   PetscFunctionBegin;
415:   linesearch->ops->apply          = SNESLineSearchApply_BT;
416:   linesearch->ops->destroy        = SNESLineSearchDestroy_BT;
417:   linesearch->ops->setfromoptions = SNESLineSearchSetFromOptions_BT;
418:   linesearch->ops->reset          = NULL;
419:   linesearch->ops->view           = SNESLineSearchView_BT;
420:   linesearch->ops->setup          = NULL;

422:   PetscCall(PetscNew(&bt));

424:   linesearch->data   = (void *)bt;
425:   linesearch->max_it = 40;
426:   linesearch->order  = SNES_LINESEARCH_ORDER_CUBIC;
427:   bt->alpha          = 1e-4;
428:   PetscFunctionReturn(PETSC_SUCCESS);
429: }