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