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` variant.

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

 15:    Level: intermediate

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

 23:   PetscFunctionBegin;
 25:   bt->alpha = alpha;
 26:   PetscFunctionReturn(PETSC_SUCCESS);
 27: }

 29: /*@
 30:    SNESLineSearchBTGetAlpha - Gets the descent parameter, alpha, in the `SNESLINESEARCHBT` variant.

 32:    Input Parameter:
 33: .  linesearch - linesearch context

 35:    Output Parameter:
 36: .  alpha - The descent parameter

 38:    Level: intermediate

 40: .seealso: `SNESLineSearchGetLambda()`, `SNESLineSearchGetTolerances()` `SNESLINESEARCHBT`, `SNESLineSearchBTGetAlpha()`
 41: @*/
 42: PetscErrorCode SNESLineSearchBTGetAlpha(SNESLineSearch linesearch, PetscReal *alpha)
 43: {
 44:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;

 46:   PetscFunctionBegin;
 48:   *alpha = bt->alpha;
 49:   PetscFunctionReturn(PETSC_SUCCESS);
 50: }

 52: static PetscErrorCode SNESLineSearchApply_BT(SNESLineSearch linesearch)
 53: {
 54:   PetscBool          changed_y, changed_w;
 55:   Vec                X, F, Y, W, G;
 56:   SNES               snes;
 57:   PetscReal          fnorm, xnorm, ynorm, gnorm;
 58:   PetscReal          lambda, lambdatemp, lambdaprev, minlambda, maxstep, initslope, alpha, stol;
 59:   PetscReal          t1, t2, a, b, d;
 60:   PetscReal          f;
 61:   PetscReal          g, gprev;
 62:   PetscViewer        monitor;
 63:   PetscInt           max_its, count;
 64:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;
 65:   Mat                jac;
 66:   PetscErrorCode (*objective)(SNES, Vec, PetscReal *, void *);

 68:   PetscFunctionBegin;
 69:   PetscCall(SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, &G));
 70:   PetscCall(SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm));
 71:   PetscCall(SNESLineSearchGetLambda(linesearch, &lambda));
 72:   PetscCall(SNESLineSearchGetSNES(linesearch, &snes));
 73:   PetscCall(SNESLineSearchGetDefaultMonitor(linesearch, &monitor));
 74:   PetscCall(SNESLineSearchGetTolerances(linesearch, &minlambda, &maxstep, NULL, NULL, NULL, &max_its));
 75:   PetscCall(SNESGetTolerances(snes, NULL, NULL, &stol, NULL, NULL));
 76:   PetscCall(SNESGetObjective(snes, &objective, NULL));
 77:   alpha = bt->alpha;

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

 82:   PetscCall(SNESLineSearchPreCheck(linesearch, X, Y, &changed_y));
 83:   PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED));

 85:   PetscCall(VecNormBegin(Y, NORM_2, &ynorm));
 86:   PetscCall(VecNormBegin(X, NORM_2, &xnorm));
 87:   PetscCall(VecNormEnd(Y, NORM_2, &ynorm));
 88:   PetscCall(VecNormEnd(X, NORM_2, &xnorm));

 90:   if (ynorm == 0.0) {
 91:     if (monitor) {
 92:       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
 93:       PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Initial direction and size is 0\n"));
 94:       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
 95:     }
 96:     PetscCall(VecCopy(X, W));
 97:     PetscCall(VecCopy(F, G));
 98:     PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, fnorm, ynorm));
 99:     PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT));
100:     PetscFunctionReturn(PETSC_SUCCESS);
101:   }
102:   if (ynorm > maxstep) { /* Step too big, so scale back */
103:     if (monitor) {
104:       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
105:       PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Scaling step by %14.12e old ynorm %14.12e\n", (double)(maxstep / ynorm), (double)ynorm));
106:       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
107:     }
108:     PetscCall(VecScale(Y, maxstep / (ynorm)));
109:     ynorm = maxstep;
110:   }

112:   /* if the SNES has an objective set, use that instead of the function value */
113:   if (objective) {
114:     PetscCall(SNESComputeObjective(snes, X, &f));
115:   } else {
116:     f = fnorm * fnorm;
117:   }

119:   /* compute the initial slope */
120:   if (objective) {
121:     /* slope comes from the function (assumed to be the gradient of the objective */
122:     PetscCall(VecDotRealPart(Y, F, &initslope));
123:   } else {
124:     /* slope comes from the normal equations */
125:     PetscCall(MatMult(jac, Y, W));
126:     PetscCall(VecDotRealPart(F, W, &initslope));
127:     if (initslope > 0.0) initslope = -initslope;
128:     if (initslope == 0.0) initslope = -1.0;
129:   }

131:   while (PETSC_TRUE) {
132:     PetscCall(VecWAXPY(W, -lambda, Y, X));
133:     if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
134:     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
135:       PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations, while checking full step length!\n"));
136:       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
137:       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION));
138:       PetscFunctionReturn(PETSC_SUCCESS);
139:     }

141:     if (objective) {
142:       PetscCall(SNESComputeObjective(snes, W, &g));
143:     } else {
144:       PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
145:       if (linesearch->ops->vinorm) {
146:         gnorm = fnorm;
147:         PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
148:       } else {
149:         PetscCall(VecNorm(G, NORM_2, &gnorm));
150:       }
151:       g = PetscSqr(gnorm);
152:     }
153:     PetscCall(SNESLineSearchMonitor(linesearch));

155:     if (!PetscIsInfOrNanReal(g)) break;
156:     if (monitor) {
157:       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
158:       PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: objective function at lambdas = %g is Inf or Nan, cutting lambda\n", (double)lambda));
159:       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
160:     }
161:     if (lambda <= minlambda) SNESCheckFunctionNorm(snes, g);
162:     lambda = .5 * lambda;
163:   }

165:   if (!objective) PetscCall(PetscInfo(snes, "Initial fnorm %14.12e gnorm %14.12e\n", (double)fnorm, (double)gnorm));
166:   if (.5 * g <= .5 * f + lambda * alpha * initslope) { /* Sufficient reduction or step tolerance convergence */
167:     if (monitor) {
168:       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
169:       if (!objective) {
170:         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Using full step: fnorm %14.12e gnorm %14.12e\n", (double)fnorm, (double)gnorm));
171:       } else {
172:         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Using full step: obj %14.12e obj %14.12e\n", (double)f, (double)g));
173:       }
174:       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
175:     }
176:   } else {
177:     /* Since the full step didn't work and the step is tiny, quit */
178:     if (stol * xnorm > ynorm) {
179:       PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, fnorm, ynorm));
180:       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED));
181:       if (monitor) {
182:         PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
183:         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Ended due to ynorm < stol*xnorm (%14.12e < %14.12e).\n", (double)ynorm, (double)(stol * xnorm)));
184:         PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
185:       }
186:       PetscFunctionReturn(PETSC_SUCCESS);
187:     }
188:     /* Fit points with quadratic */
189:     lambdatemp = -initslope / (g - f - 2.0 * lambda * initslope);
190:     lambdaprev = lambda;
191:     gprev      = g;
192:     if (lambdatemp > .5 * lambda) lambdatemp = .5 * lambda;
193:     if (lambdatemp <= .1 * lambda) lambda = .1 * lambda;
194:     else lambda = lambdatemp;

196:     PetscCall(VecWAXPY(W, -lambda, Y, X));
197:     if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
198:     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
199:       PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations, while attempting quadratic backtracking! %" PetscInt_FMT " \n", snes->nfuncs));
200:       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
201:       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION));
202:       PetscFunctionReturn(PETSC_SUCCESS);
203:     }
204:     if (objective) {
205:       PetscCall(SNESComputeObjective(snes, W, &g));
206:     } else {
207:       PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
208:       if (linesearch->ops->vinorm) {
209:         gnorm = fnorm;
210:         PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
211:       } else {
212:         PetscCall(VecNorm(G, NORM_2, &gnorm));
213:       }
214:       g = PetscSqr(gnorm);
215:     }
216:     if (PetscIsInfOrNanReal(g)) {
217:       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
218:       PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
219:       PetscFunctionReturn(PETSC_SUCCESS);
220:     }
221:     if (monitor) {
222:       PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
223:       if (!objective) {
224:         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: gnorm after quadratic fit %14.12e\n", (double)gnorm));
225:       } else {
226:         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: obj after quadratic fit %14.12e\n", (double)g));
227:       }
228:       PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
229:     }
230:     if (.5 * g < .5 * f + lambda * alpha * initslope) { /* sufficient reduction */
231:       if (monitor) {
232:         PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
233:         PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratically determined step, lambda=%18.16e\n", (double)lambda));
234:         PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
235:       }
236:     } else {
237:       /* Fit points with cubic */
238:       for (count = 0; count < max_its; count++) {
239:         if (lambda <= minlambda) {
240:           if (monitor) {
241:             PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
242:             PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: unable to find good step length! After %" PetscInt_FMT " tries \n", count));
243:             if (!objective) {
244:               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));
245:             } else {
246:               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));
247:             }
248:             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
249:           }
250:           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT));
251:           PetscFunctionReturn(PETSC_SUCCESS);
252:         }
253:         if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
254:           t1 = .5 * (g - f) - lambda * initslope;
255:           t2 = .5 * (gprev - f) - lambdaprev * initslope;
256:           a  = (t1 / (lambda * lambda) - t2 / (lambdaprev * lambdaprev)) / (lambda - lambdaprev);
257:           b  = (-lambdaprev * t1 / (lambda * lambda) + lambda * t2 / (lambdaprev * lambdaprev)) / (lambda - lambdaprev);
258:           d  = b * b - 3 * a * initslope;
259:           if (d < 0.0) d = 0.0;
260:           if (a == 0.0) lambdatemp = -initslope / (2.0 * b);
261:           else lambdatemp = (-b + PetscSqrtReal(d)) / (3.0 * a);

263:         } else if (linesearch->order == SNES_LINESEARCH_ORDER_QUADRATIC) {
264:           lambdatemp = -initslope / (g - f - 2.0 * initslope);
265:         } else SETERRQ(PetscObjectComm((PetscObject)linesearch), PETSC_ERR_SUP, "unsupported line search order for type bt");
266:         lambdaprev = lambda;
267:         gprev      = g;
268:         if (lambdatemp > .5 * lambda) lambdatemp = .5 * lambda;
269:         if (lambdatemp <= .1 * lambda) lambda = .1 * lambda;
270:         else lambda = lambdatemp;
271:         PetscCall(VecWAXPY(W, -lambda, Y, X));
272:         if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
273:         if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
274:           PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations, while looking for good step length! %" PetscInt_FMT " \n", count));
275:           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));
276:           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION));
277:           snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
278:           PetscFunctionReturn(PETSC_SUCCESS);
279:         }
280:         if (objective) {
281:           PetscCall(SNESComputeObjective(snes, W, &g));
282:         } else {
283:           PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
284:           if (linesearch->ops->vinorm) {
285:             gnorm = fnorm;
286:             PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
287:           } else {
288:             PetscCall(VecNorm(G, NORM_2, &gnorm));
289:           }
290:           g = PetscSqr(gnorm);
291:         }
292:         if (PetscIsInfOrNanReal(g)) {
293:           PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
294:           PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
295:           PetscFunctionReturn(PETSC_SUCCESS);
296:         }
297:         if (.5 * g < .5 * f + lambda * alpha * initslope) { /* is reduction enough? */
298:           if (monitor) {
299:             PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
300:             if (!objective) {
301:               if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
302:                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubically determined step, current gnorm %14.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
303:               } else {
304:                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratically determined step, current gnorm %14.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
305:               }
306:               PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
307:             } else {
308:               if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
309:                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubically determined step, obj %14.12e lambda=%18.16e\n", (double)g, (double)lambda));
310:               } else {
311:                 PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratically determined step, obj %14.12e lambda=%18.16e\n", (double)g, (double)lambda));
312:               }
313:               PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
314:             }
315:           }
316:           break;
317:         } else if (monitor) {
318:           PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel));
319:           if (!objective) {
320:             if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
321:               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubic step no good, shrinking lambda, current gnorm %12.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
322:             } else {
323:               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratic step no good, shrinking lambda, current gnorm %12.12e lambda=%18.16e\n", (double)gnorm, (double)lambda));
324:             }
325:             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
326:           } else {
327:             if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
328:               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Cubic step no good, shrinking lambda, obj %12.12e lambda=%18.16e\n", (double)g, (double)lambda));
329:             } else {
330:               PetscCall(PetscViewerASCIIPrintf(monitor, "    Line search: Quadratic step no good, shrinking lambda, obj %12.12e lambda=%18.16e\n", (double)g, (double)lambda));
331:             }
332:             PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel));
333:           }
334:         }
335:       }
336:     }
337:   }

339:   /* postcheck */
340:   /* update Y to lambda*Y so that W is consistent with  X - lambda*Y */
341:   PetscCall(VecScale(Y, lambda));
342:   PetscCall(SNESLineSearchPostCheck(linesearch, X, Y, W, &changed_y, &changed_w));
343:   if (changed_y) {
344:     PetscCall(VecWAXPY(W, -1.0, Y, X));
345:     if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W));
346:   }
347:   if (changed_y || changed_w || objective) { /* recompute the function norm if the step has changed or the objective isn't the norm */
348:     PetscCall((*linesearch->ops->snesfunc)(snes, W, G));
349:     if (linesearch->ops->vinorm) {
350:       gnorm = fnorm;
351:       PetscCall((*linesearch->ops->vinorm)(snes, G, W, &gnorm));
352:     } else {
353:       PetscCall(VecNorm(G, NORM_2, &gnorm));
354:     }
355:     PetscCall(VecNorm(Y, NORM_2, &ynorm));
356:     if (PetscIsInfOrNanReal(gnorm)) {
357:       PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF));
358:       PetscCall(PetscInfo(snes, "Aborted due to Nan or Inf in function evaluation\n"));
359:       PetscFunctionReturn(PETSC_SUCCESS);
360:     }
361:   }

363:   /* copy the solution over */
364:   PetscCall(VecCopy(W, X));
365:   PetscCall(VecCopy(G, F));
366:   PetscCall(VecNorm(X, NORM_2, &xnorm));
367:   PetscCall(SNESLineSearchSetLambda(linesearch, lambda));
368:   PetscCall(SNESLineSearchSetNorms(linesearch, xnorm, gnorm, ynorm));
369:   PetscFunctionReturn(PETSC_SUCCESS);
370: }

372: PetscErrorCode SNESLineSearchView_BT(SNESLineSearch linesearch, PetscViewer viewer)
373: {
374:   PetscBool          iascii;
375:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;

377:   PetscFunctionBegin;
378:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
379:   if (iascii) {
380:     if (linesearch->order == SNES_LINESEARCH_ORDER_CUBIC) {
381:       PetscCall(PetscViewerASCIIPrintf(viewer, "  interpolation: cubic\n"));
382:     } else if (linesearch->order == SNES_LINESEARCH_ORDER_QUADRATIC) {
383:       PetscCall(PetscViewerASCIIPrintf(viewer, "  interpolation: quadratic\n"));
384:     }
385:     PetscCall(PetscViewerASCIIPrintf(viewer, "  alpha=%e\n", (double)bt->alpha));
386:   }
387:   PetscFunctionReturn(PETSC_SUCCESS);
388: }

390: static PetscErrorCode SNESLineSearchDestroy_BT(SNESLineSearch linesearch)
391: {
392:   PetscFunctionBegin;
393:   PetscCall(PetscFree(linesearch->data));
394:   PetscFunctionReturn(PETSC_SUCCESS);
395: }

397: static PetscErrorCode SNESLineSearchSetFromOptions_BT(SNESLineSearch linesearch, PetscOptionItems *PetscOptionsObject)
398: {
399:   SNESLineSearch_BT *bt = (SNESLineSearch_BT *)linesearch->data;

401:   PetscFunctionBegin;
402:   PetscOptionsHeadBegin(PetscOptionsObject, "SNESLineSearch BT options");
403:   PetscCall(PetscOptionsReal("-snes_linesearch_alpha", "Descent tolerance", "SNESLineSearchBT", bt->alpha, &bt->alpha, NULL));
404:   PetscOptionsHeadEnd();
405:   PetscFunctionReturn(PETSC_SUCCESS);
406: }

408: /*MC
409:    SNESLINESEARCHBT - Backtracking line search.

411:    This line search finds the minimum of a polynomial fitting of the L2 norm of the
412:    function or the objective function if it is provided with `SNESSetObjective()`. If this fit does not satisfy the conditions for progress, the interval shrinks
413:    and the fit is reattempted at most max_it times or until lambda is below minlambda.

415:    Options Database Keys:
416: +  -snes_linesearch_alpha <1e\-4> - slope descent parameter
417: .  -snes_linesearch_damping <1.0> - initial step length
418: .  -snes_linesearch_maxstep <length> - if the length the initial step is larger than this then the
419:                                        step is scaled back to be of this length at the beginning of the line search
420: .  -snes_linesearch_max_it <40> - maximum number of shrinking step
421: .  -snes_linesearch_minlambda <1e\-12> - minimum step length allowed
422: -  -snes_linesearch_order <cubic,quadratic> - order of the approximation

424:    Level: advanced

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

429:    Reference:
430: .  - * - "Numerical Methods for Unconstrained Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.

432: .seealso: `SNESLineSearch`, `SNESLineSearchType`, `SNESLineSearchCreate()`, `SNESLineSearchSetType()`
433: M*/
434: PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_BT(SNESLineSearch linesearch)
435: {
436:   SNESLineSearch_BT *bt;

438:   PetscFunctionBegin;
439:   linesearch->ops->apply          = SNESLineSearchApply_BT;
440:   linesearch->ops->destroy        = SNESLineSearchDestroy_BT;
441:   linesearch->ops->setfromoptions = SNESLineSearchSetFromOptions_BT;
442:   linesearch->ops->reset          = NULL;
443:   linesearch->ops->view           = SNESLineSearchView_BT;
444:   linesearch->ops->setup          = NULL;

446:   PetscCall(PetscNew(&bt));

448:   linesearch->data    = (void *)bt;
449:   linesearch->max_its = 40;
450:   linesearch->order   = SNES_LINESEARCH_ORDER_CUBIC;
451:   bt->alpha           = 1e-4;
452:   PetscFunctionReturn(PETSC_SUCCESS);
453: }