xref: /petsc/src/snes/impls/ls/ls.c (revision 49e3953d2ddc48db66efb706eff557e010a5e7a0)
15e76c431SBarry Smith #ifndef lint
2*49e3953dSBarry Smith static char vcid[] = "$Id: ls.c,v 1.13 1995/05/05 03:51:33 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;
3223242f5aSBarry Smith   int     maxits, i, history_len,ierr,lits,flg;
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 */
4039e2f89bSBarry Smith   F		= snes->vec_func;		/* 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;
51eafb4bcbSBarry 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 */
5723242f5aSBarry Smith        (*snes->ComputeJacobian)(snes,X,&snes->jacobian,&snes->jacobian_pre,
5823242f5aSBarry Smith                                 &flg,snes->jacP);
5923242f5aSBarry Smith        ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg);
605e42470aSBarry Smith        ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERR(ierr);
615e42470aSBarry Smith        ierr = (*neP->LineSearch)(snes, X, F, G, Y, W, fnorm, &ynorm, &gnorm );
6223242f5aSBarry Smith        CHKERR(ierr);
635e76c431SBarry Smith 
6439e2f89bSBarry Smith        TMP = F; F = G; snes->vec_func_always = F; G = TMP;
6539e2f89bSBarry Smith        TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
665e76c431SBarry Smith        fnorm = gnorm;
675e76c431SBarry Smith 
685e42470aSBarry Smith        snes->norm = fnorm;
695e76c431SBarry Smith        if (history && history_len > i+1) history[i+1] = fnorm;
705e42470aSBarry Smith        VecNorm( X, &xnorm );		/* xnorm = || X || */
71eafb4bcbSBarry Smith        if (snes->Monitor) (*snes->Monitor)(snes,i+1,fnorm,snes->monP);
725e76c431SBarry Smith 
735e76c431SBarry Smith        /* Test for convergence */
745e42470aSBarry Smith        if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) {
7539e2f89bSBarry Smith            if (X != snes->vec_sol) {
7639e2f89bSBarry Smith              VecCopy( X, snes->vec_sol );
7739e2f89bSBarry Smith              snes->vec_sol_always = snes->vec_sol;
7839e2f89bSBarry Smith              snes->vec_func_always = snes->vec_func;
7939e2f89bSBarry Smith            }
805e76c431SBarry Smith            break;
815e76c431SBarry Smith        }
825e76c431SBarry Smith   }
835e76c431SBarry Smith   if (i == maxits) i--;
845e42470aSBarry Smith   *outits = i+1;
855e42470aSBarry Smith   return 0;
865e76c431SBarry Smith }
875e76c431SBarry Smith /* ------------------------------------------------------------ */
885e76c431SBarry Smith /*ARGSUSED*/
895e42470aSBarry Smith int SNESSetUp_LS(SNES snes )
905e76c431SBarry Smith {
915e42470aSBarry Smith   int ierr;
925e42470aSBarry Smith   snes->nwork = 3;
935e42470aSBarry Smith   ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr);
945e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work );
955e42470aSBarry Smith   return 0;
965e76c431SBarry Smith }
975e76c431SBarry Smith /* ------------------------------------------------------------ */
985e76c431SBarry Smith /*ARGSUSED*/
995e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
1005e76c431SBarry Smith {
1015e42470aSBarry Smith   SNES snes = (SNES) obj;
1025e42470aSBarry Smith   SLESDestroy(snes->sles);
1035e42470aSBarry Smith   VecFreeVecs(snes->work, snes->nwork );
1045e42470aSBarry Smith   PLogObjectDestroy(obj);
1055e42470aSBarry Smith   PETSCHEADERDESTROY(obj);
1065e42470aSBarry Smith   return 0;
1075e76c431SBarry Smith }
1085e42470aSBarry Smith /*@
1090de89847SLois Curfman McInnes    SNESDefaultMonitor - Default SNES monitoring routine.  Prints the
1105e42470aSBarry Smith    residual norm at each iteration.
1115e42470aSBarry Smith 
1125e42470aSBarry Smith    Input Parameters:
1130de89847SLois Curfman McInnes .  snes - the SNES context
1140de89847SLois Curfman McInnes .  its - iteration number
1150de89847SLois Curfman McInnes .  fnorm - 2-norm residual value (may be estimated)
1160de89847SLois Curfman McInnes .  dummy - unused context
1175e42470aSBarry Smith 
1185e42470aSBarry Smith    Notes:
1195e42470aSBarry Smith    f is either the residual or its negative, depending on the user's
120a9581db2SBarry Smith    preference, as set with SNESSetFunction().
121a67c8522SLois Curfman McInnes 
122a67c8522SLois Curfman McInnes .keywords: SNES, nonlinear, default, monitor, residual, norm
123a67c8522SLois Curfman McInnes 
124a67c8522SLois Curfman McInnes .seealso: SNESSetMonitor()
1255e42470aSBarry Smith @*/
126eafb4bcbSBarry Smith int SNESDefaultMonitor(SNES snes,int its, double fnorm,void *dummy)
1275e42470aSBarry Smith {
12839e2f89bSBarry Smith   fprintf( stdout, "iter = %d, Function norm %g \n",its,fnorm);
1295e42470aSBarry Smith   return 0;
1305e42470aSBarry Smith }
1315e42470aSBarry Smith 
1325e42470aSBarry Smith /*@
1335e42470aSBarry Smith    SNESDefaultConverged - Default test for monitoring the convergence
1340de89847SLois Curfman McInnes    of the SNES solvers.
1355e42470aSBarry Smith 
1365e42470aSBarry Smith    Input Parameters:
1370de89847SLois Curfman McInnes .  snes - the SNES context
1385e42470aSBarry Smith .  xnorm - 2-norm of current iterate
1395e42470aSBarry Smith .  pnorm - 2-norm of current step
1405e42470aSBarry Smith .  fnorm - 2-norm of residual
1410de89847SLois Curfman McInnes .  dummy - unused context
1425e42470aSBarry Smith 
1435e42470aSBarry Smith    Returns:
1445e42470aSBarry Smith $  2  if  ( fnorm < atol ),
1455e42470aSBarry Smith $  3  if  ( pnorm < xtol*xnorm ),
1465e42470aSBarry Smith $ -2  if  ( nres > max_res ),
1475e42470aSBarry Smith $  0  otherwise,
1485e42470aSBarry Smith 
1495e42470aSBarry Smith    where
1505e42470aSBarry Smith $    atol    - absolute residual norm tolerance,
1510de89847SLois Curfman McInnes $              set with SNESSetAbsoluteTolerance()
1525e42470aSBarry Smith $    max_res - maximum number of residual evaluations,
1530de89847SLois Curfman McInnes $              set with SNESSetMaxResidualEvaluations()
1545e42470aSBarry Smith $    nres    - number of residual evaluations
1555e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
1560de89847SLois Curfman McInnes $              set with SNESSetRelativeTolerance()
1575e42470aSBarry Smith 
1580de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence
1595e42470aSBarry Smith 
1600de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest()
1615e42470aSBarry Smith @*/
1625e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm,
1635e42470aSBarry Smith                          void *dummy)
1645e42470aSBarry Smith {
1655e42470aSBarry Smith   if (fnorm < snes->atol) {
166a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
167a67c8522SLois Curfman McInnes       "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol);
1685e42470aSBarry Smith     return 2;
1695e42470aSBarry Smith   }
1705e42470aSBarry Smith   if (pnorm < snes->xtol*(xnorm)) {
171a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
172a67c8522SLois Curfman McInnes       "Converged due to small update length  %g < %g*%g\n",
1735e42470aSBarry Smith        pnorm,snes->xtol,xnorm);
1745e42470aSBarry Smith     return 3;
1755e42470aSBarry Smith   }
176*49e3953dSBarry Smith   if (snes->nfuncs > snes->max_funcs) {
177a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
178a67c8522SLois Curfman McInnes       "Exceeded maximum number of residual evaluations: %d > %d\n",
179*49e3953dSBarry Smith        snes->nfuncs, snes->max_funcs );
180a67c8522SLois Curfman McInnes     return -2;
181a67c8522SLois Curfman McInnes   }
1825e42470aSBarry Smith   return 0;
1835e42470aSBarry Smith }
1845e42470aSBarry Smith 
1855e76c431SBarry Smith /* ------------------------------------------------------------ */
1865e76c431SBarry Smith /*ARGSUSED*/
1875e76c431SBarry Smith /*@
1885e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1895e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1905e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1915e76c431SBarry Smith 
1925e76c431SBarry Smith    Input Parameters:
1935e42470aSBarry Smith .  snes - nonlinear context
1945e76c431SBarry Smith .  x - current iterate
1955e76c431SBarry Smith .  f - residual evaluated at x
1965e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1975e76c431SBarry Smith .  w - work vector
1985e76c431SBarry Smith .  fnorm - 2-norm of f
1995e76c431SBarry Smith 
2005e76c431SBarry Smith    Output parameters:
2015e76c431SBarry Smith .  g - residual evaluated at new iterate y
2025e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2035e76c431SBarry Smith .  gnorm - 2-norm of g
2045e76c431SBarry Smith .  ynorm - 2-norm of search length
2055e76c431SBarry Smith 
2065e76c431SBarry Smith    Returns:
2075e76c431SBarry Smith    1, indicating success of the step.
2085e76c431SBarry Smith 
20928ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
21028ae5a21SLois Curfman McInnes 
211f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
212f59ffdedSLois Curfman McInnes .seealso: SNESSetLineSearchRoutine()
2135e76c431SBarry Smith @*/
2145e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
2155e42470aSBarry Smith                              double fnorm, double *ynorm, double *gnorm )
2165e76c431SBarry Smith {
2175e42470aSBarry Smith   int    ierr;
2185e42470aSBarry Smith   Scalar one = 1.0;
2197857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
2205e42470aSBarry Smith   VecNorm(y, ynorm );	/* ynorm = || y ||    */
2215e42470aSBarry Smith   VecAXPY(&one, x, y );	/* y <- x + y         */
222a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,y,g); CHKERR(ierr);
2235e42470aSBarry Smith   VecNorm( g, gnorm ); 	/* gnorm = || g ||    */
2247857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
2255e76c431SBarry Smith   return 1;
2265e76c431SBarry Smith }
2275e76c431SBarry Smith /* ------------------------------------------------------------------ */
2285e76c431SBarry Smith /*@
229f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
230f59ffdedSLois Curfman McInnes    is the default line search method.
2315e76c431SBarry Smith 
2325e76c431SBarry Smith    Input Parameters:
2335e42470aSBarry Smith .  snes - nonlinear context
2345e76c431SBarry Smith .  x - current iterate
2355e76c431SBarry Smith .  f - residual evaluated at x
2365e76c431SBarry Smith .  y - search direction (contains new iterate on output)
2375e76c431SBarry Smith .  w - work vector
2385e76c431SBarry Smith .  fnorm - 2-norm of f
2395e76c431SBarry Smith 
2405e76c431SBarry Smith    Output parameters:
2415e76c431SBarry Smith .  g - residual evaluated at new iterate y
2425e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2435e76c431SBarry Smith .  gnorm - 2-norm of g
2445e76c431SBarry Smith .  ynorm - 2-norm of search length
2455e76c431SBarry Smith 
2465e76c431SBarry Smith    Returns:
2475e76c431SBarry Smith    1 if the line search succeeds; 0 if the line search fails.
2485e76c431SBarry Smith 
2495e76c431SBarry Smith    Notes:
2505e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
2515e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
2525e76c431SBarry Smith 
25328ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
25428ae5a21SLois Curfman McInnes 
255f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
2565e76c431SBarry Smith @*/
2575e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
2585e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
2595e76c431SBarry Smith {
2605e42470aSBarry Smith   double  steptol, initslope;
2615e42470aSBarry Smith   double  lambdaprev, gnormprev;
2625e76c431SBarry Smith   double  a, b, d, t1, t2;
2636b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2645e42470aSBarry Smith   Scalar  cinitslope,clambda;
2656b5873e3SBarry Smith #endif
2665e42470aSBarry Smith   int     ierr,count;
2675e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2685e42470aSBarry Smith   Scalar  one = 1.0,scale;
2695e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2705e76c431SBarry Smith 
2717857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
2725e76c431SBarry Smith   alpha   = neP->alpha;
2735e76c431SBarry Smith   maxstep = neP->maxstep;
2745e76c431SBarry Smith   steptol = neP->steptol;
2755e76c431SBarry Smith 
2765e42470aSBarry Smith   VecNorm(y, ynorm );
2775e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2785e42470aSBarry Smith     scale = maxstep/(*ynorm);
2796b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2806b5873e3SBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale));
2816b5873e3SBarry Smith #else
2825e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
2836b5873e3SBarry Smith #endif
2845e42470aSBarry Smith     VecScale(&scale, y );
2855e76c431SBarry Smith     *ynorm = maxstep;
2865e76c431SBarry Smith   }
2875e76c431SBarry Smith   minlambda = steptol/(*ynorm);
2885e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2895e42470aSBarry Smith   VecDot(f, y, &cinitslope );
2905e42470aSBarry Smith   initslope = real(cinitslope);
2915e42470aSBarry Smith #else
2925e42470aSBarry Smith   VecDot(f, y, &initslope );
2935e42470aSBarry Smith #endif
2945e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2955e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2965e76c431SBarry Smith 
2975e42470aSBarry Smith   VecCopy(y, w );
2985e42470aSBarry Smith   VecAXPY(&one, x, w );
299a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3005e42470aSBarry Smith   VecNorm(g, gnorm );
3015e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
3025e42470aSBarry Smith       VecCopy(w, y );
3035e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
3047857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3055e42470aSBarry Smith       return 0;
3065e76c431SBarry Smith   }
3075e76c431SBarry Smith 
3085e76c431SBarry Smith   /* Fit points with quadratic */
3095e76c431SBarry Smith   lambda = 1.0; count = 0;
3105e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
3115e76c431SBarry Smith   lambdaprev = lambda;
3125e76c431SBarry Smith   gnormprev = *gnorm;
3135e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
3145e76c431SBarry Smith       lambda = .1*lambda;
3155e76c431SBarry Smith   } else lambda = lambdatemp;
3165e42470aSBarry Smith   VecCopy(x, w );
3175e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3185e42470aSBarry Smith   clambda = lambda; VecAXPY(&clambda, y, w );
3195e42470aSBarry Smith #else
3205e42470aSBarry Smith   VecAXPY(&lambda, y, w );
3215e42470aSBarry Smith #endif
322a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3235e42470aSBarry Smith   VecNorm(g, gnorm );
3245e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
3255e42470aSBarry Smith       VecCopy(w, y );
3265e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
3277857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3285e42470aSBarry Smith       return 0;
3295e76c431SBarry Smith   }
3305e76c431SBarry Smith 
3315e76c431SBarry Smith   /* Fit points with cubic */
3325e76c431SBarry Smith   count = 1;
3335e76c431SBarry Smith   while (1) {
3345e76c431SBarry Smith       if (lambda <= minlambda) { /* bad luck; use full step */
3355e42470aSBarry Smith            PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
3365e42470aSBarry Smith            PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
3375e76c431SBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
3385e42470aSBarry Smith            VecCopy(w, y );
3397857610eSBarry Smith            PLogEventEnd(SNES_LineSearch,snes,x,f,g);
34023242f5aSBarry Smith            return -1;
3415e76c431SBarry Smith       }
3425e76c431SBarry Smith       t1 = *gnorm - fnorm - lambda*initslope;
3435e76c431SBarry Smith       t2 = gnormprev  - fnorm - lambdaprev*initslope;
3445e76c431SBarry Smith       a = (t1/(lambda*lambda) -
3455e76c431SBarry Smith                 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3465e76c431SBarry Smith       b = (-lambdaprev*t1/(lambda*lambda) +
3475e76c431SBarry Smith                 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3485e76c431SBarry Smith       d = b*b - 3*a*initslope;
3495e76c431SBarry Smith       if (d < 0.0) d = 0.0;
3505e76c431SBarry Smith       if (a == 0.0) {
3515e76c431SBarry Smith          lambdatemp = -initslope/(2.0*b);
3525e76c431SBarry Smith       } else {
3535e76c431SBarry Smith          lambdatemp = (-b + sqrt(d))/(3.0*a);
3545e76c431SBarry Smith       }
3555e76c431SBarry Smith       if (lambdatemp > .5*lambda) {
3565e76c431SBarry Smith          lambdatemp = .5*lambda;
3575e76c431SBarry Smith       }
3585e76c431SBarry Smith       lambdaprev = lambda;
3595e76c431SBarry Smith       gnormprev = *gnorm;
3605e76c431SBarry Smith       if (lambdatemp <= .1*lambda) {
3615e76c431SBarry Smith          lambda = .1*lambda;
3625e76c431SBarry Smith       }
3635e76c431SBarry Smith       else lambda = lambdatemp;
3645e42470aSBarry Smith       VecCopy( x, w );
3655e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3665e42470aSBarry Smith       VecAXPY(&clambda, y, w );
3675e42470aSBarry Smith #else
3685e42470aSBarry Smith       VecAXPY(&lambda, y, w );
3695e42470aSBarry Smith #endif
370a9581db2SBarry Smith       ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3715e42470aSBarry Smith       VecNorm(g, gnorm );
3725e76c431SBarry Smith       if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
3735e42470aSBarry Smith          VecCopy(w, y );
3745e42470aSBarry Smith          PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
3757857610eSBarry Smith          PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3765e42470aSBarry Smith          return 0;
3775e76c431SBarry Smith       }
3785e76c431SBarry Smith       count++;
3795e76c431SBarry Smith    }
3807857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3815e42470aSBarry Smith   return 0;
3825e76c431SBarry Smith }
3835e42470aSBarry Smith /*@
3845e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3855e76c431SBarry Smith 
3865e42470aSBarry Smith    Input Parameters:
387f59ffdedSLois Curfman McInnes .  snes - the SNES context
3885e42470aSBarry Smith .  x - current iterate
3895e42470aSBarry Smith .  f - residual evaluated at x
3905e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3915e42470aSBarry Smith .  w - work vector
3925e42470aSBarry Smith .  fnorm - 2-norm of f
3935e42470aSBarry Smith 
3945e42470aSBarry Smith    Output parameters:
3955e42470aSBarry Smith .  g - residual evaluated at new iterate y
3965e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3975e42470aSBarry Smith .  gnorm - 2-norm of g
3985e42470aSBarry Smith .  ynorm - 2-norm of search length
3995e42470aSBarry Smith 
4005e42470aSBarry Smith    Returns:
4015e42470aSBarry Smith    1 if the line search succeeds; 0 if the line search fails.
4025e42470aSBarry Smith 
4035e42470aSBarry Smith    Notes:
404f59ffdedSLois Curfman McInnes    Use SNESSetLineSearchRoutine()
405f59ffdedSLois Curfman McInnes    to set this routine within the SNES_NLS method.
4065e42470aSBarry Smith 
407f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
408f59ffdedSLois Curfman McInnes 
409f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
4105e42470aSBarry Smith @*/
4115e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
4125e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
4135e76c431SBarry Smith {
4145e42470aSBarry Smith   double  steptol, initslope;
4155e42470aSBarry Smith   double  lambdaprev, gnormprev;
4166b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
4175e42470aSBarry Smith   Scalar  cinitslope,clambda;
4186b5873e3SBarry Smith #endif
4195e42470aSBarry Smith   int     ierr,count;
4205e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
4215e42470aSBarry Smith   Scalar  one = 1.0,scale;
4225e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
4235e76c431SBarry Smith 
4247857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
4255e42470aSBarry Smith   alpha   = neP->alpha;
4265e42470aSBarry Smith   maxstep = neP->maxstep;
4275e42470aSBarry Smith   steptol = neP->steptol;
4285e76c431SBarry Smith 
4295e42470aSBarry Smith   VecNorm(y, ynorm );
4305e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
4315e42470aSBarry Smith     scale = maxstep/(*ynorm);
4325e42470aSBarry Smith     VecScale(&scale, y );
4335e42470aSBarry Smith     *ynorm = maxstep;
4345e76c431SBarry Smith   }
4355e42470aSBarry Smith   minlambda = steptol/(*ynorm);
4365e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4375e42470aSBarry Smith   VecDot(f, y, &cinitslope );
4385e42470aSBarry Smith   initslope = real(cinitslope);
4395e42470aSBarry Smith #else
4405e42470aSBarry Smith   VecDot(f, y, &initslope );
4415e42470aSBarry Smith #endif
4425e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
4435e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
4445e42470aSBarry Smith 
4455e42470aSBarry Smith   VecCopy(y, w );
4465e42470aSBarry Smith   VecAXPY(&one, x, w );
447a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
4485e42470aSBarry Smith   VecNorm(g, gnorm );
4495e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
4505e42470aSBarry Smith       VecCopy(w, y );
4515e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
4527857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4535e42470aSBarry Smith       return 0;
4545e42470aSBarry Smith   }
4555e42470aSBarry Smith 
4565e42470aSBarry Smith   /* Fit points with quadratic */
4575e42470aSBarry Smith   lambda = 1.0; count = 0;
4585e42470aSBarry Smith   count = 1;
4595e42470aSBarry Smith   while (1) {
4605e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
4615e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
4625e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
4635e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
4645e42470aSBarry Smith       VecCopy(w, y );
4657857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4665e42470aSBarry Smith       return 0;
4675e42470aSBarry Smith     }
4685e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4695e42470aSBarry Smith     lambdaprev = lambda;
4705e42470aSBarry Smith     gnormprev = *gnorm;
4715e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4725e42470aSBarry Smith       lambda = .1*lambda;
4735e42470aSBarry Smith     } else lambda = lambdatemp;
4745e42470aSBarry Smith     VecCopy(x, w );
4755e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4765e42470aSBarry Smith     clambda = lambda; VecAXPY(&clambda, y, w );
4775e42470aSBarry Smith #else
4785e42470aSBarry Smith     VecAXPY(&lambda, y, w );
4795e42470aSBarry Smith #endif
480a9581db2SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
4815e42470aSBarry Smith     VecNorm(g, gnorm );
4825e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
4835e42470aSBarry Smith       VecCopy(w, y );
4845e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
4857857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4865e42470aSBarry Smith       return 0;
4875e42470aSBarry Smith     }
4885e42470aSBarry Smith     count++;
4895e42470aSBarry Smith   }
4905e42470aSBarry Smith 
4917857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4925e42470aSBarry Smith   return 0;
4935e76c431SBarry Smith }
4945e76c431SBarry Smith /* ------------------------------------------------------------ */
4955e76c431SBarry Smith /*@C
4965e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
497fbe28522SBarry Smith    by the method SNES_LS.
4985e76c431SBarry Smith 
4995e76c431SBarry Smith    Input Parameters:
500eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
5015e76c431SBarry Smith .  func - pointer to int function
5025e76c431SBarry Smith 
5035e76c431SBarry Smith    Possible routines:
504f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
505f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
506f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
5075e76c431SBarry Smith 
5085e76c431SBarry Smith    Calling sequence of func:
509f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
5105e42470aSBarry Smith          Vec w, double fnorm, double *ynorm, double *gnorm)
5115e76c431SBarry Smith 
5125e76c431SBarry Smith     Input parameters for func:
5135e42470aSBarry Smith .   snes - nonlinear context
5145e76c431SBarry Smith .   x - current iterate
5155e76c431SBarry Smith .   f - residual evaluated at x
5165e76c431SBarry Smith .   y - search direction (contains new iterate on output)
5175e76c431SBarry Smith .   w - work vector
5185e76c431SBarry Smith .   fnorm - 2-norm of f
5195e76c431SBarry Smith 
5205e76c431SBarry Smith     Output parameters for func:
5215e76c431SBarry Smith .   g - residual evaluated at new iterate y
5225e76c431SBarry Smith .   y - new iterate (contains search direction on input)
5235e76c431SBarry Smith .   gnorm - 2-norm of g
5245e76c431SBarry Smith .   ynorm - 2-norm of search length
5255e76c431SBarry Smith 
5265e76c431SBarry Smith     Returned by func:
5275e76c431SBarry Smith     1 if the line search succeeds; 0 if the line search fails.
528f59ffdedSLois Curfman McInnes 
529f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
530f59ffdedSLois Curfman McInnes 
531f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
5325e76c431SBarry Smith @*/
5335e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
5345e42470aSBarry Smith                              double,double *,double*) )
5355e76c431SBarry Smith {
536fbe28522SBarry Smith   if ((snes)->type == SNES_NLS)
5375e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
5385e42470aSBarry Smith   return 0;
5395e76c431SBarry Smith }
5405e42470aSBarry Smith 
5415e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
5425e42470aSBarry Smith {
5435e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5445e42470aSBarry Smith   fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n");
5455e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha);
5465e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep);
5475e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol);
5486b5873e3SBarry Smith   return 0;
5495e42470aSBarry Smith }
5505e42470aSBarry Smith 
5516b5873e3SBarry Smith #include "options.h"
5525e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5535e42470aSBarry Smith {
5545e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5555e42470aSBarry Smith   char    ver[16];
5565e42470aSBarry Smith   double  tmp;
5575e42470aSBarry Smith 
5585e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_alpa",&tmp)) {
5595e42470aSBarry Smith     ls->alpha = tmp;
5605e42470aSBarry Smith   }
5615e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_maxstep",&tmp)) {
5625e42470aSBarry Smith     ls->maxstep = tmp;
5635e42470aSBarry Smith   }
5645e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_steptol",&tmp)) {
5655e42470aSBarry Smith     ls->steptol = tmp;
5665e42470aSBarry Smith   }
5675e42470aSBarry Smith   if (OptionsGetString(0,snes->prefix,"-snes_line_search",ver,16)) {
5685e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
5695e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
5705e42470aSBarry Smith     }
5715e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
5725e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
5735e42470aSBarry Smith     }
5745e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
5755e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
5765e42470aSBarry Smith     }
5775e42470aSBarry Smith     else {SETERR(1,"Unknown line search?");}
5785e42470aSBarry Smith   }
5795e42470aSBarry Smith   return 0;
5805e42470aSBarry Smith }
5815e42470aSBarry Smith 
5825e42470aSBarry Smith /* ------------------------------------------------------------ */
5835e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
5845e42470aSBarry Smith {
5855e42470aSBarry Smith   SNES_LS *neP;
5865e42470aSBarry Smith 
587fbe28522SBarry Smith   snes->type		= SNES_NLS;
588*49e3953dSBarry Smith   snes->setup		= SNESSetUp_LS;
589*49e3953dSBarry Smith   snes->solve		= SNESSolve_LS;
5905e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
5915e42470aSBarry Smith   snes->Converged	= SNESDefaultConverged;
592*49e3953dSBarry Smith   snes->printhelp       = SNESPrintHelp_LS;
593*49e3953dSBarry Smith   snes->setfromoptions  = SNESSetFromOptions_LS;
5945e42470aSBarry Smith 
5955e42470aSBarry Smith   neP			= NEW(SNES_LS);   CHKPTR(neP);
5965e42470aSBarry Smith   snes->data    	= (void *) neP;
5975e42470aSBarry Smith   neP->alpha		= 1.e-4;
5985e42470aSBarry Smith   neP->maxstep		= 1.e8;
5995e42470aSBarry Smith   neP->steptol		= 1.e-12;
6005e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
6015e42470aSBarry Smith   return 0;
6025e42470aSBarry Smith }
6035e42470aSBarry Smith 
6045e42470aSBarry Smith 
6055e42470aSBarry Smith 
6065e42470aSBarry Smith 
607