xref: /petsc/src/snes/impls/ls/ls.c (revision fbe28522c53d349534977c8c6cbaf0d5bf3f7b02)
15e76c431SBarry Smith #ifndef lint
2*fbe28522SBarry Smith static char vcid[] = "$Id: ls.c,v 1.2 1995/04/05 20:34:33 bsmith Exp bsmith $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6*fbe28522SBarry 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;
325e42470aSBarry Smith   int     maxits, i, iters, line, nlconv, history_len,ierr,lits;
335e76c431SBarry Smith   double  fnorm, gnorm, gpnorm, 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 || */
475e42470aSBarry Smith   ierr = SNESComputeResidual(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*fbe28522SBarry Smith   if (snes->Monitor) (*snes->Monitor)(snes,0,X,F,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*fbe28522SBarry Smith        if (snes->Monitor) (*snes->Monitor)(snes,i+1,X,F,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   SNES_LS *ctx = (SNES_LS *)snes->data;
865e42470aSBarry Smith   int             ierr;
875e42470aSBarry Smith   snes->nwork = 3;
885e42470aSBarry Smith   ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr);
895e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work );
905e42470aSBarry Smith   return 0;
915e76c431SBarry Smith }
925e76c431SBarry Smith /* ------------------------------------------------------------ */
935e76c431SBarry Smith /*ARGSUSED*/
945e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
955e76c431SBarry Smith {
965e42470aSBarry Smith   SNES snes = (SNES) obj;
975e42470aSBarry Smith   SLESDestroy(snes->sles);
985e42470aSBarry Smith   VecFreeVecs(snes->work, snes->nwork );
995e42470aSBarry Smith   PLogObjectDestroy(obj);
1005e42470aSBarry Smith   PETSCHEADERDESTROY(obj);
1015e42470aSBarry Smith   return 0;
1025e76c431SBarry Smith }
1035e42470aSBarry Smith /*@
1045e42470aSBarry Smith    SNESDefaultMonitor - Default monitor for NLE solvers.  Prints the
1055e42470aSBarry Smith    residual norm at each iteration.
1065e42470aSBarry Smith 
1075e42470aSBarry Smith    Input Parameters:
1085e42470aSBarry Smith .  nlP - nonlinear context
1095e42470aSBarry Smith .  x - current iterate
1105e42470aSBarry Smith .  f - current residual (+/-)
1115e42470aSBarry Smith .  fnorm - 2-norm residual value (may be estimated).
1125e42470aSBarry Smith 
1135e42470aSBarry Smith    Notes:
1145e42470aSBarry Smith    f is either the residual or its negative, depending on the user's
1155e42470aSBarry Smith    preference, as set with NLSetResidualRoutine().
1165e42470aSBarry Smith 
1175e42470aSBarry Smith 
1185e42470aSBarry Smith @*/
1195e42470aSBarry Smith int SNESDefaultMonitor(SNES snes,int its, Vec x,Vec f,double fnorm,void *dummy)
1205e42470aSBarry Smith {
1215e42470aSBarry Smith   fprintf( stdout, "iter = %d, residual norm %g \n",its,fnorm);
1225e42470aSBarry Smith   return 0;
1235e42470aSBarry Smith }
1245e42470aSBarry Smith 
1255e42470aSBarry Smith /*@
1265e42470aSBarry Smith    SNESDefaultConverged - Default test for monitoring the convergence
1275e42470aSBarry Smith    of the NLE solvers.
1285e42470aSBarry Smith 
1295e42470aSBarry Smith    Input Parameters:
1305e42470aSBarry Smith .  nlP - nonlinear context
1315e42470aSBarry Smith .  xnorm - 2-norm of current iterate
1325e42470aSBarry Smith .  pnorm - 2-norm of current step
1335e42470aSBarry Smith .  fnorm - 2-norm of residual
1345e42470aSBarry Smith 
1355e42470aSBarry Smith    Returns:
1365e42470aSBarry Smith $  2  if  ( fnorm < atol ),
1375e42470aSBarry Smith $  3  if  ( pnorm < xtol*xnorm ),
1385e42470aSBarry Smith $ -2  if  ( nres > max_res ),
1395e42470aSBarry Smith $  0  otherwise,
1405e42470aSBarry Smith 
1415e42470aSBarry Smith    where
1425e42470aSBarry Smith $    atol    - absolute residual norm tolerance,
1435e42470aSBarry Smith $              set with NLSetAbsConvergenceTol()
1445e42470aSBarry Smith $    max_res - maximum number of residual evaluations,
1455e42470aSBarry Smith $              set with NLSetMaxResidualEvaluations()
1465e42470aSBarry Smith $    nres    - number of residual evaluations
1475e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
1485e42470aSBarry Smith 
1495e42470aSBarry Smith $              set with NLSetMaxResidualEvaluations()
1505e42470aSBarry Smith $    nres    - number of residual evaluations
1515e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
1525e42470aSBarry Smith $              set with NLSetRelConvergenceTol()
1535e42470aSBarry Smith 
1545e42470aSBarry Smith @*/
1555e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm,
1565e42470aSBarry Smith                          void *dummy)
1575e42470aSBarry Smith {
1585e42470aSBarry Smith   if (fnorm < snes->atol) {
1595e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Converged due to absolute residual norm %g < %g\n",
1605e42470aSBarry Smith                    fnorm,snes->atol);
1615e42470aSBarry Smith     return 2;
1625e42470aSBarry Smith   }
1635e42470aSBarry Smith   if (pnorm < snes->xtol*(xnorm)) {
1645e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Converged due to small update length  %g < %g*%g\n",
1655e42470aSBarry Smith                    pnorm,snes->xtol,xnorm);
1665e42470aSBarry Smith     return 3;
1675e42470aSBarry Smith   }
1685e42470aSBarry Smith   return 0;
1695e42470aSBarry Smith }
1705e42470aSBarry Smith 
1715e76c431SBarry Smith /* ------------------------------------------------------------ */
1725e76c431SBarry Smith /*ARGSUSED*/
1735e76c431SBarry Smith /*@
1745e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1755e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1765e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1775e76c431SBarry Smith 
1785e76c431SBarry Smith    Input Parameters:
1795e42470aSBarry Smith .  snes - nonlinear context
1805e76c431SBarry Smith .  x - current iterate
1815e76c431SBarry Smith .  f - residual evaluated at x
1825e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1835e76c431SBarry Smith .  w - work vector
1845e76c431SBarry Smith .  fnorm - 2-norm of f
1855e76c431SBarry Smith 
1865e76c431SBarry Smith    Output parameters:
1875e76c431SBarry Smith .  g - residual evaluated at new iterate y
1885e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1895e76c431SBarry Smith .  gnorm - 2-norm of g
1905e76c431SBarry Smith .  ynorm - 2-norm of search length
1915e76c431SBarry Smith 
1925e76c431SBarry Smith    Returns:
1935e76c431SBarry Smith    1, indicating success of the step.
1945e76c431SBarry Smith 
1955e76c431SBarry Smith @*/
1965e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
1975e42470aSBarry Smith                              double fnorm, double *ynorm, double *gnorm )
1985e76c431SBarry Smith {
1995e42470aSBarry Smith   int    ierr;
2005e42470aSBarry Smith   Scalar one = 1.0;
2015e42470aSBarry Smith   VecNorm(y, ynorm );	/* ynorm = || y ||    */
2025e42470aSBarry Smith   VecAXPY(&one, x, y );	/* y <- x + y         */
2035e42470aSBarry Smith   ierr = SNESComputeResidual(snes,y,g); CHKERR(ierr);
2045e42470aSBarry Smith   VecNorm( g, gnorm ); 	/* gnorm = || g ||    */
2055e76c431SBarry Smith   return 1;
2065e76c431SBarry Smith }
2075e76c431SBarry Smith /* ------------------------------------------------------------------ */
2085e76c431SBarry Smith /*@
2095e42470aSBarry Smith    SNESCubicLineSearch - This routine performs a cubic line search.
2105e76c431SBarry Smith 
2115e76c431SBarry Smith    Input Parameters:
2125e42470aSBarry Smith .  snes - nonlinear context
2135e76c431SBarry Smith .  x - current iterate
2145e76c431SBarry Smith .  f - residual evaluated at x
2155e76c431SBarry Smith .  y - search direction (contains new iterate on output)
2165e76c431SBarry Smith .  w - work vector
2175e76c431SBarry Smith .  fnorm - 2-norm of f
2185e76c431SBarry Smith 
2195e76c431SBarry Smith    Output parameters:
2205e76c431SBarry Smith .  g - residual evaluated at new iterate y
2215e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2225e76c431SBarry Smith .  gnorm - 2-norm of g
2235e76c431SBarry Smith .  ynorm - 2-norm of search length
2245e76c431SBarry Smith 
2255e76c431SBarry Smith    Returns:
2265e76c431SBarry Smith    1 if the line search succeeds; 0 if the line search fails.
2275e76c431SBarry Smith 
2285e76c431SBarry Smith    Notes:
2295e76c431SBarry Smith    Use either NLSetStepLineSearchRoutines() or NLSetLineSearchRoutine()
2305e42470aSBarry Smith    to set this routine within the SNES_NLS1 method.
2315e76c431SBarry Smith 
2325e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
2335e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
2345e76c431SBarry Smith 
2355e76c431SBarry Smith @*/
2365e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
2375e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
2385e76c431SBarry Smith {
2395e42470aSBarry Smith   double  steptol, initslope;
2405e42470aSBarry Smith   double  lambdaprev, gnormprev;
2415e76c431SBarry Smith   double  a, b, d, t1, t2;
2425e42470aSBarry Smith   Scalar  cinitslope,clambda;
2435e42470aSBarry Smith   int     ierr,count;
2445e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2455e42470aSBarry Smith   Scalar  one = 1.0,scale;
2465e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2475e76c431SBarry Smith 
2485e76c431SBarry Smith   alpha   = neP->alpha;
2495e76c431SBarry Smith   maxstep = neP->maxstep;
2505e76c431SBarry Smith   steptol = neP->steptol;
2515e76c431SBarry Smith 
2525e42470aSBarry Smith   VecNorm(y, ynorm );
2535e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2545e42470aSBarry Smith     scale = maxstep/(*ynorm);
2555e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
2565e42470aSBarry Smith     VecScale(&scale, y );
2575e76c431SBarry Smith     *ynorm = maxstep;
2585e76c431SBarry Smith   }
2595e76c431SBarry Smith   minlambda = steptol/(*ynorm);
2605e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2615e42470aSBarry Smith   VecDot(f, y, &cinitslope );
2625e42470aSBarry Smith   initslope = real(cinitslope);
2635e42470aSBarry Smith #else
2645e42470aSBarry Smith   VecDot(f, y, &initslope );
2655e42470aSBarry Smith #endif
2665e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2675e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2685e76c431SBarry Smith 
2695e42470aSBarry Smith   VecCopy(y, w );
2705e42470aSBarry Smith   VecAXPY(&one, x, w );
2715e42470aSBarry Smith   ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
2725e42470aSBarry Smith   VecNorm(g, gnorm );
2735e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
2745e42470aSBarry Smith       VecCopy(w, y );
2755e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
2765e42470aSBarry Smith       return 0;
2775e76c431SBarry Smith   }
2785e76c431SBarry Smith 
2795e76c431SBarry Smith   /* Fit points with quadratic */
2805e76c431SBarry Smith   lambda = 1.0; count = 0;
2815e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
2825e76c431SBarry Smith   lambdaprev = lambda;
2835e76c431SBarry Smith   gnormprev = *gnorm;
2845e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
2855e76c431SBarry Smith       lambda = .1*lambda;
2865e76c431SBarry Smith   } else lambda = lambdatemp;
2875e42470aSBarry Smith   VecCopy(x, w );
2885e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2895e42470aSBarry Smith   clambda = lambda; VecAXPY(&clambda, y, w );
2905e42470aSBarry Smith #else
2915e42470aSBarry Smith   VecAXPY(&lambda, y, w );
2925e42470aSBarry Smith #endif
2935e42470aSBarry Smith   ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
2945e42470aSBarry Smith   VecNorm(g, gnorm );
2955e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
2965e42470aSBarry Smith       VecCopy(w, y );
2975e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
2985e42470aSBarry Smith       return 0;
2995e76c431SBarry Smith   }
3005e76c431SBarry Smith 
3015e76c431SBarry Smith   /* Fit points with cubic */
3025e76c431SBarry Smith   count = 1;
3035e76c431SBarry Smith   while (1) {
3045e76c431SBarry Smith       if (lambda <= minlambda) { /* bad luck; use full step */
3055e42470aSBarry Smith            PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
3065e42470aSBarry Smith            PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
3075e76c431SBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
3085e42470aSBarry Smith            VecCopy(w, y );
3095e76c431SBarry Smith            return 0;
3105e76c431SBarry Smith       }
3115e76c431SBarry Smith       t1 = *gnorm - fnorm - lambda*initslope;
3125e76c431SBarry Smith       t2 = gnormprev  - fnorm - lambdaprev*initslope;
3135e76c431SBarry Smith       a = (t1/(lambda*lambda) -
3145e76c431SBarry Smith                 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3155e76c431SBarry Smith       b = (-lambdaprev*t1/(lambda*lambda) +
3165e76c431SBarry Smith                 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3175e76c431SBarry Smith       d = b*b - 3*a*initslope;
3185e76c431SBarry Smith       if (d < 0.0) d = 0.0;
3195e76c431SBarry Smith       if (a == 0.0) {
3205e76c431SBarry Smith          lambdatemp = -initslope/(2.0*b);
3215e76c431SBarry Smith       } else {
3225e76c431SBarry Smith          lambdatemp = (-b + sqrt(d))/(3.0*a);
3235e76c431SBarry Smith       }
3245e76c431SBarry Smith       if (lambdatemp > .5*lambda) {
3255e76c431SBarry Smith          lambdatemp = .5*lambda;
3265e76c431SBarry Smith       }
3275e76c431SBarry Smith       lambdaprev = lambda;
3285e76c431SBarry Smith       gnormprev = *gnorm;
3295e76c431SBarry Smith       if (lambdatemp <= .1*lambda) {
3305e76c431SBarry Smith          lambda = .1*lambda;
3315e76c431SBarry Smith       }
3325e76c431SBarry Smith       else lambda = lambdatemp;
3335e42470aSBarry Smith       VecCopy( x, w );
3345e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3355e42470aSBarry Smith       VecAXPY(&clambda, y, w );
3365e42470aSBarry Smith #else
3375e42470aSBarry Smith       VecAXPY(&lambda, y, w );
3385e42470aSBarry Smith #endif
3395e42470aSBarry Smith       ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
3405e42470aSBarry Smith       VecNorm(g, gnorm );
3415e76c431SBarry Smith       if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
3425e42470aSBarry Smith          VecCopy(w, y );
3435e42470aSBarry Smith          PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
3445e42470aSBarry Smith          return 0;
3455e76c431SBarry Smith       }
3465e76c431SBarry Smith       count++;
3475e76c431SBarry Smith    }
3485e42470aSBarry Smith   return 0;
3495e76c431SBarry Smith }
3505e42470aSBarry Smith /*@
3515e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3525e76c431SBarry Smith 
3535e42470aSBarry Smith    Input Parameters:
3545e42470aSBarry Smith .  snes - nonlinear context
3555e42470aSBarry Smith .  x - current iterate
3565e42470aSBarry Smith .  f - residual evaluated at x
3575e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3585e42470aSBarry Smith .  w - work vector
3595e42470aSBarry Smith .  fnorm - 2-norm of f
3605e42470aSBarry Smith 
3615e42470aSBarry Smith    Output parameters:
3625e42470aSBarry Smith .  g - residual evaluated at new iterate y
3635e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3645e42470aSBarry Smith .  gnorm - 2-norm of g
3655e42470aSBarry Smith .  ynorm - 2-norm of search length
3665e42470aSBarry Smith 
3675e42470aSBarry Smith    Returns:
3685e42470aSBarry Smith    1 if the line search succeeds; 0 if the line search fails.
3695e42470aSBarry Smith 
3705e42470aSBarry Smith    Notes:
3715e42470aSBarry Smith    Use SNESSetLineSearchRoutines()
3725e42470aSBarry Smith    to set this routine within the SNES_NLS1 method.
3735e42470aSBarry Smith 
3745e42470aSBarry Smith @*/
3755e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
3765e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
3775e76c431SBarry Smith {
3785e42470aSBarry Smith   double  steptol, initslope;
3795e42470aSBarry Smith   double  lambdaprev, gnormprev;
3805e42470aSBarry Smith   double  a, b, d, t1, t2;
3815e42470aSBarry Smith   Scalar  cinitslope,clambda;
3825e42470aSBarry Smith   int     ierr,count;
3835e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
3845e42470aSBarry Smith   Scalar  one = 1.0,scale;
3855e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
3865e76c431SBarry Smith 
3875e42470aSBarry Smith   alpha   = neP->alpha;
3885e42470aSBarry Smith   maxstep = neP->maxstep;
3895e42470aSBarry Smith   steptol = neP->steptol;
3905e76c431SBarry Smith 
3915e42470aSBarry Smith   VecNorm(y, ynorm );
3925e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
3935e42470aSBarry Smith     scale = maxstep/(*ynorm);
3945e42470aSBarry Smith     VecScale(&scale, y );
3955e42470aSBarry Smith     *ynorm = maxstep;
3965e76c431SBarry Smith   }
3975e42470aSBarry Smith   minlambda = steptol/(*ynorm);
3985e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3995e42470aSBarry Smith   VecDot(f, y, &cinitslope );
4005e42470aSBarry Smith   initslope = real(cinitslope);
4015e42470aSBarry Smith #else
4025e42470aSBarry Smith   VecDot(f, y, &initslope );
4035e42470aSBarry Smith #endif
4045e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
4055e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
4065e42470aSBarry Smith 
4075e42470aSBarry Smith   VecCopy(y, w );
4085e42470aSBarry Smith   VecAXPY(&one, x, w );
4095e42470aSBarry Smith   ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
4105e42470aSBarry Smith   VecNorm(g, gnorm );
4115e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
4125e42470aSBarry Smith       VecCopy(w, y );
4135e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
4145e42470aSBarry Smith       return 0;
4155e42470aSBarry Smith   }
4165e42470aSBarry Smith 
4175e42470aSBarry Smith   /* Fit points with quadratic */
4185e42470aSBarry Smith   lambda = 1.0; count = 0;
4195e42470aSBarry Smith   count = 1;
4205e42470aSBarry Smith   while (1) {
4215e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
4225e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
4235e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
4245e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
4255e42470aSBarry Smith       VecCopy(w, y );
4265e42470aSBarry Smith       return 0;
4275e42470aSBarry Smith     }
4285e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4295e42470aSBarry Smith     lambdaprev = lambda;
4305e42470aSBarry Smith     gnormprev = *gnorm;
4315e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4325e42470aSBarry Smith       lambda = .1*lambda;
4335e42470aSBarry Smith     } else lambda = lambdatemp;
4345e42470aSBarry Smith     VecCopy(x, w );
4355e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4365e42470aSBarry Smith     clambda = lambda; VecAXPY(&clambda, y, w );
4375e42470aSBarry Smith #else
4385e42470aSBarry Smith     VecAXPY(&lambda, y, w );
4395e42470aSBarry Smith #endif
4405e42470aSBarry Smith     ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
4415e42470aSBarry Smith     VecNorm(g, gnorm );
4425e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
4435e42470aSBarry Smith       VecCopy(w, y );
4445e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
4455e42470aSBarry Smith       return 0;
4465e42470aSBarry Smith     }
4475e42470aSBarry Smith     count++;
4485e42470aSBarry Smith   }
4495e42470aSBarry Smith 
4505e42470aSBarry Smith   return 0;
4515e76c431SBarry Smith }
4525e76c431SBarry Smith /* ------------------------------------------------------------ */
4535e76c431SBarry Smith /*@C
4545e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
455*fbe28522SBarry Smith    by the method SNES_LS.
4565e76c431SBarry Smith 
4575e76c431SBarry Smith    Input Parameters:
4585e42470aSBarry Smith .  snes - nonlinear context obtained from NLCreate()
4595e76c431SBarry Smith .  func - pointer to int function
4605e76c431SBarry Smith 
4615e76c431SBarry Smith    Possible routines:
4625e42470aSBarry Smith    SNESCubicLineSearch() - default line search
4635e42470aSBarry Smith    SNESNoLineSearch() - the full Newton step (actually not a line search)
4645e76c431SBarry Smith 
4655e76c431SBarry Smith    Calling sequence of func:
4665e42470aSBarry Smith .  func (SNES snes, Vec x, Vec f, Vec g, Vec y,
4675e42470aSBarry Smith          Vec w, double fnorm, double *ynorm, double *gnorm )
4685e76c431SBarry Smith 
4695e76c431SBarry Smith     Input parameters for func:
4705e42470aSBarry Smith .   snes - nonlinear context
4715e76c431SBarry Smith .   x - current iterate
4725e76c431SBarry Smith .   f - residual evaluated at x
4735e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4745e76c431SBarry Smith .   w - work vector
4755e76c431SBarry Smith .   fnorm - 2-norm of f
4765e76c431SBarry Smith 
4775e76c431SBarry Smith     Output parameters for func:
4785e76c431SBarry Smith .   g - residual evaluated at new iterate y
4795e76c431SBarry Smith .   y - new iterate (contains search direction on input)
4805e76c431SBarry Smith .   gnorm - 2-norm of g
4815e76c431SBarry Smith .   ynorm - 2-norm of search length
4825e76c431SBarry Smith 
4835e76c431SBarry Smith     Returned by func:
4845e76c431SBarry Smith     1 if the line search succeeds; 0 if the line search fails.
4855e76c431SBarry Smith @*/
4865e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
4875e42470aSBarry Smith                              double,double *,double*) )
4885e76c431SBarry Smith {
489*fbe28522SBarry Smith   if ((snes)->type == SNES_NLS)
4905e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
4915e42470aSBarry Smith   return 0;
4925e76c431SBarry Smith }
4935e42470aSBarry Smith 
4945e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
4955e42470aSBarry Smith {
4965e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
4975e42470aSBarry Smith   fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n");
4985e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha);
4995e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep);
5005e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol);
5015e42470aSBarry Smith }
5025e42470aSBarry Smith 
5035e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5045e42470aSBarry Smith {
5055e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5065e42470aSBarry Smith   char    ver[16];
5075e42470aSBarry Smith   double  tmp;
5085e42470aSBarry Smith 
5095e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_alpa",&tmp)) {
5105e42470aSBarry Smith     ls->alpha = tmp;
5115e42470aSBarry Smith   }
5125e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_maxstep",&tmp)) {
5135e42470aSBarry Smith     ls->maxstep = tmp;
5145e42470aSBarry Smith   }
5155e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_steptol",&tmp)) {
5165e42470aSBarry Smith     ls->steptol = tmp;
5175e42470aSBarry Smith   }
5185e42470aSBarry Smith   if (OptionsGetString(0,snes->prefix,"-snes_line_search",ver,16)) {
5195e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
5205e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
5215e42470aSBarry Smith     }
5225e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
5235e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
5245e42470aSBarry Smith     }
5255e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
5265e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
5275e42470aSBarry Smith     }
5285e42470aSBarry Smith     else {SETERR(1,"Unknown line search?");}
5295e42470aSBarry Smith   }
5305e42470aSBarry Smith   return 0;
5315e42470aSBarry Smith }
5325e42470aSBarry Smith 
5335e42470aSBarry Smith /* ------------------------------------------------------------ */
5345e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
5355e42470aSBarry Smith {
5365e42470aSBarry Smith   SNES_LS *neP;
5375e42470aSBarry Smith 
538*fbe28522SBarry Smith   snes->type		= SNES_NLS;
5395e42470aSBarry Smith   snes->Setup		= SNESSetUp_LS;
5405e42470aSBarry Smith   snes->Solver		= SNESSolve_LS;
5415e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
542*fbe28522SBarry Smith   snes->Monitor  	= 0;
5435e42470aSBarry Smith   snes->Converged	= SNESDefaultConverged;
5445e42470aSBarry Smith   snes->PrintHelp       = SNESPrintHelp_LS;
5455e42470aSBarry Smith   snes->SetFromOptions  = SNESSetFromOptions_LS;
5465e42470aSBarry Smith 
5475e42470aSBarry Smith   neP			= NEW(SNES_LS);   CHKPTR(neP);
5485e42470aSBarry Smith   snes->data    	= (void *) neP;
5495e42470aSBarry Smith   neP->alpha		= 1.e-4;
5505e42470aSBarry Smith   neP->maxstep		= 1.e8;
5515e42470aSBarry Smith   neP->steptol		= 1.e-12;
5525e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
5535e42470aSBarry Smith   return 0;
5545e42470aSBarry Smith }
5555e42470aSBarry Smith 
5565e42470aSBarry Smith 
5575e42470aSBarry Smith 
5585e42470aSBarry Smith 
559