xref: /petsc/src/snes/impls/ls/ls.c (revision edd2f0e15628dd9b7f6ddd2a5df1609eab2c4ee0)
15e76c431SBarry Smith #ifndef lint
2*edd2f0e1SBarry Smith static char vcid[] = "$Id: ls.c,v 1.14 1995/05/12 03:35:41 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;
32*edd2f0e1SBarry Smith   int          maxits, i, history_len,ierr,lits;
33*edd2f0e1SBarry Smith   MatStructure flg;
346b5873e3SBarry Smith   double       fnorm, gnorm, xnorm, ynorm, *history;
355e42470aSBarry Smith   Vec          Y, X, F, G, W, TMP;
365e76c431SBarry Smith 
375e42470aSBarry Smith   history	= snes->conv_hist;	/* convergence history */
385e42470aSBarry Smith   history_len	= snes->conv_hist_len;	/* convergence history length */
395e42470aSBarry Smith   maxits	= snes->max_its;	/* maximum number of iterations */
405e42470aSBarry Smith   X		= snes->vec_sol;		/* solution vector */
4139e2f89bSBarry Smith   F		= snes->vec_func;		/* residual vector */
425e42470aSBarry Smith   Y		= snes->work[0];		/* work vectors */
435e42470aSBarry Smith   G		= snes->work[1];
445e42470aSBarry Smith   W		= snes->work[2];
455e76c431SBarry Smith 
465e42470aSBarry Smith   ierr = SNESComputeInitialGuess(snes,X); CHKERR(ierr);  /* X <- X_0 */
475e42470aSBarry Smith   VecNorm( X, &xnorm );		       /* xnorm = || X || */
48a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERR(ierr); /* (+/-) F(X) */
495e42470aSBarry Smith   VecNorm(F, &fnorm );	        	/* fnorm <- || F || */
505e42470aSBarry Smith   snes->norm = fnorm;
515e76c431SBarry Smith   if (history && history_len > 0) history[0] = fnorm;
52eafb4bcbSBarry Smith   if (snes->Monitor) (*snes->Monitor)(snes,0,fnorm,snes->monP);
535e76c431SBarry Smith 
545e76c431SBarry Smith   for ( i=0; i<maxits; i++ ) {
555e42470aSBarry Smith        snes->iter = i+1;
565e76c431SBarry Smith 
575e76c431SBarry Smith        /* Solve J Y = -F, where J is Jacobian matrix */
5823242f5aSBarry Smith        (*snes->ComputeJacobian)(snes,X,&snes->jacobian,&snes->jacobian_pre,
5923242f5aSBarry Smith                                 &flg,snes->jacP);
6023242f5aSBarry Smith        ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg);
615e42470aSBarry Smith        ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERR(ierr);
625e42470aSBarry Smith        ierr = (*neP->LineSearch)(snes, X, F, G, Y, W, fnorm, &ynorm, &gnorm );
6323242f5aSBarry Smith        CHKERR(ierr);
645e76c431SBarry Smith 
6539e2f89bSBarry Smith        TMP = F; F = G; snes->vec_func_always = F; G = TMP;
6639e2f89bSBarry Smith        TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
675e76c431SBarry Smith        fnorm = gnorm;
685e76c431SBarry Smith 
695e42470aSBarry Smith        snes->norm = fnorm;
705e76c431SBarry Smith        if (history && history_len > i+1) history[i+1] = fnorm;
715e42470aSBarry Smith        VecNorm( X, &xnorm );		/* xnorm = || X || */
72eafb4bcbSBarry Smith        if (snes->Monitor) (*snes->Monitor)(snes,i+1,fnorm,snes->monP);
735e76c431SBarry Smith 
745e76c431SBarry Smith        /* Test for convergence */
755e42470aSBarry Smith        if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) {
7639e2f89bSBarry Smith            if (X != snes->vec_sol) {
7739e2f89bSBarry Smith              VecCopy( X, snes->vec_sol );
7839e2f89bSBarry Smith              snes->vec_sol_always = snes->vec_sol;
7939e2f89bSBarry Smith              snes->vec_func_always = snes->vec_func;
8039e2f89bSBarry Smith            }
815e76c431SBarry Smith            break;
825e76c431SBarry Smith        }
835e76c431SBarry Smith   }
845e76c431SBarry Smith   if (i == maxits) i--;
855e42470aSBarry Smith   *outits = i+1;
865e42470aSBarry Smith   return 0;
875e76c431SBarry Smith }
885e76c431SBarry Smith /* ------------------------------------------------------------ */
895e76c431SBarry Smith /*ARGSUSED*/
905e42470aSBarry Smith int SNESSetUp_LS(SNES snes )
915e76c431SBarry Smith {
925e42470aSBarry Smith   int ierr;
935e42470aSBarry Smith   snes->nwork = 3;
945e42470aSBarry Smith   ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr);
955e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work );
965e42470aSBarry Smith   return 0;
975e76c431SBarry Smith }
985e76c431SBarry Smith /* ------------------------------------------------------------ */
995e76c431SBarry Smith /*ARGSUSED*/
1005e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
1015e76c431SBarry Smith {
1025e42470aSBarry Smith   SNES snes = (SNES) obj;
1035e42470aSBarry Smith   SLESDestroy(snes->sles);
1045e42470aSBarry Smith   VecFreeVecs(snes->work, snes->nwork );
1055e42470aSBarry Smith   PLogObjectDestroy(obj);
1065e42470aSBarry Smith   PETSCHEADERDESTROY(obj);
1075e42470aSBarry Smith   return 0;
1085e76c431SBarry Smith }
1095e42470aSBarry Smith /*@
1100de89847SLois Curfman McInnes    SNESDefaultMonitor - Default SNES monitoring routine.  Prints the
1115e42470aSBarry Smith    residual norm at each iteration.
1125e42470aSBarry Smith 
1135e42470aSBarry Smith    Input Parameters:
1140de89847SLois Curfman McInnes .  snes - the SNES context
1150de89847SLois Curfman McInnes .  its - iteration number
1160de89847SLois Curfman McInnes .  fnorm - 2-norm residual value (may be estimated)
1170de89847SLois Curfman McInnes .  dummy - unused context
1185e42470aSBarry Smith 
1195e42470aSBarry Smith    Notes:
1205e42470aSBarry Smith    f is either the residual or its negative, depending on the user's
121a9581db2SBarry Smith    preference, as set with SNESSetFunction().
122a67c8522SLois Curfman McInnes 
123a67c8522SLois Curfman McInnes .keywords: SNES, nonlinear, default, monitor, residual, norm
124a67c8522SLois Curfman McInnes 
125a67c8522SLois Curfman McInnes .seealso: SNESSetMonitor()
1265e42470aSBarry Smith @*/
127eafb4bcbSBarry Smith int SNESDefaultMonitor(SNES snes,int its, double fnorm,void *dummy)
1285e42470aSBarry Smith {
129*edd2f0e1SBarry Smith   MPE_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm);
1305e42470aSBarry Smith   return 0;
1315e42470aSBarry Smith }
1325e42470aSBarry Smith 
1335e42470aSBarry Smith /*@
1345e42470aSBarry Smith    SNESDefaultConverged - Default test for monitoring the convergence
1350de89847SLois Curfman McInnes    of the SNES solvers.
1365e42470aSBarry Smith 
1375e42470aSBarry Smith    Input Parameters:
1380de89847SLois Curfman McInnes .  snes - the SNES context
1395e42470aSBarry Smith .  xnorm - 2-norm of current iterate
1405e42470aSBarry Smith .  pnorm - 2-norm of current step
1415e42470aSBarry Smith .  fnorm - 2-norm of residual
1420de89847SLois Curfman McInnes .  dummy - unused context
1435e42470aSBarry Smith 
1445e42470aSBarry Smith    Returns:
1455e42470aSBarry Smith $  2  if  ( fnorm < atol ),
1465e42470aSBarry Smith $  3  if  ( pnorm < xtol*xnorm ),
1475e42470aSBarry Smith $ -2  if  ( nres > max_res ),
1485e42470aSBarry Smith $  0  otherwise,
1495e42470aSBarry Smith 
1505e42470aSBarry Smith    where
1515e42470aSBarry Smith $    atol    - absolute residual norm tolerance,
1520de89847SLois Curfman McInnes $              set with SNESSetAbsoluteTolerance()
1535e42470aSBarry Smith $    max_res - maximum number of residual evaluations,
1540de89847SLois Curfman McInnes $              set with SNESSetMaxResidualEvaluations()
1555e42470aSBarry Smith $    nres    - number of residual evaluations
1565e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
1570de89847SLois Curfman McInnes $              set with SNESSetRelativeTolerance()
1585e42470aSBarry Smith 
1590de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence
1605e42470aSBarry Smith 
1610de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest()
1625e42470aSBarry Smith @*/
1635e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm,
1645e42470aSBarry Smith                          void *dummy)
1655e42470aSBarry Smith {
1665e42470aSBarry Smith   if (fnorm < snes->atol) {
167a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
168a67c8522SLois Curfman McInnes       "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol);
1695e42470aSBarry Smith     return 2;
1705e42470aSBarry Smith   }
1715e42470aSBarry Smith   if (pnorm < snes->xtol*(xnorm)) {
172a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
173a67c8522SLois Curfman McInnes       "Converged due to small update length  %g < %g*%g\n",
1745e42470aSBarry Smith        pnorm,snes->xtol,xnorm);
1755e42470aSBarry Smith     return 3;
1765e42470aSBarry Smith   }
17749e3953dSBarry Smith   if (snes->nfuncs > snes->max_funcs) {
178a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
179a67c8522SLois Curfman McInnes       "Exceeded maximum number of residual evaluations: %d > %d\n",
18049e3953dSBarry Smith        snes->nfuncs, snes->max_funcs );
181a67c8522SLois Curfman McInnes     return -2;
182a67c8522SLois Curfman McInnes   }
1835e42470aSBarry Smith   return 0;
1845e42470aSBarry Smith }
1855e42470aSBarry Smith 
1865e76c431SBarry Smith /* ------------------------------------------------------------ */
1875e76c431SBarry Smith /*ARGSUSED*/
1885e76c431SBarry Smith /*@
1895e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1905e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1915e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1925e76c431SBarry Smith 
1935e76c431SBarry Smith    Input Parameters:
1945e42470aSBarry Smith .  snes - nonlinear context
1955e76c431SBarry Smith .  x - current iterate
1965e76c431SBarry Smith .  f - residual evaluated at x
1975e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1985e76c431SBarry Smith .  w - work vector
1995e76c431SBarry Smith .  fnorm - 2-norm of f
2005e76c431SBarry Smith 
2015e76c431SBarry Smith    Output parameters:
2025e76c431SBarry Smith .  g - residual evaluated at new iterate y
2035e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2045e76c431SBarry Smith .  gnorm - 2-norm of g
2055e76c431SBarry Smith .  ynorm - 2-norm of search length
2065e76c431SBarry Smith 
2075e76c431SBarry Smith    Returns:
2085e76c431SBarry Smith    1, indicating success of the step.
2095e76c431SBarry Smith 
21028ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
21128ae5a21SLois Curfman McInnes 
212f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
213f59ffdedSLois Curfman McInnes .seealso: SNESSetLineSearchRoutine()
2145e76c431SBarry Smith @*/
2155e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
2165e42470aSBarry Smith                              double fnorm, double *ynorm, double *gnorm )
2175e76c431SBarry Smith {
2185e42470aSBarry Smith   int    ierr;
2195e42470aSBarry Smith   Scalar one = 1.0;
2207857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
2215e42470aSBarry Smith   VecNorm(y, ynorm );	/* ynorm = || y ||    */
2225e42470aSBarry Smith   VecAXPY(&one, x, y );	/* y <- x + y         */
223a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,y,g); CHKERR(ierr);
2245e42470aSBarry Smith   VecNorm( g, gnorm ); 	/* gnorm = || g ||    */
2257857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
2265e76c431SBarry Smith   return 1;
2275e76c431SBarry Smith }
2285e76c431SBarry Smith /* ------------------------------------------------------------------ */
2295e76c431SBarry Smith /*@
230f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
231f59ffdedSLois Curfman McInnes    is the default line search method.
2325e76c431SBarry Smith 
2335e76c431SBarry Smith    Input Parameters:
2345e42470aSBarry Smith .  snes - nonlinear context
2355e76c431SBarry Smith .  x - current iterate
2365e76c431SBarry Smith .  f - residual evaluated at x
2375e76c431SBarry Smith .  y - search direction (contains new iterate on output)
2385e76c431SBarry Smith .  w - work vector
2395e76c431SBarry Smith .  fnorm - 2-norm of f
2405e76c431SBarry Smith 
2415e76c431SBarry Smith    Output parameters:
2425e76c431SBarry Smith .  g - residual evaluated at new iterate y
2435e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2445e76c431SBarry Smith .  gnorm - 2-norm of g
2455e76c431SBarry Smith .  ynorm - 2-norm of search length
2465e76c431SBarry Smith 
2475e76c431SBarry Smith    Returns:
2485e76c431SBarry Smith    1 if the line search succeeds; 0 if the line search fails.
2495e76c431SBarry Smith 
2505e76c431SBarry Smith    Notes:
2515e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
2525e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
2535e76c431SBarry Smith 
25428ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
25528ae5a21SLois Curfman McInnes 
256f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
2575e76c431SBarry Smith @*/
2585e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
2595e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
2605e76c431SBarry Smith {
2615e42470aSBarry Smith   double  steptol, initslope;
2625e42470aSBarry Smith   double  lambdaprev, gnormprev;
2635e76c431SBarry Smith   double  a, b, d, t1, t2;
2646b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2655e42470aSBarry Smith   Scalar  cinitslope,clambda;
2666b5873e3SBarry Smith #endif
2675e42470aSBarry Smith   int     ierr,count;
2685e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2695e42470aSBarry Smith   Scalar  one = 1.0,scale;
2705e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2715e76c431SBarry Smith 
2727857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
2735e76c431SBarry Smith   alpha   = neP->alpha;
2745e76c431SBarry Smith   maxstep = neP->maxstep;
2755e76c431SBarry Smith   steptol = neP->steptol;
2765e76c431SBarry Smith 
2775e42470aSBarry Smith   VecNorm(y, ynorm );
2785e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2795e42470aSBarry Smith     scale = maxstep/(*ynorm);
2806b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2816b5873e3SBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale));
2826b5873e3SBarry Smith #else
2835e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
2846b5873e3SBarry Smith #endif
2855e42470aSBarry Smith     VecScale(&scale, y );
2865e76c431SBarry Smith     *ynorm = maxstep;
2875e76c431SBarry Smith   }
2885e76c431SBarry Smith   minlambda = steptol/(*ynorm);
2895e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2905e42470aSBarry Smith   VecDot(f, y, &cinitslope );
2915e42470aSBarry Smith   initslope = real(cinitslope);
2925e42470aSBarry Smith #else
2935e42470aSBarry Smith   VecDot(f, y, &initslope );
2945e42470aSBarry Smith #endif
2955e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2965e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2975e76c431SBarry Smith 
2985e42470aSBarry Smith   VecCopy(y, w );
2995e42470aSBarry Smith   VecAXPY(&one, x, w );
300a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3015e42470aSBarry Smith   VecNorm(g, gnorm );
3025e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
3035e42470aSBarry Smith       VecCopy(w, y );
3045e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
3057857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3065e42470aSBarry Smith       return 0;
3075e76c431SBarry Smith   }
3085e76c431SBarry Smith 
3095e76c431SBarry Smith   /* Fit points with quadratic */
3105e76c431SBarry Smith   lambda = 1.0; count = 0;
3115e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
3125e76c431SBarry Smith   lambdaprev = lambda;
3135e76c431SBarry Smith   gnormprev = *gnorm;
3145e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
3155e76c431SBarry Smith       lambda = .1*lambda;
3165e76c431SBarry Smith   } else lambda = lambdatemp;
3175e42470aSBarry Smith   VecCopy(x, w );
3185e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3195e42470aSBarry Smith   clambda = lambda; VecAXPY(&clambda, y, w );
3205e42470aSBarry Smith #else
3215e42470aSBarry Smith   VecAXPY(&lambda, y, w );
3225e42470aSBarry Smith #endif
323a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3245e42470aSBarry Smith   VecNorm(g, gnorm );
3255e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
3265e42470aSBarry Smith       VecCopy(w, y );
3275e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
3287857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3295e42470aSBarry Smith       return 0;
3305e76c431SBarry Smith   }
3315e76c431SBarry Smith 
3325e76c431SBarry Smith   /* Fit points with cubic */
3335e76c431SBarry Smith   count = 1;
3345e76c431SBarry Smith   while (1) {
3355e76c431SBarry Smith       if (lambda <= minlambda) { /* bad luck; use full step */
3365e42470aSBarry Smith            PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
3375e42470aSBarry Smith            PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
3385e76c431SBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
3395e42470aSBarry Smith            VecCopy(w, y );
3407857610eSBarry Smith            PLogEventEnd(SNES_LineSearch,snes,x,f,g);
34123242f5aSBarry Smith            return -1;
3425e76c431SBarry Smith       }
3435e76c431SBarry Smith       t1 = *gnorm - fnorm - lambda*initslope;
3445e76c431SBarry Smith       t2 = gnormprev  - fnorm - lambdaprev*initslope;
3455e76c431SBarry Smith       a = (t1/(lambda*lambda) -
3465e76c431SBarry Smith                 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3475e76c431SBarry Smith       b = (-lambdaprev*t1/(lambda*lambda) +
3485e76c431SBarry Smith                 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3495e76c431SBarry Smith       d = b*b - 3*a*initslope;
3505e76c431SBarry Smith       if (d < 0.0) d = 0.0;
3515e76c431SBarry Smith       if (a == 0.0) {
3525e76c431SBarry Smith          lambdatemp = -initslope/(2.0*b);
3535e76c431SBarry Smith       } else {
3545e76c431SBarry Smith          lambdatemp = (-b + sqrt(d))/(3.0*a);
3555e76c431SBarry Smith       }
3565e76c431SBarry Smith       if (lambdatemp > .5*lambda) {
3575e76c431SBarry Smith          lambdatemp = .5*lambda;
3585e76c431SBarry Smith       }
3595e76c431SBarry Smith       lambdaprev = lambda;
3605e76c431SBarry Smith       gnormprev = *gnorm;
3615e76c431SBarry Smith       if (lambdatemp <= .1*lambda) {
3625e76c431SBarry Smith          lambda = .1*lambda;
3635e76c431SBarry Smith       }
3645e76c431SBarry Smith       else lambda = lambdatemp;
3655e42470aSBarry Smith       VecCopy( x, w );
3665e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3675e42470aSBarry Smith       VecAXPY(&clambda, y, w );
3685e42470aSBarry Smith #else
3695e42470aSBarry Smith       VecAXPY(&lambda, y, w );
3705e42470aSBarry Smith #endif
371a9581db2SBarry Smith       ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
3725e42470aSBarry Smith       VecNorm(g, gnorm );
3735e76c431SBarry Smith       if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
3745e42470aSBarry Smith          VecCopy(w, y );
3755e42470aSBarry Smith          PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
3767857610eSBarry Smith          PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3775e42470aSBarry Smith          return 0;
3785e76c431SBarry Smith       }
3795e76c431SBarry Smith       count++;
3805e76c431SBarry Smith    }
3817857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3825e42470aSBarry Smith   return 0;
3835e76c431SBarry Smith }
3845e42470aSBarry Smith /*@
3855e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3865e76c431SBarry Smith 
3875e42470aSBarry Smith    Input Parameters:
388f59ffdedSLois Curfman McInnes .  snes - the SNES context
3895e42470aSBarry Smith .  x - current iterate
3905e42470aSBarry Smith .  f - residual evaluated at x
3915e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3925e42470aSBarry Smith .  w - work vector
3935e42470aSBarry Smith .  fnorm - 2-norm of f
3945e42470aSBarry Smith 
3955e42470aSBarry Smith    Output parameters:
3965e42470aSBarry Smith .  g - residual evaluated at new iterate y
3975e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3985e42470aSBarry Smith .  gnorm - 2-norm of g
3995e42470aSBarry Smith .  ynorm - 2-norm of search length
4005e42470aSBarry Smith 
4015e42470aSBarry Smith    Returns:
4025e42470aSBarry Smith    1 if the line search succeeds; 0 if the line search fails.
4035e42470aSBarry Smith 
4045e42470aSBarry Smith    Notes:
405f59ffdedSLois Curfman McInnes    Use SNESSetLineSearchRoutine()
406f59ffdedSLois Curfman McInnes    to set this routine within the SNES_NLS method.
4075e42470aSBarry Smith 
408f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
409f59ffdedSLois Curfman McInnes 
410f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
4115e42470aSBarry Smith @*/
4125e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
4135e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
4145e76c431SBarry Smith {
4155e42470aSBarry Smith   double  steptol, initslope;
4165e42470aSBarry Smith   double  lambdaprev, gnormprev;
4176b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
4185e42470aSBarry Smith   Scalar  cinitslope,clambda;
4196b5873e3SBarry Smith #endif
4205e42470aSBarry Smith   int     ierr,count;
4215e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
4225e42470aSBarry Smith   Scalar  one = 1.0,scale;
4235e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
4245e76c431SBarry Smith 
4257857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
4265e42470aSBarry Smith   alpha   = neP->alpha;
4275e42470aSBarry Smith   maxstep = neP->maxstep;
4285e42470aSBarry Smith   steptol = neP->steptol;
4295e76c431SBarry Smith 
4305e42470aSBarry Smith   VecNorm(y, ynorm );
4315e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
4325e42470aSBarry Smith     scale = maxstep/(*ynorm);
4335e42470aSBarry Smith     VecScale(&scale, y );
4345e42470aSBarry Smith     *ynorm = maxstep;
4355e76c431SBarry Smith   }
4365e42470aSBarry Smith   minlambda = steptol/(*ynorm);
4375e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4385e42470aSBarry Smith   VecDot(f, y, &cinitslope );
4395e42470aSBarry Smith   initslope = real(cinitslope);
4405e42470aSBarry Smith #else
4415e42470aSBarry Smith   VecDot(f, y, &initslope );
4425e42470aSBarry Smith #endif
4435e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
4445e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
4455e42470aSBarry Smith 
4465e42470aSBarry Smith   VecCopy(y, w );
4475e42470aSBarry Smith   VecAXPY(&one, x, w );
448a9581db2SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
4495e42470aSBarry Smith   VecNorm(g, gnorm );
4505e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
4515e42470aSBarry Smith       VecCopy(w, y );
4525e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
4537857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4545e42470aSBarry Smith       return 0;
4555e42470aSBarry Smith   }
4565e42470aSBarry Smith 
4575e42470aSBarry Smith   /* Fit points with quadratic */
4585e42470aSBarry Smith   lambda = 1.0; count = 0;
4595e42470aSBarry Smith   count = 1;
4605e42470aSBarry Smith   while (1) {
4615e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
4625e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
4635e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
4645e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
4655e42470aSBarry Smith       VecCopy(w, y );
4667857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4675e42470aSBarry Smith       return 0;
4685e42470aSBarry Smith     }
4695e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4705e42470aSBarry Smith     lambdaprev = lambda;
4715e42470aSBarry Smith     gnormprev = *gnorm;
4725e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4735e42470aSBarry Smith       lambda = .1*lambda;
4745e42470aSBarry Smith     } else lambda = lambdatemp;
4755e42470aSBarry Smith     VecCopy(x, w );
4765e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4775e42470aSBarry Smith     clambda = lambda; VecAXPY(&clambda, y, w );
4785e42470aSBarry Smith #else
4795e42470aSBarry Smith     VecAXPY(&lambda, y, w );
4805e42470aSBarry Smith #endif
481a9581db2SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr);
4825e42470aSBarry Smith     VecNorm(g, gnorm );
4835e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
4845e42470aSBarry Smith       VecCopy(w, y );
4855e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
4867857610eSBarry Smith       PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4875e42470aSBarry Smith       return 0;
4885e42470aSBarry Smith     }
4895e42470aSBarry Smith     count++;
4905e42470aSBarry Smith   }
4915e42470aSBarry Smith 
4927857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4935e42470aSBarry Smith   return 0;
4945e76c431SBarry Smith }
4955e76c431SBarry Smith /* ------------------------------------------------------------ */
4965e76c431SBarry Smith /*@C
4975e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
498fbe28522SBarry Smith    by the method SNES_LS.
4995e76c431SBarry Smith 
5005e76c431SBarry Smith    Input Parameters:
501eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
5025e76c431SBarry Smith .  func - pointer to int function
5035e76c431SBarry Smith 
5045e76c431SBarry Smith    Possible routines:
505f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
506f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
507f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
5085e76c431SBarry Smith 
5095e76c431SBarry Smith    Calling sequence of func:
510f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
5115e42470aSBarry Smith          Vec w, double fnorm, double *ynorm, double *gnorm)
5125e76c431SBarry Smith 
5135e76c431SBarry Smith     Input parameters for func:
5145e42470aSBarry Smith .   snes - nonlinear context
5155e76c431SBarry Smith .   x - current iterate
5165e76c431SBarry Smith .   f - residual evaluated at x
5175e76c431SBarry Smith .   y - search direction (contains new iterate on output)
5185e76c431SBarry Smith .   w - work vector
5195e76c431SBarry Smith .   fnorm - 2-norm of f
5205e76c431SBarry Smith 
5215e76c431SBarry Smith     Output parameters for func:
5225e76c431SBarry Smith .   g - residual evaluated at new iterate y
5235e76c431SBarry Smith .   y - new iterate (contains search direction on input)
5245e76c431SBarry Smith .   gnorm - 2-norm of g
5255e76c431SBarry Smith .   ynorm - 2-norm of search length
5265e76c431SBarry Smith 
5275e76c431SBarry Smith     Returned by func:
5285e76c431SBarry Smith     1 if the line search succeeds; 0 if the line search fails.
529f59ffdedSLois Curfman McInnes 
530f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
531f59ffdedSLois Curfman McInnes 
532f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
5335e76c431SBarry Smith @*/
5345e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
5355e42470aSBarry Smith                              double,double *,double*) )
5365e76c431SBarry Smith {
537fbe28522SBarry Smith   if ((snes)->type == SNES_NLS)
5385e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
5395e42470aSBarry Smith   return 0;
5405e76c431SBarry Smith }
5415e42470aSBarry Smith 
5425e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
5435e42470aSBarry Smith {
5445e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5455e42470aSBarry Smith   fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n");
5465e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha);
5475e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep);
5485e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol);
5496b5873e3SBarry Smith   return 0;
5505e42470aSBarry Smith }
5515e42470aSBarry Smith 
5526b5873e3SBarry Smith #include "options.h"
5535e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5545e42470aSBarry Smith {
5555e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5565e42470aSBarry Smith   char    ver[16];
5575e42470aSBarry Smith   double  tmp;
5585e42470aSBarry Smith 
5595e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_alpa",&tmp)) {
5605e42470aSBarry Smith     ls->alpha = tmp;
5615e42470aSBarry Smith   }
5625e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_maxstep",&tmp)) {
5635e42470aSBarry Smith     ls->maxstep = tmp;
5645e42470aSBarry Smith   }
5655e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_steptol",&tmp)) {
5665e42470aSBarry Smith     ls->steptol = tmp;
5675e42470aSBarry Smith   }
5685e42470aSBarry Smith   if (OptionsGetString(0,snes->prefix,"-snes_line_search",ver,16)) {
5695e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
5705e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
5715e42470aSBarry Smith     }
5725e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
5735e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
5745e42470aSBarry Smith     }
5755e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
5765e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
5775e42470aSBarry Smith     }
5785e42470aSBarry Smith     else {SETERR(1,"Unknown line search?");}
5795e42470aSBarry Smith   }
5805e42470aSBarry Smith   return 0;
5815e42470aSBarry Smith }
5825e42470aSBarry Smith 
5835e42470aSBarry Smith /* ------------------------------------------------------------ */
5845e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
5855e42470aSBarry Smith {
5865e42470aSBarry Smith   SNES_LS *neP;
5875e42470aSBarry Smith 
588fbe28522SBarry Smith   snes->type		= SNES_NLS;
58949e3953dSBarry Smith   snes->setup		= SNESSetUp_LS;
59049e3953dSBarry Smith   snes->solve		= SNESSolve_LS;
5915e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
5925e42470aSBarry Smith   snes->Converged	= SNESDefaultConverged;
59349e3953dSBarry Smith   snes->printhelp       = SNESPrintHelp_LS;
59449e3953dSBarry Smith   snes->setfromoptions  = SNESSetFromOptions_LS;
5955e42470aSBarry Smith 
5965e42470aSBarry Smith   neP			= NEW(SNES_LS);   CHKPTR(neP);
5975e42470aSBarry Smith   snes->data    	= (void *) neP;
5985e42470aSBarry Smith   neP->alpha		= 1.e-4;
5995e42470aSBarry Smith   neP->maxstep		= 1.e8;
6005e42470aSBarry Smith   neP->steptol		= 1.e-12;
6015e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
6025e42470aSBarry Smith   return 0;
6035e42470aSBarry Smith }
6045e42470aSBarry Smith 
6055e42470aSBarry Smith 
6065e42470aSBarry Smith 
6075e42470aSBarry Smith 
608