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