xref: /petsc/src/snes/impls/ls/ls.c (revision eafb4bcbebb1c0621d2677f4c5d850d03cbd72eb)
15e76c431SBarry Smith #ifndef lint
2*eafb4bcbSBarry Smith static char vcid[] = "$Id: ls.c,v 1.9 1995/04/18 16:26:03 bsmith Exp bsmith $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6fbe28522SBarry Smith #include "ls.h"
75e42470aSBarry Smith 
85e42470aSBarry Smith /*
95e42470aSBarry Smith      Implements a line search variant of Newton's Method
105e76c431SBarry Smith     for solving systems of nonlinear equations.
115e76c431SBarry Smith 
125e76c431SBarry Smith     Input parameters:
135e42470aSBarry Smith .   snes - nonlinear context obtained from SNESCreate()
145e76c431SBarry Smith 
155e42470aSBarry Smith     Output Parameters:
165e42470aSBarry Smith .   its  - Number of global iterations until termination.
175e76c431SBarry Smith 
185e76c431SBarry Smith     Notes:
195e42470aSBarry Smith     See SNESCreate() and SNESSetUp() for information on the definition and
205e76c431SBarry Smith     initialization of the nonlinear solver context.
215e76c431SBarry Smith 
225e76c431SBarry Smith     This implements essentially a truncated Newton method with a
235e76c431SBarry Smith     line search.  By default a cubic backtracking line search
245e76c431SBarry Smith     is employed, as described in the text "Numerical Methods for
255e76c431SBarry Smith     Unconstrained Optimization and Nonlinear Equations" by Dennis
265e42470aSBarry Smith     and Schnabel.  See the examples in src/snes/examples.
275e76c431SBarry Smith */
285e76c431SBarry Smith 
295e42470aSBarry Smith int SNESSolve_LS( SNES snes, int *outits )
305e76c431SBarry Smith {
315e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
326b5873e3SBarry Smith   int     maxits, i, history_len,ierr,lits;
336b5873e3SBarry Smith   double  fnorm, gnorm, xnorm, ynorm, *history;
345e42470aSBarry Smith   Vec     Y, X, F, G, W, TMP;
355e76c431SBarry Smith 
365e42470aSBarry Smith   history	= snes->conv_hist;	/* convergence history */
375e42470aSBarry Smith   history_len	= snes->conv_hist_len;	/* convergence history length */
385e42470aSBarry Smith   maxits	= snes->max_its;	/* maximum number of iterations */
395e42470aSBarry Smith   X		= snes->vec_sol;		/* solution vector */
405e42470aSBarry Smith   F		= snes->vec_res;		/* residual vector */
415e42470aSBarry Smith   Y		= snes->work[0];		/* work vectors */
425e42470aSBarry Smith   G		= snes->work[1];
435e42470aSBarry Smith   W		= snes->work[2];
445e76c431SBarry Smith 
455e42470aSBarry Smith   ierr = SNESComputeInitialGuess(snes,X); CHKERR(ierr);  /* X <- X_0 */
465e42470aSBarry Smith   VecNorm( X, &xnorm );		       /* xnorm = || X || */
47a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERR(ierr); /* (+/-) F(X) */
485e42470aSBarry Smith   VecNorm(F, &fnorm );	        	/* fnorm <- || F || */
495e42470aSBarry Smith   snes->norm = fnorm;
505e76c431SBarry Smith   if (history && history_len > 0) history[0] = fnorm;
51*eafb4bcbSBarry Smith   if (snes->Monitor) (*snes->Monitor)(snes,0,fnorm,snes->monP);
525e76c431SBarry Smith 
535e76c431SBarry Smith   for ( i=0; i<maxits; i++ ) {
545e42470aSBarry Smith        snes->iter = i+1;
555e76c431SBarry Smith 
565e76c431SBarry Smith        /* Solve J Y = -F, where J is Jacobian matrix */
575e42470aSBarry Smith        (*snes->ComputeJacobian)(X,&snes->jacobian,snes->jacP);
585e42470aSBarry Smith        ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian,0);
595e42470aSBarry Smith        ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERR(ierr);
605e42470aSBarry Smith        ierr = (*neP->LineSearch)(snes, X, F, G, Y, W, fnorm, &ynorm, &gnorm );
615e76c431SBarry Smith 
625e76c431SBarry Smith        TMP = F; F = G; G = TMP;
635e76c431SBarry Smith        TMP = X; X = Y; Y = TMP;
645e76c431SBarry Smith        fnorm = gnorm;
655e76c431SBarry Smith 
665e42470aSBarry Smith        snes->norm = fnorm;
675e76c431SBarry Smith        if (history && history_len > i+1) history[i+1] = fnorm;
685e42470aSBarry Smith        VecNorm( X, &xnorm );		/* xnorm = || X || */
69*eafb4bcbSBarry Smith        if (snes->Monitor) (*snes->Monitor)(snes,i+1,fnorm,snes->monP);
705e76c431SBarry Smith 
715e76c431SBarry Smith        /* Test for convergence */
725e42470aSBarry Smith        if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) {
735e42470aSBarry Smith            if (X != snes->vec_sol) VecCopy( X, snes->vec_sol );
745e76c431SBarry Smith            break;
755e76c431SBarry Smith        }
765e76c431SBarry Smith   }
775e76c431SBarry Smith   if (i == maxits) i--;
785e42470aSBarry Smith   *outits = i+1;
795e42470aSBarry Smith   return 0;
805e76c431SBarry Smith }
815e76c431SBarry Smith /* ------------------------------------------------------------ */
825e76c431SBarry Smith /*ARGSUSED*/
835e42470aSBarry Smith int SNESSetUp_LS(SNES snes )
845e76c431SBarry Smith {
855e42470aSBarry Smith   int ierr;
865e42470aSBarry Smith   snes->nwork = 3;
875e42470aSBarry Smith   ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr);
885e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work );
895e42470aSBarry Smith   return 0;
905e76c431SBarry Smith }
915e76c431SBarry Smith /* ------------------------------------------------------------ */
925e76c431SBarry Smith /*ARGSUSED*/
935e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
945e76c431SBarry Smith {
955e42470aSBarry Smith   SNES snes = (SNES) obj;
965e42470aSBarry Smith   SLESDestroy(snes->sles);
975e42470aSBarry Smith   VecFreeVecs(snes->work, snes->nwork );
985e42470aSBarry Smith   PLogObjectDestroy(obj);
995e42470aSBarry Smith   PETSCHEADERDESTROY(obj);
1005e42470aSBarry Smith   return 0;
1015e76c431SBarry Smith }
1025e42470aSBarry Smith /*@
1030de89847SLois Curfman McInnes    SNESDefaultMonitor - Default SNES monitoring routine.  Prints the
1045e42470aSBarry Smith    residual norm at each iteration.
1055e42470aSBarry Smith 
1065e42470aSBarry Smith    Input Parameters:
1070de89847SLois Curfman McInnes .  snes - the SNES context
1080de89847SLois Curfman McInnes .  its - iteration number
1090de89847SLois Curfman McInnes .  fnorm - 2-norm residual value (may be estimated)
1100de89847SLois Curfman McInnes .  dummy - unused context
1115e42470aSBarry Smith 
1125e42470aSBarry Smith    Notes:
1135e42470aSBarry Smith    f is either the residual or its negative, depending on the user's
114a9581db2SBarry Smith    preference, as set with SNESSetFunction().
115a67c8522SLois Curfman McInnes 
116a67c8522SLois Curfman McInnes .keywords: SNES, nonlinear, default, monitor, residual, norm
117a67c8522SLois Curfman McInnes 
118a67c8522SLois Curfman McInnes .seealso: SNESSetMonitor()
1195e42470aSBarry Smith @*/
120*eafb4bcbSBarry Smith int SNESDefaultMonitor(SNES snes,int its, double fnorm,void *dummy)
1215e42470aSBarry Smith {
1225e42470aSBarry Smith   fprintf( stdout, "iter = %d, residual norm %g \n",its,fnorm);
1235e42470aSBarry Smith   return 0;
1245e42470aSBarry Smith }
1255e42470aSBarry Smith 
1265e42470aSBarry Smith /*@
1275e42470aSBarry Smith    SNESDefaultConverged - Default test for monitoring the convergence
1280de89847SLois Curfman McInnes    of the SNES solvers.
1295e42470aSBarry Smith 
1305e42470aSBarry Smith    Input Parameters:
1310de89847SLois Curfman McInnes .  snes - the SNES context
1325e42470aSBarry Smith .  xnorm - 2-norm of current iterate
1335e42470aSBarry Smith .  pnorm - 2-norm of current step
1345e42470aSBarry Smith .  fnorm - 2-norm of residual
1350de89847SLois Curfman McInnes .  dummy - unused context
1365e42470aSBarry Smith 
1375e42470aSBarry Smith    Returns:
1385e42470aSBarry Smith $  2  if  ( fnorm < atol ),
1395e42470aSBarry Smith $  3  if  ( pnorm < xtol*xnorm ),
1405e42470aSBarry Smith $ -2  if  ( nres > max_res ),
1415e42470aSBarry Smith $  0  otherwise,
1425e42470aSBarry Smith 
1435e42470aSBarry Smith    where
1445e42470aSBarry Smith $    atol    - absolute residual norm tolerance,
1450de89847SLois Curfman McInnes $              set with SNESSetAbsoluteTolerance()
1465e42470aSBarry Smith $    max_res - maximum number of residual evaluations,
1470de89847SLois Curfman McInnes $              set with SNESSetMaxResidualEvaluations()
1485e42470aSBarry Smith $    nres    - number of residual evaluations
1495e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
1500de89847SLois Curfman McInnes $              set with SNESSetRelativeTolerance()
1515e42470aSBarry Smith 
1520de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence
1535e42470aSBarry Smith 
1540de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest()
1555e42470aSBarry Smith @*/
1565e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm,
1575e42470aSBarry Smith                          void *dummy)
1585e42470aSBarry Smith {
1595e42470aSBarry Smith   if (fnorm < snes->atol) {
160a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
161a67c8522SLois Curfman McInnes       "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol);
1625e42470aSBarry Smith     return 2;
1635e42470aSBarry Smith   }
1645e42470aSBarry Smith   if (pnorm < snes->xtol*(xnorm)) {
165a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
166a67c8522SLois Curfman McInnes       "Converged due to small update length  %g < %g*%g\n",
1675e42470aSBarry Smith        pnorm,snes->xtol,xnorm);
1685e42470aSBarry Smith     return 3;
1695e42470aSBarry Smith   }
170a67c8522SLois Curfman McInnes   if (snes->nresids > snes->max_resids) {
171a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
172a67c8522SLois Curfman McInnes       "Exceeded maximum number of residual evaluations: %d > %d\n",
173a67c8522SLois Curfman McInnes        snes->nresids, snes->max_resids );
174a67c8522SLois Curfman McInnes     return -2;
175a67c8522SLois Curfman McInnes   }
1765e42470aSBarry Smith   return 0;
1775e42470aSBarry Smith }
1785e42470aSBarry Smith 
1795e76c431SBarry Smith /* ------------------------------------------------------------ */
1805e76c431SBarry Smith /*ARGSUSED*/
1815e76c431SBarry Smith /*@
1825e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1835e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1845e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1855e76c431SBarry Smith 
1865e76c431SBarry Smith    Input Parameters:
1875e42470aSBarry Smith .  snes - nonlinear context
1885e76c431SBarry Smith .  x - current iterate
1895e76c431SBarry Smith .  f - residual evaluated at x
1905e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1915e76c431SBarry Smith .  w - work vector
1925e76c431SBarry Smith .  fnorm - 2-norm of f
1935e76c431SBarry Smith 
1945e76c431SBarry Smith    Output parameters:
1955e76c431SBarry Smith .  g - residual evaluated at new iterate y
1965e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1975e76c431SBarry Smith .  gnorm - 2-norm of g
1985e76c431SBarry Smith .  ynorm - 2-norm of search length
1995e76c431SBarry Smith 
2005e76c431SBarry Smith    Returns:
2015e76c431SBarry Smith    1, indicating success of the step.
2025e76c431SBarry Smith 
20328ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
20428ae5a21SLois Curfman McInnes 
205f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
206f59ffdedSLois Curfman McInnes .seealso: SNESSetLineSearchRoutine()
2075e76c431SBarry Smith @*/
2085e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
2095e42470aSBarry Smith                              double fnorm, double *ynorm, double *gnorm )
2105e76c431SBarry Smith {
2115e42470aSBarry Smith   int    ierr;
2125e42470aSBarry Smith   Scalar one = 1.0;
2135e42470aSBarry Smith   VecNorm(y, ynorm );	/* ynorm = || y ||    */
2145e42470aSBarry Smith   VecAXPY(&one, x, y );	/* y <- x + y         */
215a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,y,g); CHKERR(ierr);
2165e42470aSBarry Smith   VecNorm( g, gnorm ); 	/* gnorm = || g ||    */
2175e76c431SBarry Smith   return 1;
2185e76c431SBarry Smith }
2195e76c431SBarry Smith /* ------------------------------------------------------------------ */
2205e76c431SBarry Smith /*@
221f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
222f59ffdedSLois Curfman McInnes    is the default line search method.
2235e76c431SBarry Smith 
2245e76c431SBarry Smith    Input Parameters:
2255e42470aSBarry Smith .  snes - nonlinear context
2265e76c431SBarry Smith .  x - current iterate
2275e76c431SBarry Smith .  f - residual evaluated at x
2285e76c431SBarry Smith .  y - search direction (contains new iterate on output)
2295e76c431SBarry Smith .  w - work vector
2305e76c431SBarry Smith .  fnorm - 2-norm of f
2315e76c431SBarry Smith 
2325e76c431SBarry Smith    Output parameters:
2335e76c431SBarry Smith .  g - residual evaluated at new iterate y
2345e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2355e76c431SBarry Smith .  gnorm - 2-norm of g
2365e76c431SBarry Smith .  ynorm - 2-norm of search length
2375e76c431SBarry Smith 
2385e76c431SBarry Smith    Returns:
2395e76c431SBarry Smith    1 if the line search succeeds; 0 if the line search fails.
2405e76c431SBarry Smith 
2415e76c431SBarry Smith    Notes:
2425e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
2435e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
2445e76c431SBarry Smith 
24528ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
24628ae5a21SLois Curfman McInnes 
247f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
2485e76c431SBarry Smith @*/
2495e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
2505e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
2515e76c431SBarry Smith {
2525e42470aSBarry Smith   double  steptol, initslope;
2535e42470aSBarry Smith   double  lambdaprev, gnormprev;
2545e76c431SBarry Smith   double  a, b, d, t1, t2;
2556b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2565e42470aSBarry Smith   Scalar  cinitslope,clambda;
2576b5873e3SBarry Smith #endif
2585e42470aSBarry Smith   int     ierr,count;
2595e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2605e42470aSBarry Smith   Scalar  one = 1.0,scale;
2615e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2625e76c431SBarry Smith 
2635e76c431SBarry Smith   alpha   = neP->alpha;
2645e76c431SBarry Smith   maxstep = neP->maxstep;
2655e76c431SBarry Smith   steptol = neP->steptol;
2665e76c431SBarry Smith 
2675e42470aSBarry Smith   VecNorm(y, ynorm );
2685e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2695e42470aSBarry Smith     scale = maxstep/(*ynorm);
2706b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2716b5873e3SBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale));
2726b5873e3SBarry Smith #else
2735e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
2746b5873e3SBarry Smith #endif
2755e42470aSBarry Smith     VecScale(&scale, y );
2765e76c431SBarry Smith     *ynorm = maxstep;
2775e76c431SBarry Smith   }
2785e76c431SBarry Smith   minlambda = steptol/(*ynorm);
2795e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2805e42470aSBarry Smith   VecDot(f, y, &cinitslope );
2815e42470aSBarry Smith   initslope = real(cinitslope);
2825e42470aSBarry Smith #else
2835e42470aSBarry Smith   VecDot(f, y, &initslope );
2845e42470aSBarry Smith #endif
2855e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2865e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2875e76c431SBarry Smith 
2885e42470aSBarry Smith   VecCopy(y, w );
2895e42470aSBarry Smith   VecAXPY(&one, x, w );
290a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
2915e42470aSBarry Smith   VecNorm(g, gnorm );
2925e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
2935e42470aSBarry Smith       VecCopy(w, y );
2945e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
2955e42470aSBarry Smith       return 0;
2965e76c431SBarry Smith   }
2975e76c431SBarry Smith 
2985e76c431SBarry Smith   /* Fit points with quadratic */
2995e76c431SBarry Smith   lambda = 1.0; count = 0;
3005e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
3015e76c431SBarry Smith   lambdaprev = lambda;
3025e76c431SBarry Smith   gnormprev = *gnorm;
3035e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
3045e76c431SBarry Smith       lambda = .1*lambda;
3055e76c431SBarry Smith   } else lambda = lambdatemp;
3065e42470aSBarry Smith   VecCopy(x, w );
3075e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3085e42470aSBarry Smith   clambda = lambda; VecAXPY(&clambda, y, w );
3095e42470aSBarry Smith #else
3105e42470aSBarry Smith   VecAXPY(&lambda, y, w );
3115e42470aSBarry Smith #endif
312a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3135e42470aSBarry Smith   VecNorm(g, gnorm );
3145e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
3155e42470aSBarry Smith       VecCopy(w, y );
3165e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
3175e42470aSBarry Smith       return 0;
3185e76c431SBarry Smith   }
3195e76c431SBarry Smith 
3205e76c431SBarry Smith   /* Fit points with cubic */
3215e76c431SBarry Smith   count = 1;
3225e76c431SBarry Smith   while (1) {
3235e76c431SBarry Smith       if (lambda <= minlambda) { /* bad luck; use full step */
3245e42470aSBarry Smith            PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
3255e42470aSBarry Smith            PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
3265e76c431SBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
3275e42470aSBarry Smith            VecCopy(w, y );
3285e76c431SBarry Smith            return 0;
3295e76c431SBarry Smith       }
3305e76c431SBarry Smith       t1 = *gnorm - fnorm - lambda*initslope;
3315e76c431SBarry Smith       t2 = gnormprev  - fnorm - lambdaprev*initslope;
3325e76c431SBarry Smith       a = (t1/(lambda*lambda) -
3335e76c431SBarry Smith                 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3345e76c431SBarry Smith       b = (-lambdaprev*t1/(lambda*lambda) +
3355e76c431SBarry Smith                 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3365e76c431SBarry Smith       d = b*b - 3*a*initslope;
3375e76c431SBarry Smith       if (d < 0.0) d = 0.0;
3385e76c431SBarry Smith       if (a == 0.0) {
3395e76c431SBarry Smith          lambdatemp = -initslope/(2.0*b);
3405e76c431SBarry Smith       } else {
3415e76c431SBarry Smith          lambdatemp = (-b + sqrt(d))/(3.0*a);
3425e76c431SBarry Smith       }
3435e76c431SBarry Smith       if (lambdatemp > .5*lambda) {
3445e76c431SBarry Smith          lambdatemp = .5*lambda;
3455e76c431SBarry Smith       }
3465e76c431SBarry Smith       lambdaprev = lambda;
3475e76c431SBarry Smith       gnormprev = *gnorm;
3485e76c431SBarry Smith       if (lambdatemp <= .1*lambda) {
3495e76c431SBarry Smith          lambda = .1*lambda;
3505e76c431SBarry Smith       }
3515e76c431SBarry Smith       else lambda = lambdatemp;
3525e42470aSBarry Smith       VecCopy( x, w );
3535e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3545e42470aSBarry Smith       VecAXPY(&clambda, y, w );
3555e42470aSBarry Smith #else
3565e42470aSBarry Smith       VecAXPY(&lambda, y, w );
3575e42470aSBarry Smith #endif
358a9581db2SBarry Smith       ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3595e42470aSBarry Smith       VecNorm(g, gnorm );
3605e76c431SBarry Smith       if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
3615e42470aSBarry Smith          VecCopy(w, y );
3625e42470aSBarry Smith          PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
3635e42470aSBarry Smith          return 0;
3645e76c431SBarry Smith       }
3655e76c431SBarry Smith       count++;
3665e76c431SBarry Smith    }
3675e42470aSBarry Smith   return 0;
3685e76c431SBarry Smith }
3695e42470aSBarry Smith /*@
3705e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3715e76c431SBarry Smith 
3725e42470aSBarry Smith    Input Parameters:
373f59ffdedSLois Curfman McInnes .  snes - the SNES context
3745e42470aSBarry Smith .  x - current iterate
3755e42470aSBarry Smith .  f - residual evaluated at x
3765e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3775e42470aSBarry Smith .  w - work vector
3785e42470aSBarry Smith .  fnorm - 2-norm of f
3795e42470aSBarry Smith 
3805e42470aSBarry Smith    Output parameters:
3815e42470aSBarry Smith .  g - residual evaluated at new iterate y
3825e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3835e42470aSBarry Smith .  gnorm - 2-norm of g
3845e42470aSBarry Smith .  ynorm - 2-norm of search length
3855e42470aSBarry Smith 
3865e42470aSBarry Smith    Returns:
3875e42470aSBarry Smith    1 if the line search succeeds; 0 if the line search fails.
3885e42470aSBarry Smith 
3895e42470aSBarry Smith    Notes:
390f59ffdedSLois Curfman McInnes    Use SNESSetLineSearchRoutine()
391f59ffdedSLois Curfman McInnes    to set this routine within the SNES_NLS method.
3925e42470aSBarry Smith 
393f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
394f59ffdedSLois Curfman McInnes 
395f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
3965e42470aSBarry Smith @*/
3975e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
3985e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
3995e76c431SBarry Smith {
4005e42470aSBarry Smith   double  steptol, initslope;
4015e42470aSBarry Smith   double  lambdaprev, gnormprev;
4026b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
4035e42470aSBarry Smith   Scalar  cinitslope,clambda;
4046b5873e3SBarry Smith #endif
4055e42470aSBarry Smith   int     ierr,count;
4065e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
4075e42470aSBarry Smith   Scalar  one = 1.0,scale;
4085e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
4095e76c431SBarry Smith 
4105e42470aSBarry Smith   alpha   = neP->alpha;
4115e42470aSBarry Smith   maxstep = neP->maxstep;
4125e42470aSBarry Smith   steptol = neP->steptol;
4135e76c431SBarry Smith 
4145e42470aSBarry Smith   VecNorm(y, ynorm );
4155e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
4165e42470aSBarry Smith     scale = maxstep/(*ynorm);
4175e42470aSBarry Smith     VecScale(&scale, y );
4185e42470aSBarry Smith     *ynorm = maxstep;
4195e76c431SBarry Smith   }
4205e42470aSBarry Smith   minlambda = steptol/(*ynorm);
4215e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4225e42470aSBarry Smith   VecDot(f, y, &cinitslope );
4235e42470aSBarry Smith   initslope = real(cinitslope);
4245e42470aSBarry Smith #else
4255e42470aSBarry Smith   VecDot(f, y, &initslope );
4265e42470aSBarry Smith #endif
4275e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
4285e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
4295e42470aSBarry Smith 
4305e42470aSBarry Smith   VecCopy(y, w );
4315e42470aSBarry Smith   VecAXPY(&one, x, w );
432a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
4335e42470aSBarry Smith   VecNorm(g, gnorm );
4345e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
4355e42470aSBarry Smith       VecCopy(w, y );
4365e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
4375e42470aSBarry Smith       return 0;
4385e42470aSBarry Smith   }
4395e42470aSBarry Smith 
4405e42470aSBarry Smith   /* Fit points with quadratic */
4415e42470aSBarry Smith   lambda = 1.0; count = 0;
4425e42470aSBarry Smith   count = 1;
4435e42470aSBarry Smith   while (1) {
4445e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
4455e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
4465e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
4475e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
4485e42470aSBarry Smith       VecCopy(w, y );
4495e42470aSBarry Smith       return 0;
4505e42470aSBarry Smith     }
4515e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4525e42470aSBarry Smith     lambdaprev = lambda;
4535e42470aSBarry Smith     gnormprev = *gnorm;
4545e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4555e42470aSBarry Smith       lambda = .1*lambda;
4565e42470aSBarry Smith     } else lambda = lambdatemp;
4575e42470aSBarry Smith     VecCopy(x, w );
4585e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4595e42470aSBarry Smith     clambda = lambda; VecAXPY(&clambda, y, w );
4605e42470aSBarry Smith #else
4615e42470aSBarry Smith     VecAXPY(&lambda, y, w );
4625e42470aSBarry Smith #endif
463a9581db2SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
4645e42470aSBarry Smith     VecNorm(g, gnorm );
4655e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
4665e42470aSBarry Smith       VecCopy(w, y );
4675e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
4685e42470aSBarry Smith       return 0;
4695e42470aSBarry Smith     }
4705e42470aSBarry Smith     count++;
4715e42470aSBarry Smith   }
4725e42470aSBarry Smith 
4735e42470aSBarry Smith   return 0;
4745e76c431SBarry Smith }
4755e76c431SBarry Smith /* ------------------------------------------------------------ */
4765e76c431SBarry Smith /*@C
4775e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
478fbe28522SBarry Smith    by the method SNES_LS.
4795e76c431SBarry Smith 
4805e76c431SBarry Smith    Input Parameters:
481*eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
4825e76c431SBarry Smith .  func - pointer to int function
4835e76c431SBarry Smith 
4845e76c431SBarry Smith    Possible routines:
485f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
486f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
487f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
4885e76c431SBarry Smith 
4895e76c431SBarry Smith    Calling sequence of func:
490f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
4915e42470aSBarry Smith          Vec w, double fnorm, double *ynorm, double *gnorm)
4925e76c431SBarry Smith 
4935e76c431SBarry Smith     Input parameters for func:
4945e42470aSBarry Smith .   snes - nonlinear context
4955e76c431SBarry Smith .   x - current iterate
4965e76c431SBarry Smith .   f - residual evaluated at x
4975e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4985e76c431SBarry Smith .   w - work vector
4995e76c431SBarry Smith .   fnorm - 2-norm of f
5005e76c431SBarry Smith 
5015e76c431SBarry Smith     Output parameters for func:
5025e76c431SBarry Smith .   g - residual evaluated at new iterate y
5035e76c431SBarry Smith .   y - new iterate (contains search direction on input)
5045e76c431SBarry Smith .   gnorm - 2-norm of g
5055e76c431SBarry Smith .   ynorm - 2-norm of search length
5065e76c431SBarry Smith 
5075e76c431SBarry Smith     Returned by func:
5085e76c431SBarry Smith     1 if the line search succeeds; 0 if the line search fails.
509f59ffdedSLois Curfman McInnes 
510f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
511f59ffdedSLois Curfman McInnes 
512f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
5135e76c431SBarry Smith @*/
5145e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
5155e42470aSBarry Smith                              double,double *,double*) )
5165e76c431SBarry Smith {
517fbe28522SBarry Smith   if ((snes)->type == SNES_NLS)
5185e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
5195e42470aSBarry Smith   return 0;
5205e76c431SBarry Smith }
5215e42470aSBarry Smith 
5225e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
5235e42470aSBarry Smith {
5245e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5255e42470aSBarry Smith   fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n");
5265e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha);
5275e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep);
5285e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol);
5296b5873e3SBarry Smith   return 0;
5305e42470aSBarry Smith }
5315e42470aSBarry Smith 
5326b5873e3SBarry Smith #include "options.h"
5335e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5345e42470aSBarry Smith {
5355e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5365e42470aSBarry Smith   char    ver[16];
5375e42470aSBarry Smith   double  tmp;
5385e42470aSBarry Smith 
5395e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_alpa",&tmp)) {
5405e42470aSBarry Smith     ls->alpha = tmp;
5415e42470aSBarry Smith   }
5425e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_maxstep",&tmp)) {
5435e42470aSBarry Smith     ls->maxstep = tmp;
5445e42470aSBarry Smith   }
5455e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_steptol",&tmp)) {
5465e42470aSBarry Smith     ls->steptol = tmp;
5475e42470aSBarry Smith   }
5485e42470aSBarry Smith   if (OptionsGetString(0,snes->prefix,"-snes_line_search",ver,16)) {
5495e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
5505e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
5515e42470aSBarry Smith     }
5525e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
5535e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
5545e42470aSBarry Smith     }
5555e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
5565e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
5575e42470aSBarry Smith     }
5585e42470aSBarry Smith     else {SETERR(1,"Unknown line search?");}
5595e42470aSBarry Smith   }
5605e42470aSBarry Smith   return 0;
5615e42470aSBarry Smith }
5625e42470aSBarry Smith 
5635e42470aSBarry Smith /* ------------------------------------------------------------ */
5645e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
5655e42470aSBarry Smith {
5665e42470aSBarry Smith   SNES_LS *neP;
5675e42470aSBarry Smith 
568fbe28522SBarry Smith   snes->type		= SNES_NLS;
5695e42470aSBarry Smith   snes->Setup		= SNESSetUp_LS;
5705e42470aSBarry Smith   snes->Solver		= SNESSolve_LS;
5715e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
5725e42470aSBarry Smith   snes->Converged	= SNESDefaultConverged;
5735e42470aSBarry Smith   snes->PrintHelp       = SNESPrintHelp_LS;
5745e42470aSBarry Smith   snes->SetFromOptions  = SNESSetFromOptions_LS;
5755e42470aSBarry Smith 
5765e42470aSBarry Smith   neP			= NEW(SNES_LS);   CHKPTR(neP);
5775e42470aSBarry Smith   snes->data    	= (void *) neP;
5785e42470aSBarry Smith   neP->alpha		= 1.e-4;
5795e42470aSBarry Smith   neP->maxstep		= 1.e8;
5805e42470aSBarry Smith   neP->steptol		= 1.e-12;
5815e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
5825e42470aSBarry Smith   return 0;
5835e42470aSBarry Smith }
5845e42470aSBarry Smith 
5855e42470aSBarry Smith 
5865e42470aSBarry Smith 
5875e42470aSBarry Smith 
588