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