Actual source code: ls.c
1: /*$Id: ls.c,v 1.172 2001/08/07 03:04:11 balay Exp $*/
3: #include src/snes/impls/ls/ls.h
5: /*
6: Checks if J^T F = 0 which implies we've found a local minimum of the function,
7: but not a zero. In the case when one cannot compute J^T F we use the fact that
8: 0 = (J^T F)^T W = F^T J W iff W not in the null space of J. Thanks for Jorge More
9: for this trick.
10: */
13: int SNESLSCheckLocalMin_Private(Mat A,Vec F,Vec W,PetscReal fnorm,PetscTruth *ismin)
14: {
15: PetscReal a1;
16: int ierr;
17: PetscTruth hastranspose;
20: *ismin = PETSC_FALSE;
21: MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);
22: if (hastranspose) {
23: /* Compute || J^T F|| */
24: MatMultTranspose(A,F,W);
25: VecNorm(W,NORM_2,&a1);
26: PetscLogInfo(0,"SNESSolve_LS: || J^T F|| %g near zero implies found a local minimum\n",a1/fnorm);
27: if (a1/fnorm < 1.e-4) *ismin = PETSC_TRUE;
28: } else {
29: Vec work;
30: PetscScalar result;
31: PetscReal wnorm;
33: VecSetRandom(PETSC_NULL,W);
34: VecNorm(W,NORM_2,&wnorm);
35: VecDuplicate(W,&work);
36: MatMult(A,W,work);
37: VecDot(F,work,&result);
38: VecDestroy(work);
39: a1 = PetscAbsScalar(result)/(fnorm*wnorm);
40: PetscLogInfo(0,"SNESSolve_LS: (F^T J random)/(|| F ||*||random|| %g near zero implies found a local minimum\n",a1);
41: if (a1 < 1.e-4) *ismin = PETSC_TRUE;
42: }
43: return(0);
44: }
46: /*
47: Checks if J^T(F - J*X) = 0
48: */
51: int SNESLSCheckResidual_Private(Mat A,Vec F,Vec X,Vec W1,Vec W2)
52: {
53: PetscReal a1,a2;
54: int ierr;
55: PetscTruth hastranspose;
56: PetscScalar mone = -1.0;
59: MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);
60: if (hastranspose) {
61: MatMult(A,X,W1);
62: VecAXPY(&mone,F,W1);
64: /* Compute || J^T W|| */
65: MatMultTranspose(A,W1,W2);
66: VecNorm(W1,NORM_2,&a1);
67: VecNorm(W2,NORM_2,&a2);
68: if (a1 != 0) {
69: PetscLogInfo(0,"SNESSolve_LS: ||J^T(F-Ax)||/||F-AX|| %g near zero implies inconsistent rhs\n",a2/a1);
70: }
71: }
72: return(0);
73: }
75: /* --------------------------------------------------------------------
77: This file implements a truncated Newton method with a line search,
78: for solving a system of nonlinear equations, using the KSP, Vec,
79: and Mat interfaces for linear solvers, vectors, and matrices,
80: respectively.
82: The following basic routines are required for each nonlinear solver:
83: SNESCreate_XXX() - Creates a nonlinear solver context
84: SNESSetFromOptions_XXX() - Sets runtime options
85: SNESSolve_XXX() - Solves the nonlinear system
86: SNESDestroy_XXX() - Destroys the nonlinear solver context
87: The suffix "_XXX" denotes a particular implementation, in this case
88: we use _LS (e.g., SNESCreate_LS, SNESSolve_LS) for solving
89: systems of nonlinear equations with a line search (LS) method.
90: These routines are actually called via the common user interface
91: routines SNESCreate(), SNESSetFromOptions(), SNESSolve(), and
92: SNESDestroy(), so the application code interface remains identical
93: for all nonlinear solvers.
95: Another key routine is:
96: SNESSetUp_XXX() - Prepares for the use of a nonlinear solver
97: by setting data structures and options. The interface routine SNESSetUp()
98: is not usually called directly by the user, but instead is called by
99: SNESSolve() if necessary.
101: Additional basic routines are:
102: SNESView_XXX() - Prints details of runtime options that
103: have actually been used.
104: These are called by application codes via the interface routines
105: SNESView().
107: The various types of solvers (preconditioners, Krylov subspace methods,
108: nonlinear solvers, timesteppers) are all organized similarly, so the
109: above description applies to these categories also.
111: -------------------------------------------------------------------- */
112: /*
113: SNESSolve_LS - Solves a nonlinear system with a truncated Newton
114: method with a line search.
116: Input Parameters:
117: . snes - the SNES context
119: Output Parameter:
120: . outits - number of iterations until termination
122: Application Interface Routine: SNESSolve()
124: Notes:
125: This implements essentially a truncated Newton method with a
126: line search. By default a cubic backtracking line search
127: is employed, as described in the text "Numerical Methods for
128: Unconstrained Optimization and Nonlinear Equations" by Dennis
129: and Schnabel.
130: */
133: int SNESSolve_LS(SNES snes)
134: {
135: SNES_LS *neP = (SNES_LS*)snes->data;
136: int maxits,i,ierr,lits,lsfail;
137: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
138: PetscReal fnorm,gnorm,xnorm,ynorm;
139: Vec Y,X,F,G,W,TMP;
140: KSP ksp;
143: SNESGetKSP(snes,&ksp);
144: snes->reason = SNES_CONVERGED_ITERATING;
146: maxits = snes->max_its; /* maximum number of iterations */
147: X = snes->vec_sol; /* solution vector */
148: F = snes->vec_func; /* residual vector */
149: Y = snes->work[0]; /* work vectors */
150: G = snes->work[1];
151: W = snes->work[2];
153: PetscObjectTakeAccess(snes);
154: snes->iter = 0;
155: PetscObjectGrantAccess(snes);
156: SNESComputeFunction(snes,X,F); /* F(X) */
157: VecNorm(F,NORM_2,&fnorm); /* fnorm <- ||F|| */
158: PetscObjectTakeAccess(snes);
159: snes->norm = fnorm;
160: PetscObjectGrantAccess(snes);
161: SNESLogConvHistory(snes,fnorm,0);
162: SNESMonitor(snes,0,fnorm);
164: if (fnorm < snes->atol) {snes->reason = SNES_CONVERGED_FNORM_ABS; return(0);}
166: /* set parameter for default relative tolerance convergence test */
167: snes->ttol = fnorm*snes->rtol;
169: for (i=0; i<maxits; i++) {
171: /* Call general purpose update function */
172: if (snes->update != PETSC_NULL) {
173: (*snes->update)(snes, snes->iter);
174: }
176: /* Solve J Y = F, where J is Jacobian matrix */
177: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
178: KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
179: KSPSetRhs(snes->ksp,F);
180: KSPSetSolution(snes->ksp,Y);
181: KSPSolve(snes->ksp);
182: KSPGetIterationNumber(ksp,&lits);
184: if (PetscLogPrintInfo){
185: SNESLSCheckResidual_Private(snes->jacobian,F,Y,G,W);
186: }
188: /* should check what happened to the linear solve? */
189: snes->linear_its += lits;
190: PetscLogInfo(snes,"SNESSolve_LS: iter=%d, linear solve iterations=%d\n",snes->iter,lits);
192: /* Compute a (scaled) negative update in the line search routine:
193: Y <- X - lambda*Y
194: and evaluate G(Y) = function(Y))
195: */
196: VecCopy(Y,snes->vec_sol_update_always);
197: (*neP->LineSearch)(snes,neP->lsP,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail);
198: PetscLogInfo(snes,"SNESSolve_LS: fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lsfail=%d\n",fnorm,gnorm,ynorm,lsfail);
200: TMP = F; F = G; snes->vec_func_always = F; G = TMP;
201: TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
202: fnorm = gnorm;
204: PetscObjectTakeAccess(snes);
205: snes->iter = i+1;
206: snes->norm = fnorm;
207: PetscObjectGrantAccess(snes);
208: SNESLogConvHistory(snes,fnorm,lits);
209: SNESMonitor(snes,i+1,fnorm);
211: if (lsfail) {
212: PetscTruth ismin;
214: if (++snes->numFailures >= snes->maxFailures) {
215: snes->reason = SNES_DIVERGED_LS_FAILURE;
216: SNESLSCheckLocalMin_Private(snes->jacobian,F,W,fnorm,&ismin);
217: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
218: break;
219: }
220: }
222: /* Test for convergence */
223: if (snes->converged) {
224: VecNorm(X,NORM_2,&xnorm); /* xnorm = || X || */
225: (*snes->converged)(snes,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
226: if (snes->reason) {
227: break;
228: }
229: }
230: }
231: if (X != snes->vec_sol) {
232: VecCopy(X,snes->vec_sol);
233: }
234: if (F != snes->vec_func) {
235: VecCopy(F,snes->vec_func);
236: }
237: snes->vec_sol_always = snes->vec_sol;
238: snes->vec_func_always = snes->vec_func;
239: if (i == maxits) {
240: PetscLogInfo(snes,"SNESSolve_LS: Maximum number of iterations has been reached: %d\n",maxits);
241: snes->reason = SNES_DIVERGED_MAX_IT;
242: }
243: return(0);
244: }
245: /* -------------------------------------------------------------------------- */
246: /*
247: SNESSetUp_LS - Sets up the internal data structures for the later use
248: of the SNESLS nonlinear solver.
250: Input Parameter:
251: . snes - the SNES context
252: . x - the solution vector
254: Application Interface Routine: SNESSetUp()
256: Notes:
257: For basic use of the SNES solvers, the user need not explicitly call
258: SNESSetUp(), since these actions will automatically occur during
259: the call to SNESSolve().
260: */
263: int SNESSetUp_LS(SNES snes)
264: {
268: snes->nwork = 4;
269: VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work);
270: PetscLogObjectParents(snes,snes->nwork,snes->work);
271: snes->vec_sol_update_always = snes->work[3];
272: return(0);
273: }
274: /* -------------------------------------------------------------------------- */
275: /*
276: SNESDestroy_LS - Destroys the private SNES_LS context that was created
277: with SNESCreate_LS().
279: Input Parameter:
280: . snes - the SNES context
282: Application Interface Routine: SNESDestroy()
283: */
286: int SNESDestroy_LS(SNES snes)
287: {
288: int ierr;
291: if (snes->nwork) {
292: VecDestroyVecs(snes->work,snes->nwork);
293: }
294: PetscFree(snes->data);
295: return(0);
296: }
297: /* -------------------------------------------------------------------------- */
301: /*@C
302: SNESNoLineSearch - This routine is not a line search at all;
303: it simply uses the full Newton step. Thus, this routine is intended
304: to serve as a template and is not recommended for general use.
306: Collective on SNES and Vec
308: Input Parameters:
309: + snes - nonlinear context
310: . lsctx - optional context for line search (not used here)
311: . x - current iterate
312: . f - residual evaluated at x
313: . y - search direction (contains new iterate on output)
314: . w - work vector
315: - fnorm - 2-norm of f
317: Output Parameters:
318: + g - residual evaluated at new iterate y
319: . y - new iterate (contains search direction on input)
320: . gnorm - 2-norm of g
321: . ynorm - 2-norm of search length
322: - flag - set to 0, indicating a successful line search
324: Options Database Key:
325: . -snes_ls basic - Activates SNESNoLineSearch()
327: Level: advanced
329: .keywords: SNES, nonlinear, line search, cubic
331: .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
332: SNESSetLineSearch(), SNESNoLineSearchNoNorms()
333: @*/
334: int SNESNoLineSearch(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,int *flag)
335: {
336: int ierr;
337: PetscScalar mone = -1.0;
338: SNES_LS *neP = (SNES_LS*)snes->data;
339: PetscTruth change_y = PETSC_FALSE;
342: *flag = 0;
343: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
344: VecNorm(y,NORM_2,ynorm); /* ynorm = || y || */
345: VecAYPX(&mone,x,y); /* y <- y - x */
346: if (neP->CheckStep) {
347: (*neP->CheckStep)(snes,neP->checkP,y,&change_y);
348: }
349: SNESComputeFunction(snes,y,g); /* Compute F(y) */
350: VecNorm(g,NORM_2,gnorm); /* gnorm = || g || */
351: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
352: return(0);
353: }
354: /* -------------------------------------------------------------------------- */
358: /*@C
359: SNESNoLineSearchNoNorms - This routine is not a line search at
360: all; it simply uses the full Newton step. This version does not
361: even compute the norm of the function or search direction; this
362: is intended only when you know the full step is fine and are
363: not checking for convergence of the nonlinear iteration (for
364: example, you are running always for a fixed number of Newton steps).
366: Collective on SNES and Vec
368: Input Parameters:
369: + snes - nonlinear context
370: . lsctx - optional context for line search (not used here)
371: . x - current iterate
372: . f - residual evaluated at x
373: . y - search direction (contains new iterate on output)
374: . w - work vector
375: - fnorm - 2-norm of f
377: Output Parameters:
378: + g - residual evaluated at new iterate y
379: . gnorm - not changed
380: . ynorm - not changed
381: - flag - set to 0, indicating a successful line search
383: Options Database Key:
384: . -snes_ls basicnonorms - Activates SNESNoLineSearchNoNorms()
386: Notes:
387: SNESNoLineSearchNoNorms() must be used in conjunction with
388: either the options
389: $ -snes_no_convergence_test -snes_max_it <its>
390: or alternatively a user-defined custom test set via
391: SNESSetConvergenceTest(); or a -snes_max_it of 1,
392: otherwise, the SNES solver will generate an error.
394: During the final iteration this will not evaluate the function at
395: the solution point. This is to save a function evaluation while
396: using pseudo-timestepping.
398: The residual norms printed by monitoring routines such as
399: SNESDefaultMonitor() (as activated via -snes_monitor) will not be
400: correct, since they are not computed.
402: Level: advanced
404: .keywords: SNES, nonlinear, line search, cubic
406: .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
407: SNESSetLineSearch(), SNESNoLineSearch()
408: @*/
409: int SNESNoLineSearchNoNorms(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,int *flag)
410: {
411: int ierr;
412: PetscScalar mone = -1.0;
413: SNES_LS *neP = (SNES_LS*)snes->data;
414: PetscTruth change_y = PETSC_FALSE;
417: *flag = 0;
418: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
419: VecAYPX(&mone,x,y); /* y <- y - x */
420: if (neP->CheckStep) {
421: (*neP->CheckStep)(snes,neP->checkP,y,&change_y);
422: }
423:
424: /* don't evaluate function the last time through */
425: if (snes->iter < snes->max_its-1) {
426: SNESComputeFunction(snes,y,g); /* Compute F(y) */
427: }
428: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
429: return(0);
430: }
431: /* -------------------------------------------------------------------------- */
434: /*@C
435: SNESCubicLineSearch - Performs a cubic line search (default line search method).
437: Collective on SNES
439: Input Parameters:
440: + snes - nonlinear context
441: . lsctx - optional context for line search (not used here)
442: . x - current iterate
443: . f - residual evaluated at x
444: . y - search direction (contains new iterate on output)
445: . w - work vector
446: - fnorm - 2-norm of f
448: Output Parameters:
449: + g - residual evaluated at new iterate y
450: . y - new iterate (contains search direction on input)
451: . gnorm - 2-norm of g
452: . ynorm - 2-norm of search length
453: - flag - 0 if line search succeeds; -1 on failure.
455: Options Database Key:
456: $ -snes_ls cubic - Activates SNESCubicLineSearch()
458: Notes:
459: This line search is taken from "Numerical Methods for Unconstrained
460: Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
462: Level: advanced
464: .keywords: SNES, nonlinear, line search, cubic
466: .seealso: SNESQuadraticLineSearch(), SNESNoLineSearch(), SNESSetLineSearch(), SNESNoLineSearchNoNorms()
467: @*/
468: int SNESCubicLineSearch(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,int *flag)
469: {
470: /*
471: Note that for line search purposes we work with with the related
472: minimization problem:
473: min z(x): R^n -> R,
474: where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2.
475: */
476:
477: PetscReal steptol,initslope,lambdaprev,gnormprev,a,b,d,t1,t2,rellength;
478: PetscReal maxstep,minlambda,alpha,lambda,lambdatemp,lambdaneg;
479: #if defined(PETSC_USE_COMPLEX)
480: PetscScalar cinitslope,clambda;
481: #endif
482: int ierr,count;
483: SNES_LS *neP = (SNES_LS*)snes->data;
484: PetscScalar mone = -1.0,scale;
485: PetscTruth change_y = PETSC_FALSE;
488: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
489: *flag = 0;
490: alpha = neP->alpha;
491: maxstep = neP->maxstep;
492: steptol = neP->steptol;
494: VecNorm(y,NORM_2,ynorm);
495: if (*ynorm == 0.0) {
496: PetscLogInfo(snes,"SNESCubicLineSearch: Search direction and size is 0\n");
497: *gnorm = fnorm;
498: VecCopy(x,y);
499: VecCopy(f,g);
500: *flag = -1;
501: goto theend1;
502: }
503: if (*ynorm > maxstep) { /* Step too big, so scale back */
504: scale = maxstep/(*ynorm);
505: #if defined(PETSC_USE_COMPLEX)
506: PetscLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g old ynorm %g\n",PetscRealPart(scale),*ynorm);
507: #else
508: PetscLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g old ynorm %g\n",scale,*ynorm);
509: #endif
510: VecScale(&scale,y);
511: *ynorm = maxstep;
512: }
513: VecMaxPointwiseDivide(y,x,&rellength);
514: minlambda = steptol/rellength;
515: MatMult(snes->jacobian,y,w);
516: #if defined(PETSC_USE_COMPLEX)
517: VecDot(f,w,&cinitslope);
518: initslope = PetscRealPart(cinitslope);
519: #else
520: VecDot(f,w,&initslope);
521: #endif
522: if (initslope > 0.0) initslope = -initslope;
523: if (initslope == 0.0) initslope = -1.0;
525: VecCopy(y,w);
526: VecAYPX(&mone,x,w);
527: SNESComputeFunction(snes,w,g);
528: VecNorm(g,NORM_2,gnorm);
529: if (.5*(*gnorm)*(*gnorm) <= .5*fnorm*fnorm + alpha*initslope) { /* Sufficient reduction */
530: VecCopy(w,y);
531: PetscLogInfo(snes,"SNESCubicLineSearch: Using full step\n");
532: goto theend1;
533: }
535: /* Fit points with quadratic */
536: lambda = 1.0;
537: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
538: lambdaprev = lambda;
539: gnormprev = *gnorm;
540: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
541: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
542: else lambda = lambdatemp;
543: VecCopy(x,w);
544: lambdaneg = -lambda;
545: #if defined(PETSC_USE_COMPLEX)
546: clambda = lambdaneg; VecAXPY(&clambda,y,w);
547: #else
548: VecAXPY(&lambdaneg,y,w);
549: #endif
550: SNESComputeFunction(snes,w,g);
551: VecNorm(g,NORM_2,gnorm);
552: if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*alpha*initslope) { /* sufficient reduction */
553: VecCopy(w,y);
554: PetscLogInfo(snes,"SNESCubicLineSearch: Quadratically determined step, lambda=%18.16e\n",lambda);
555: goto theend1;
556: }
558: /* Fit points with cubic */
559: count = 1;
560: while (count < 10000) {
561: if (lambda <= minlambda) { /* bad luck; use full step */
562: PetscLogInfo(snes,"SNESCubicLineSearch:Unable to find good step length! %d \n",count);
563: PetscLogInfo(snes,"SNESCubicLineSearch:fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
564: VecCopy(x,y);
565: *flag = -1; break;
566: }
567: t1 = .5*((*gnorm)*(*gnorm) - fnorm*fnorm) - lambda*initslope;
568: t2 = .5*(gnormprev*gnormprev - fnorm*fnorm) - lambdaprev*initslope;
569: a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
570: b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
571: d = b*b - 3*a*initslope;
572: if (d < 0.0) d = 0.0;
573: if (a == 0.0) {
574: lambdatemp = -initslope/(2.0*b);
575: } else {
576: lambdatemp = (-b + sqrt(d))/(3.0*a);
577: }
578: lambdaprev = lambda;
579: gnormprev = *gnorm;
580: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
581: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
582: else lambda = lambdatemp;
583: VecCopy(x,w);
584: lambdaneg = -lambda;
585: #if defined(PETSC_USE_COMPLEX)
586: clambda = lambdaneg;
587: VecAXPY(&clambda,y,w);
588: #else
589: VecAXPY(&lambdaneg,y,w);
590: #endif
591: SNESComputeFunction(snes,w,g);
592: VecNorm(g,NORM_2,gnorm);
593: if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*alpha*initslope) { /* is reduction enough? */
594: VecCopy(w,y);
595: PetscLogInfo(snes,"SNESCubicLineSearch: Cubically determined step, lambda=%18.16e\n",lambda);
596: goto theend1;
597: } else {
598: PetscLogInfo(snes,"SNESCubicLineSearch: Cubic step no good, shrinking lambda, lambda=%18.16e\n",lambda);
599: }
600: count++;
601: }
602: if (count >= 10000) {
603: SETERRQ(PETSC_ERR_LIB, "Lambda was decreased more than 10,000 times, so something is probably wrong with the function evaluation");
604: }
605: theend1:
606: /* Optional user-defined check for line search step validity */
607: if (neP->CheckStep) {
608: (*neP->CheckStep)(snes,neP->checkP,y,&change_y);
609: if (change_y == PETSC_TRUE) { /* recompute the function if the step has changed */
610: SNESComputeFunction(snes,y,g);
611: VecNormBegin(y,NORM_2,ynorm);
612: VecNormBegin(g,NORM_2,gnorm);
613: VecNormEnd(y,NORM_2,ynorm);
614: VecNormEnd(g,NORM_2,gnorm);
615: }
616: }
617: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
618: return(0);
619: }
620: /* -------------------------------------------------------------------------- */
623: /*@C
624: SNESQuadraticLineSearch - Performs a quadratic line search.
626: Collective on SNES and Vec
628: Input Parameters:
629: + snes - the SNES context
630: . lsctx - optional context for line search (not used here)
631: . x - current iterate
632: . f - residual evaluated at x
633: . y - search direction (contains new iterate on output)
634: . w - work vector
635: - fnorm - 2-norm of f
637: Output Parameters:
638: + g - residual evaluated at new iterate y
639: . y - new iterate (contains search direction on input)
640: . gnorm - 2-norm of g
641: . ynorm - 2-norm of search length
642: - flag - 0 if line search succeeds; -1 on failure.
644: Options Database Key:
645: . -snes_ls quadratic - Activates SNESQuadraticLineSearch()
647: Notes:
648: Use SNESSetLineSearch() to set this routine within the SNESLS method.
650: Level: advanced
652: .keywords: SNES, nonlinear, quadratic, line search
654: .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearch(), SNESNoLineSearchNoNorms()
655: @*/
656: int SNESQuadraticLineSearch(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,int *flag)
657: {
658: /*
659: Note that for line search purposes we work with with the related
660: minimization problem:
661: min z(x): R^n -> R,
662: where z(x) = .5 * fnorm*fnorm,and fnorm = || f ||_2.
663: */
664: PetscReal steptol,initslope,maxstep,minlambda,alpha,lambda,lambdatemp,lambdaneg,rellength;
665: #if defined(PETSC_USE_COMPLEX)
666: PetscScalar cinitslope,clambda;
667: #endif
668: int ierr,count;
669: SNES_LS *neP = (SNES_LS*)snes->data;
670: PetscScalar mone = -1.0,scale;
671: PetscTruth change_y = PETSC_FALSE;
674: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
675: *flag = 0;
676: alpha = neP->alpha;
677: maxstep = neP->maxstep;
678: steptol = neP->steptol;
680: VecNorm(y,NORM_2,ynorm);
681: if (*ynorm == 0.0) {
682: PetscLogInfo(snes,"SNESQuadraticLineSearch: Search direction and size is 0\n");
683: *gnorm = fnorm;
684: VecCopy(x,y);
685: VecCopy(f,g);
686: *flag = -1;
687: goto theend2;
688: }
689: if (*ynorm > maxstep) { /* Step too big, so scale back */
690: scale = maxstep/(*ynorm);
691: VecScale(&scale,y);
692: *ynorm = maxstep;
693: }
694: VecMaxPointwiseDivide(y,x,&rellength);
695: minlambda = steptol/rellength;
696: MatMult(snes->jacobian,y,w);
697: #if defined(PETSC_USE_COMPLEX)
698: VecDot(f,w,&cinitslope);
699: initslope = PetscRealPart(cinitslope);
700: #else
701: VecDot(f,w,&initslope);
702: #endif
703: if (initslope > 0.0) initslope = -initslope;
704: if (initslope == 0.0) initslope = -1.0;
706: VecCopy(y,w);
707: VecAYPX(&mone,x,w);
708: SNESComputeFunction(snes,w,g);
709: VecNorm(g,NORM_2,gnorm);
710: if (.5*(*gnorm)*(*gnorm) <= .5*fnorm*fnorm + alpha*initslope) { /* Sufficient reduction */
711: VecCopy(w,y);
712: PetscLogInfo(snes,"SNESQuadraticLineSearch: Using full step\n");
713: goto theend2;
714: }
716: /* Fit points with quadratic */
717: lambda = 1.0;
718: count = 1;
719: while (PETSC_TRUE) {
720: if (lambda <= minlambda) { /* bad luck; use full step */
721: PetscLogInfo(snes,"SNESQuadraticLineSearch:Unable to find good step length! %d \n",count);
722: PetscLogInfo(snes,"SNESQuadraticLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n",fnorm,*gnorm,*ynorm,lambda,initslope);
723: VecCopy(x,y);
724: *flag = -1; break;
725: }
726: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
727: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
728: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
729: else lambda = lambdatemp;
730: VecCopy(x,w);
731: lambdaneg = -lambda;
732: #if defined(PETSC_USE_COMPLEX)
733: clambda = lambdaneg; VecAXPY(&clambda,y,w);
734: #else
735: VecAXPY(&lambdaneg,y,w);
736: #endif
737: SNESComputeFunction(snes,w,g);
738: VecNorm(g,NORM_2,gnorm);
739: if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*alpha*initslope) { /* sufficient reduction */
740: VecCopy(w,y);
741: PetscLogInfo(snes,"SNESQuadraticLineSearch:Quadratically determined step, lambda=%g\n",lambda);
742: break;
743: }
744: count++;
745: }
746: theend2:
747: /* Optional user-defined check for line search step validity */
748: if (neP->CheckStep) {
749: (*neP->CheckStep)(snes,neP->checkP,y,&change_y);
750: if (change_y == PETSC_TRUE) { /* recompute the function if the step has changed */
751: SNESComputeFunction(snes,y,g);
752: VecNormBegin(y,NORM_2,ynorm);
753: VecNormBegin(g,NORM_2,gnorm);
754: VecNormEnd(y,NORM_2,ynorm);
755: VecNormEnd(g,NORM_2,gnorm);
756: }
757: }
758: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
759: return(0);
760: }
762: /* -------------------------------------------------------------------------- */
765: /*@C
766: SNESSetLineSearch - Sets the line search routine to be used
767: by the method SNESLS.
769: Input Parameters:
770: + snes - nonlinear context obtained from SNESCreate()
771: . lsctx - optional user-defined context for use by line search
772: - func - pointer to int function
774: Collective on SNES
776: Available Routines:
777: + SNESCubicLineSearch() - default line search
778: . SNESQuadraticLineSearch() - quadratic line search
779: . SNESNoLineSearch() - the full Newton step (actually not a line search)
780: - SNESNoLineSearchNoNorms() - the full Newton step (calculating no norms; faster in parallel)
782: Options Database Keys:
783: + -snes_ls [cubic,quadratic,basic,basicnonorms] - Selects line search
784: . -snes_ls_alpha <alpha> - Sets alpha
785: . -snes_ls_maxstep <max> - Sets maxstep
786: - -snes_ls_steptol <steptol> - Sets steptol, this is the minimum step size that the line search code
787: will accept; min p[i]/x[i] < steptol. The -snes_stol <stol> is the minimum step length
788: the default convergence test will use and is based on 2-norm(p) < stol*2-norm(x)
790: Calling sequence of func:
791: .vb
792: func (SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,
793: PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,*flag)
794: .ve
796: Input parameters for func:
797: + snes - nonlinear context
798: . lsctx - optional user-defined context for line search
799: . x - current iterate
800: . f - residual evaluated at x
801: . y - search direction (contains new iterate on output)
802: . w - work vector
803: - fnorm - 2-norm of f
805: Output parameters for func:
806: + g - residual evaluated at new iterate y
807: . y - new iterate (contains search direction on input)
808: . gnorm - 2-norm of g
809: . ynorm - 2-norm of search length
810: - flag - set to 0 if the line search succeeds; a nonzero integer
811: on failure.
813: Level: advanced
815: .keywords: SNES, nonlinear, set, line search, routine
817: .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), SNESNoLineSearch(), SNESNoLineSearchNoNorms(),
818: SNESSetLineSearchCheck(), SNESSetLineSearchParams(), SNESGetLineSearchParams()
819: @*/
820: int SNESSetLineSearch(SNES snes,int (*func)(SNES,void*,Vec,Vec,Vec,Vec,Vec,PetscReal,PetscReal*,PetscReal*,int*),void *lsctx)
821: {
822: int ierr,(*f)(SNES,int (*)(SNES,void*,Vec,Vec,Vec,Vec,Vec,PetscReal,PetscReal*,PetscReal*,int*),void*);
825: PetscObjectQueryFunction((PetscObject)snes,"SNESSetLineSearch_C",(void (**)(void))&f);
826: if (f) {
827: (*f)(snes,func,lsctx);
828: }
829: return(0);
830: }
832: typedef int (*FCN2)(SNES,void*,Vec,Vec,Vec,Vec,Vec,PetscReal,PetscReal*,PetscReal*,int*); /* force argument to next function to not be extern C*/
833: /* -------------------------------------------------------------------------- */
834: EXTERN_C_BEGIN
837: int SNESSetLineSearch_LS(SNES snes,FCN2 func,void *lsctx)
838: {
840: ((SNES_LS *)(snes->data))->LineSearch = func;
841: ((SNES_LS *)(snes->data))->lsP = lsctx;
842: return(0);
843: }
844: EXTERN_C_END
845: /* -------------------------------------------------------------------------- */
848: /*@C
849: SNESSetLineSearchCheck - Sets a routine to check the validity of new iterate computed
850: by the line search routine in the Newton-based method SNESLS.
852: Input Parameters:
853: + snes - nonlinear context obtained from SNESCreate()
854: . func - pointer to int function
855: - checkctx - optional user-defined context for use by step checking routine
857: Collective on SNES
859: Calling sequence of func:
860: .vb
861: int func (SNES snes, void *checkctx, Vec x, PetscTruth *flag)
862: .ve
863: where func returns an error code of 0 on success and a nonzero
864: on failure.
866: Input parameters for func:
867: + snes - nonlinear context
868: . checkctx - optional user-defined context for use by step checking routine
869: - x - current candidate iterate
871: Output parameters for func:
872: + x - current iterate (possibly modified)
873: - flag - flag indicating whether x has been modified (either
874: PETSC_TRUE of PETSC_FALSE)
876: Level: advanced
878: Notes:
879: SNESNoLineSearch() and SNESNoLineSearchNoNorms() accept the new
880: iterate computed by the line search checking routine. In particular,
881: these routines (1) compute a candidate iterate u_{i+1}, (2) pass control
882: to the checking routine, and then (3) compute the corresponding nonlinear
883: function f(u_{i+1}) with the (possibly altered) iterate u_{i+1}.
885: SNESQuadraticLineSearch() and SNESCubicLineSearch() also accept the
886: new iterate computed by the line search checking routine. In particular,
887: these routines (1) compute a candidate iterate u_{i+1} as well as a
888: candidate nonlinear function f(u_{i+1}), (2) pass control to the checking
889: routine, and then (3) force a re-evaluation of f(u_{i+1}) if any changes
890: were made to the candidate iterate in the checking routine (as indicated
891: by flag=PETSC_TRUE). The overhead of this function re-evaluation can be
892: very costly, so use this feature with caution!
894: .keywords: SNES, nonlinear, set, line search check, step check, routine
896: .seealso: SNESSetLineSearch()
897: @*/
898: int SNESSetLineSearchCheck(SNES snes,int (*func)(SNES,void*,Vec,PetscTruth*),void *checkctx)
899: {
900: int ierr,(*f)(SNES,int (*)(SNES,void*,Vec,PetscTruth*),void*);
903: PetscObjectQueryFunction((PetscObject)snes,"SNESSetLineSearchCheck_C",(void (**)(void))&f);
904: if (f) {
905: (*f)(snes,func,checkctx);
906: }
907: return(0);
908: }
909: /* -------------------------------------------------------------------------- */
910: typedef int (*FCN)(SNES,void*,Vec,PetscTruth*); /* force argument to next function to not be extern C*/
911: EXTERN_C_BEGIN
914: int SNESSetLineSearchCheck_LS(SNES snes,FCN func,void *checkctx)
915: {
917: ((SNES_LS *)(snes->data))->CheckStep = func;
918: ((SNES_LS *)(snes->data))->checkP = checkctx;
919: return(0);
920: }
921: EXTERN_C_END
922: /* -------------------------------------------------------------------------- */
923: /*
924: SNESPrintHelp_LS - Prints all options for the SNES_LS method.
926: Input Parameter:
927: . snes - the SNES context
929: Application Interface Routine: SNESPrintHelp()
930: */
933: static int SNESPrintHelp_LS(SNES snes,char *p)
934: {
935: SNES_LS *ls = (SNES_LS *)snes->data;
938: (*PetscHelpPrintf)(snes->comm," method SNES_LS (ls) for systems of nonlinear equations:\n");
939: (*PetscHelpPrintf)(snes->comm," %ssnes_ls [cubic,quadratic,basic,basicnonorms,...]\n",p);
940: (*PetscHelpPrintf)(snes->comm," %ssnes_ls_alpha <alpha> (default %g)\n",p,ls->alpha);
941: (*PetscHelpPrintf)(snes->comm," %ssnes_ls_maxstep <max> (default %g)\n",p,ls->maxstep);
942: (*PetscHelpPrintf)(snes->comm," %ssnes_ls_steptol <tol> (default %g)\n",p,ls->steptol);
943: return(0);
944: }
946: /*
947: SNESView_LS - Prints info from the SNESLS data structure.
949: Input Parameters:
950: . SNES - the SNES context
951: . viewer - visualization context
953: Application Interface Routine: SNESView()
954: */
957: static int SNESView_LS(SNES snes,PetscViewer viewer)
958: {
959: SNES_LS *ls = (SNES_LS *)snes->data;
960: const char *cstr;
961: int ierr;
962: PetscTruth isascii;
965: PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&isascii);
966: if (isascii) {
967: if (ls->LineSearch == SNESNoLineSearch) cstr = "SNESNoLineSearch";
968: else if (ls->LineSearch == SNESQuadraticLineSearch) cstr = "SNESQuadraticLineSearch";
969: else if (ls->LineSearch == SNESCubicLineSearch) cstr = "SNESCubicLineSearch";
970: else cstr = "unknown";
971: PetscViewerASCIIPrintf(viewer," line search variant: %s\n",cstr);
972: PetscViewerASCIIPrintf(viewer," alpha=%g, maxstep=%g, steptol=%g\n",ls->alpha,ls->maxstep,ls->steptol);
973: } else {
974: SETERRQ1(1,"Viewer type %s not supported for SNES EQ LS",((PetscObject)viewer)->type_name);
975: }
976: return(0);
977: }
978: /* -------------------------------------------------------------------------- */
979: /*
980: SNESSetFromOptions_LS - Sets various parameters for the SNESLS method.
982: Input Parameter:
983: . snes - the SNES context
985: Application Interface Routine: SNESSetFromOptions()
986: */
989: static int SNESSetFromOptions_LS(SNES snes)
990: {
991: SNES_LS *ls = (SNES_LS *)snes->data;
992: const char *lses[] = {"basic","basicnonorms","quadratic","cubic"};
993: int ierr,indx;
994: PetscTruth flg;
997: PetscOptionsHead("SNES Line search options");
998: PetscOptionsReal("-snes_ls_alpha","Function norm must decrease by","None",ls->alpha,&ls->alpha,0);
999: PetscOptionsReal("-snes_ls_maxstep","Step must be less than","None",ls->maxstep,&ls->maxstep,0);
1000: PetscOptionsReal("-snes_ls_steptol","Step must be greater than","None",ls->steptol,&ls->steptol,0);
1002: PetscOptionsEList("-snes_ls","Line search used","SNESSetLineSearch",lses,4,"cubic",&indx,&flg);
1003: if (flg) {
1004: switch (indx) {
1005: case 0:
1006: SNESSetLineSearch(snes,SNESNoLineSearch,PETSC_NULL);
1007: break;
1008: case 1:
1009: SNESSetLineSearch(snes,SNESNoLineSearchNoNorms,PETSC_NULL);
1010: break;
1011: case 2:
1012: SNESSetLineSearch(snes,SNESQuadraticLineSearch,PETSC_NULL);
1013: break;
1014: case 3:
1015: SNESSetLineSearch(snes,SNESCubicLineSearch,PETSC_NULL);
1016: break;
1017: }
1018: }
1019: PetscOptionsTail();
1020: return(0);
1021: }
1022: /* -------------------------------------------------------------------------- */
1023: /*MC
1024: SNESLS - Newton based nonlinear solver that uses a line search
1026: Options Database:
1027: + -snes_ls [cubic,quadratic,basic,basicnonorms] - Selects line search
1028: . -snes_ls_alpha <alpha> - Sets alpha
1029: . -snes_ls_maxstep <max> - Sets maxstep
1030: - -snes_ls_steptol <steptol> - Sets steptol, this is the minimum step size that the line search code
1031: will accept; min p[i]/x[i] < steptol. The -snes_stol <stol> is the minimum step length
1032: the default convergence test will use and is based on 2-norm(p) < stol*2-norm(x)
1034: Notes: This is the default nonlinear solver in SNES
1036: .seealso: SNESCreate(), SNES, SNESSetType(), SNESTR, SNESSetLineSearch(),
1037: SNESSetLineSearchCheck(), SNESNoLineSearch(), SNESCubicLineSearch(), SNESQuadraticLineSearch(),
1038: SNESSetLineSearch(), SNESNoLineSearchNoNorms()
1040: M*/
1041: EXTERN_C_BEGIN
1044: int SNESCreate_LS(SNES snes)
1045: {
1046: int ierr;
1047: SNES_LS *neP;
1050: snes->setup = SNESSetUp_LS;
1051: snes->solve = SNESSolve_LS;
1052: snes->destroy = SNESDestroy_LS;
1053: snes->converged = SNESConverged_LS;
1054: snes->printhelp = SNESPrintHelp_LS;
1055: snes->setfromoptions = SNESSetFromOptions_LS;
1056: snes->view = SNESView_LS;
1057: snes->nwork = 0;
1059: PetscNew(SNES_LS,&neP);
1060: PetscLogObjectMemory(snes,sizeof(SNES_LS));
1061: snes->data = (void*)neP;
1062: neP->alpha = 1.e-4;
1063: neP->maxstep = 1.e8;
1064: neP->steptol = 1.e-12;
1065: neP->LineSearch = SNESCubicLineSearch;
1066: neP->lsP = PETSC_NULL;
1067: neP->CheckStep = PETSC_NULL;
1068: neP->checkP = PETSC_NULL;
1070: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESSetLineSearch_C","SNESSetLineSearch_LS",SNESSetLineSearch_LS);
1071: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESSetLineSearchCheck_C","SNESSetLineSearchCheck_LS",SNESSetLineSearchCheck_LS);
1073: return(0);
1074: }
1075: EXTERN_C_END