Date: 2026-03-01T05:25:06+00:00

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