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