xref: /petsc/src/snes/impls/ls/ls.c (revision ddbbdb52ef8421db4fbc53121240cc6e66667bce)
15e76c431SBarry Smith #ifndef lint
2*ddbbdb52SLois Curfman McInnes static char vcid[] = "$Id: ls.c,v 1.30 1995/07/14 18:36:07 curfman Exp curfman $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6fbe28522SBarry Smith #include "ls.h"
7a935fc98SLois Curfman McInnes #include "pviewer.h"
85e42470aSBarry Smith 
95e42470aSBarry Smith /*
105e42470aSBarry Smith      Implements a line search variant of Newton's Method
115e76c431SBarry Smith     for solving systems of nonlinear equations.
125e76c431SBarry Smith 
135e76c431SBarry Smith     Input parameters:
145e42470aSBarry Smith .   snes - nonlinear context obtained from SNESCreate()
155e76c431SBarry Smith 
165e42470aSBarry Smith     Output Parameters:
17a935fc98SLois Curfman McInnes .   outits  - Number of global iterations until termination.
185e76c431SBarry Smith 
195e76c431SBarry Smith     Notes:
205e76c431SBarry Smith     This implements essentially a truncated Newton method with a
215e76c431SBarry Smith     line search.  By default a cubic backtracking line search
225e76c431SBarry Smith     is employed, as described in the text "Numerical Methods for
235e76c431SBarry Smith     Unconstrained Optimization and Nonlinear Equations" by Dennis
245e42470aSBarry Smith     and Schnabel.  See the examples in src/snes/examples.
255e76c431SBarry Smith */
265e76c431SBarry Smith 
275e42470aSBarry Smith int SNESSolve_LS(SNES snes,int *outits)
285e76c431SBarry Smith {
295e42470aSBarry Smith   SNES_LS      *neP = (SNES_LS *) snes->data;
30761aaf1bSLois Curfman McInnes   int          maxits, i, history_len, ierr, lits, lsfail;
31df60cc22SBarry Smith   MatStructure flg = ALLMAT_DIFFERENT_NONZERO_PATTERN;
326b5873e3SBarry Smith   double       fnorm, gnorm, xnorm, ynorm, *history;
335e42470aSBarry Smith   Vec          Y, X, F, G, W, TMP;
345e76c431SBarry Smith 
355e42470aSBarry Smith   history	= snes->conv_hist;	/* convergence history */
365e42470aSBarry Smith   history_len	= snes->conv_hist_len;	/* convergence history length */
375e42470aSBarry Smith   maxits	= snes->max_its;	/* maximum number of iterations */
385e42470aSBarry Smith   X		= snes->vec_sol;	/* solution vector */
3939e2f89bSBarry Smith   F		= snes->vec_func;	/* residual vector */
405e42470aSBarry Smith   Y		= snes->work[0];	/* work vectors */
415e42470aSBarry Smith   G		= snes->work[1];
425e42470aSBarry Smith   W		= snes->work[2];
435e76c431SBarry Smith 
4478b31e54SBarry Smith   ierr = SNESComputeInitialGuess(snes,X); CHKERRQ(ierr); /* X <- X_0 */
455e42470aSBarry Smith   VecNorm(X,&xnorm);		                         /* xnorm = || X || */
4678b31e54SBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);   /* (+/-) F(X) */
475e42470aSBarry Smith   VecNorm(F,&fnorm);	        	                 /* fnorm <- ||F|| */
485e42470aSBarry Smith   snes->norm = fnorm;
495e76c431SBarry Smith   if (history && history_len > 0) history[0] = fnorm;
50a935fc98SLois Curfman McInnes   if (snes->Monitor)
51a935fc98SLois Curfman McInnes     {ierr = (*snes->Monitor)(snes,0,fnorm,snes->monP); CHKERRQ(ierr);}
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 */
57df60cc22SBarry Smith        ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,
5878b31e54SBarry Smith                                 &flg,snes->jacP); CHKERRQ(ierr);
59a935fc98SLois Curfman McInnes        ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,
60a935fc98SLois Curfman McInnes                                 flg); CHKERRQ(ierr);
6178b31e54SBarry Smith        ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr);
6281b6cf68SLois Curfman McInnes        ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr);
63761aaf1bSLois Curfman McInnes        ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail);
64761aaf1bSLois Curfman McInnes        if (lsfail) snes->nfailures++;
6578b31e54SBarry Smith        CHKERRQ(ierr);
665e76c431SBarry Smith 
6739e2f89bSBarry Smith        TMP = F; F = G; snes->vec_func_always = F; G = TMP;
6839e2f89bSBarry Smith        TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
695e76c431SBarry Smith        fnorm = gnorm;
705e76c431SBarry Smith 
715e42470aSBarry Smith        snes->norm = fnorm;
725e76c431SBarry Smith        if (history && history_len > i+1) history[i+1] = fnorm;
735e42470aSBarry Smith        VecNorm(X,&xnorm);		/* xnorm = || X || */
74a935fc98SLois Curfman McInnes        if (snes->Monitor)
75a935fc98SLois Curfman McInnes          {(*snes->Monitor)(snes,i+1,fnorm,snes->monP); CHKERRQ(ierr);}
765e76c431SBarry Smith 
775e76c431SBarry Smith        /* Test for convergence */
785e42470aSBarry Smith        if ((*snes->Converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
7939e2f89bSBarry Smith            if (X != snes->vec_sol) {
8039e2f89bSBarry Smith              VecCopy(X,snes->vec_sol);
8139e2f89bSBarry Smith              snes->vec_sol_always = snes->vec_sol;
8239e2f89bSBarry Smith              snes->vec_func_always = snes->vec_func;
8339e2f89bSBarry Smith            }
845e76c431SBarry Smith            break;
855e76c431SBarry Smith        }
865e76c431SBarry Smith   }
875e76c431SBarry Smith   if (i == maxits) i--;
885e42470aSBarry Smith   *outits = i+1;
895e42470aSBarry Smith   return 0;
905e76c431SBarry Smith }
915e76c431SBarry Smith /* ------------------------------------------------------------ */
925e42470aSBarry Smith int SNESSetUp_LS(SNES snes )
935e76c431SBarry Smith {
945e42470aSBarry Smith   int ierr;
9581b6cf68SLois Curfman McInnes   snes->nwork = 4;
9678b31e54SBarry Smith   ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work); CHKERRQ(ierr);
975e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work );
9881b6cf68SLois Curfman McInnes   snes->vec_sol_update_always = snes->work[3];
995e42470aSBarry Smith   return 0;
1005e76c431SBarry Smith }
1015e76c431SBarry Smith /* ------------------------------------------------------------ */
1025e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
1035e76c431SBarry Smith {
1045e42470aSBarry Smith   SNES snes = (SNES) obj;
1055e42470aSBarry Smith   VecFreeVecs(snes->work, snes->nwork );
10678b31e54SBarry Smith   PETSCFREE(snes->data);
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 {
129614e12f8SBarry Smith   MPIU_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm);
130614e12f8SBarry Smith   return 0;
131614e12f8SBarry Smith }
132614e12f8SBarry Smith 
133614e12f8SBarry Smith int SNESDefaultSMonitor(SNES snes,int its, double fnorm,void *dummy)
134614e12f8SBarry Smith {
135614e12f8SBarry Smith   if (fnorm > 1.e-9 || fnorm == 0.0) {
1367f813858SBarry Smith     MPIU_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm);
13769e4de80SBarry Smith   }
138614e12f8SBarry Smith   else if (fnorm > 1.e-11){
13969e4de80SBarry Smith     MPIU_printf(snes->comm, "iter = %d, Function norm %5.3e \n",its,fnorm);
14069e4de80SBarry Smith   }
141614e12f8SBarry Smith   else {
142614e12f8SBarry Smith     MPIU_printf(snes->comm, "iter = %d, Function norm < 1.e-11\n",its);
143614e12f8SBarry Smith   }
1445e42470aSBarry Smith   return 0;
1455e42470aSBarry Smith }
1465e42470aSBarry Smith 
1475e42470aSBarry Smith /*@
1485e42470aSBarry Smith    SNESDefaultConverged - Default test for monitoring the convergence
1490de89847SLois Curfman McInnes    of the SNES solvers.
1505e42470aSBarry Smith 
1515e42470aSBarry Smith    Input Parameters:
1520de89847SLois Curfman McInnes .  snes - the SNES context
1535e42470aSBarry Smith .  xnorm - 2-norm of current iterate
1545e42470aSBarry Smith .  pnorm - 2-norm of current step
1555e42470aSBarry Smith .  fnorm - 2-norm of residual
1560de89847SLois Curfman McInnes .  dummy - unused context
1575e42470aSBarry Smith 
1585e42470aSBarry Smith    Returns:
1595e42470aSBarry Smith $  2  if  ( fnorm < atol ),
1605e42470aSBarry Smith $  3  if  ( pnorm < xtol*xnorm ),
1615e42470aSBarry Smith $ -2  if  ( nres > max_res ),
1625e42470aSBarry Smith $  0  otherwise,
1635e42470aSBarry Smith 
1645e42470aSBarry Smith    where
1655e42470aSBarry Smith $    atol    - absolute residual norm tolerance,
1660de89847SLois Curfman McInnes $              set with SNESSetAbsoluteTolerance()
1675e42470aSBarry Smith $    max_res - maximum number of residual evaluations,
1680de89847SLois Curfman McInnes $              set with SNESSetMaxResidualEvaluations()
1695e42470aSBarry Smith $    nres    - number of residual evaluations
1705e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
1710de89847SLois Curfman McInnes $              set with SNESSetRelativeTolerance()
1725e42470aSBarry Smith 
1730de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence
1745e42470aSBarry Smith 
1750de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest()
1765e42470aSBarry Smith @*/
1775e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm,
1785e42470aSBarry Smith                          void *dummy)
1795e42470aSBarry Smith {
1805e42470aSBarry Smith   if (fnorm < snes->atol) {
181a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
182a67c8522SLois Curfman McInnes       "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol);
1835e42470aSBarry Smith     return 2;
1845e42470aSBarry Smith   }
1855e42470aSBarry Smith   if (pnorm < snes->xtol*(xnorm)) {
186a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
187a67c8522SLois Curfman McInnes       "Converged due to small update length  %g < %g*%g\n",
1885e42470aSBarry Smith        pnorm,snes->xtol,xnorm);
1895e42470aSBarry Smith     return 3;
1905e42470aSBarry Smith   }
19149e3953dSBarry Smith   if (snes->nfuncs > snes->max_funcs) {
192a67c8522SLois Curfman McInnes     PLogInfo((PetscObject)snes,
193*ddbbdb52SLois Curfman McInnes       "Exceeded maximum number of function evaluations: %d > %d\n",
19449e3953dSBarry Smith        snes->nfuncs, snes->max_funcs );
195a67c8522SLois Curfman McInnes     return -2;
196a67c8522SLois Curfman McInnes   }
1975e42470aSBarry Smith   return 0;
1985e42470aSBarry Smith }
1995e42470aSBarry Smith 
2005e76c431SBarry Smith /* ------------------------------------------------------------ */
2015e76c431SBarry Smith /*ARGSUSED*/
2025e76c431SBarry Smith /*@
2035e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
2045e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
2055e76c431SBarry Smith    to serve as a template and is not recommended for general use.
2065e76c431SBarry Smith 
2075e76c431SBarry Smith    Input Parameters:
2085e42470aSBarry Smith .  snes - nonlinear context
2095e76c431SBarry Smith .  x - current iterate
2105e76c431SBarry Smith .  f - residual evaluated at x
2115e76c431SBarry Smith .  y - search direction (contains new iterate on output)
2125e76c431SBarry Smith .  w - work vector
2135e76c431SBarry Smith .  fnorm - 2-norm of f
2145e76c431SBarry Smith 
215c4a48953SLois Curfman McInnes    Output Parameters:
2165e76c431SBarry Smith .  g - residual evaluated at new iterate y
2175e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2185e76c431SBarry Smith .  gnorm - 2-norm of g
2195e76c431SBarry Smith .  ynorm - 2-norm of search length
220761aaf1bSLois Curfman McInnes .  flag - set to 0, indicating a successful line search
2215e76c431SBarry Smith 
222c4a48953SLois Curfman McInnes    Options Database Key:
223c4a48953SLois Curfman McInnes $  -snes_line_search basic
224c4a48953SLois Curfman McInnes 
22528ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
22628ae5a21SLois Curfman McInnes 
227f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
228a935fc98SLois Curfman McInnes           SNESSetLineSearchRoutine()
2295e76c431SBarry Smith @*/
2305e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
231761aaf1bSLois Curfman McInnes               double fnorm, double *ynorm, double *gnorm,int *flag )
2325e76c431SBarry Smith {
2335e42470aSBarry Smith   int    ierr;
2345e42470aSBarry Smith   Scalar one = 1.0;
235761aaf1bSLois Curfman McInnes   *flag = 0;
2367857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
2375e42470aSBarry Smith   VecNorm(y,ynorm);                            /* ynorm = || y || */
2385e42470aSBarry Smith   VecAXPY(&one,x,y);	                       /* y <- x + y      */
23978b31e54SBarry Smith   ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr);
2405e42470aSBarry Smith   VecNorm(g,gnorm); 	                       /* gnorm = || g || */
2417857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
242761aaf1bSLois Curfman McInnes   return 0;
2435e76c431SBarry Smith }
2445e76c431SBarry Smith /* ------------------------------------------------------------------ */
2455e76c431SBarry Smith /*@
246f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
247f59ffdedSLois Curfman McInnes    is the default line search method.
2485e76c431SBarry Smith 
2495e76c431SBarry Smith    Input Parameters:
2505e42470aSBarry Smith .  snes - nonlinear context
2515e76c431SBarry Smith .  x - current iterate
2525e76c431SBarry Smith .  f - residual evaluated at x
2535e76c431SBarry Smith .  y - search direction (contains new iterate on output)
2545e76c431SBarry Smith .  w - work vector
2555e76c431SBarry Smith .  fnorm - 2-norm of f
2565e76c431SBarry Smith 
2575e76c431SBarry Smith    Output parameters:
2585e76c431SBarry Smith .  g - residual evaluated at new iterate y
2595e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2605e76c431SBarry Smith .  gnorm - 2-norm of g
2615e76c431SBarry Smith .  ynorm - 2-norm of search length
262761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
2635e76c431SBarry Smith 
264c4a48953SLois Curfman McInnes    Options Database Key:
265c4a48953SLois Curfman McInnes $  -snes_line_search cubic
266c4a48953SLois Curfman McInnes 
2675e76c431SBarry Smith    Notes:
2685e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
2695e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
2705e76c431SBarry Smith 
27128ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
27228ae5a21SLois Curfman McInnes 
273f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
2745e76c431SBarry Smith @*/
2755e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
276761aaf1bSLois Curfman McInnes                 double fnorm, double *ynorm, double *gnorm,int *flag)
2775e76c431SBarry Smith {
2785e42470aSBarry Smith   double  steptol, initslope;
2795e42470aSBarry Smith   double  lambdaprev, gnormprev;
2805e76c431SBarry Smith   double  a, b, d, t1, t2;
2816b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2825e42470aSBarry Smith   Scalar  cinitslope,clambda;
2836b5873e3SBarry Smith #endif
2845e42470aSBarry Smith   int     ierr,count;
2855e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2865e42470aSBarry Smith   Scalar  one = 1.0,scale;
2875e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2885e76c431SBarry Smith 
2897857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
290761aaf1bSLois Curfman McInnes   *flag = 0;
2915e76c431SBarry Smith   alpha   = neP->alpha;
2925e76c431SBarry Smith   maxstep = neP->maxstep;
2935e76c431SBarry Smith   steptol = neP->steptol;
2945e76c431SBarry Smith 
2955e42470aSBarry Smith   VecNorm(y, ynorm );
2965e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2975e42470aSBarry Smith     scale = maxstep/(*ynorm);
2986b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2996b5873e3SBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale));
3006b5873e3SBarry Smith #else
3015e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
3026b5873e3SBarry Smith #endif
3035e42470aSBarry Smith     VecScale(&scale, y );
3045e76c431SBarry Smith     *ynorm = maxstep;
3055e76c431SBarry Smith   }
3065e76c431SBarry Smith   minlambda = steptol/(*ynorm);
3075e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3085e42470aSBarry Smith   VecDot(f, y, &cinitslope );
3095e42470aSBarry Smith   initslope = real(cinitslope);
3105e42470aSBarry Smith #else
3115e42470aSBarry Smith   VecDot(f, y, &initslope );
3125e42470aSBarry Smith #endif
3135e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
3145e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
3155e76c431SBarry Smith 
3165e42470aSBarry Smith   VecCopy(y, w );
3175e42470aSBarry Smith   VecAXPY(&one, x, w );
31878b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
3195e42470aSBarry Smith   VecNorm(g, gnorm );
3205e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
3215e42470aSBarry Smith       VecCopy(w, y );
3225e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
323d93a2b8dSBarry Smith       goto theend;
3245e76c431SBarry Smith   }
3255e76c431SBarry Smith 
3265e76c431SBarry Smith   /* Fit points with quadratic */
3275e76c431SBarry Smith   lambda = 1.0; count = 0;
3285e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
3295e76c431SBarry Smith   lambdaprev = lambda;
3305e76c431SBarry Smith   gnormprev = *gnorm;
3315e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
3325e76c431SBarry Smith       lambda = .1*lambda;
3335e76c431SBarry Smith   } else lambda = lambdatemp;
3345e42470aSBarry Smith   VecCopy(x, w );
3355e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3365e42470aSBarry Smith   clambda = lambda; VecAXPY(&clambda, y, w );
3375e42470aSBarry Smith #else
3385e42470aSBarry Smith   VecAXPY(&lambda, y, w );
3395e42470aSBarry Smith #endif
34078b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
3415e42470aSBarry Smith   VecNorm(g, gnorm );
3425e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
3435e42470aSBarry Smith       VecCopy(w, y );
3445e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
345d93a2b8dSBarry Smith       goto theend;
3465e76c431SBarry Smith   }
3475e76c431SBarry Smith 
3485e76c431SBarry Smith   /* Fit points with cubic */
3495e76c431SBarry Smith   count = 1;
3505e76c431SBarry Smith   while (1) {
3515e76c431SBarry Smith       if (lambda <= minlambda) { /* bad luck; use full step */
3525e42470aSBarry Smith            PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
3535e42470aSBarry Smith            PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
3545e76c431SBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
3555e42470aSBarry Smith            VecCopy(w, y );
356761aaf1bSLois Curfman McInnes            *flag = -1; break;
3575e76c431SBarry Smith       }
3585e76c431SBarry Smith       t1 = *gnorm - fnorm - lambda*initslope;
3595e76c431SBarry Smith       t2 = gnormprev  - fnorm - lambdaprev*initslope;
3605e76c431SBarry Smith       a = (t1/(lambda*lambda) -
3615e76c431SBarry Smith                 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3625e76c431SBarry Smith       b = (-lambdaprev*t1/(lambda*lambda) +
3635e76c431SBarry Smith                 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3645e76c431SBarry Smith       d = b*b - 3*a*initslope;
3655e76c431SBarry Smith       if (d < 0.0) d = 0.0;
3665e76c431SBarry Smith       if (a == 0.0) {
3675e76c431SBarry Smith          lambdatemp = -initslope/(2.0*b);
3685e76c431SBarry Smith       } else {
3695e76c431SBarry Smith          lambdatemp = (-b + sqrt(d))/(3.0*a);
3705e76c431SBarry Smith       }
3715e76c431SBarry Smith       if (lambdatemp > .5*lambda) {
3725e76c431SBarry Smith          lambdatemp = .5*lambda;
3735e76c431SBarry Smith       }
3745e76c431SBarry Smith       lambdaprev = lambda;
3755e76c431SBarry Smith       gnormprev = *gnorm;
3765e76c431SBarry Smith       if (lambdatemp <= .1*lambda) {
3775e76c431SBarry Smith          lambda = .1*lambda;
3785e76c431SBarry Smith       }
3795e76c431SBarry Smith       else lambda = lambdatemp;
3805e42470aSBarry Smith       VecCopy( x, w );
3815e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3825e42470aSBarry Smith       VecAXPY(&clambda, y, w );
3835e42470aSBarry Smith #else
3845e42470aSBarry Smith       VecAXPY(&lambda, y, w );
3855e42470aSBarry Smith #endif
38678b31e54SBarry Smith       ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
3875e42470aSBarry Smith       VecNorm(g, gnorm );
3885e76c431SBarry Smith       if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
3895e42470aSBarry Smith          VecCopy(w, y );
3905e42470aSBarry Smith          PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
391761aaf1bSLois Curfman McInnes          *flag = -1; break;
3925e76c431SBarry Smith       }
3935e76c431SBarry Smith       count++;
3945e76c431SBarry Smith    }
395d93a2b8dSBarry Smith   theend:
3967857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3975e42470aSBarry Smith   return 0;
3985e76c431SBarry Smith }
3995e42470aSBarry Smith /*@
4005e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
4015e76c431SBarry Smith 
4025e42470aSBarry Smith    Input Parameters:
403f59ffdedSLois Curfman McInnes .  snes - the SNES context
4045e42470aSBarry Smith .  x - current iterate
4055e42470aSBarry Smith .  f - residual evaluated at x
4065e42470aSBarry Smith .  y - search direction (contains new iterate on output)
4075e42470aSBarry Smith .  w - work vector
4085e42470aSBarry Smith .  fnorm - 2-norm of f
4095e42470aSBarry Smith 
410c4a48953SLois Curfman McInnes    Output Parameters:
4115e42470aSBarry Smith .  g - residual evaluated at new iterate y
4125e42470aSBarry Smith .  y - new iterate (contains search direction on input)
4135e42470aSBarry Smith .  gnorm - 2-norm of g
4145e42470aSBarry Smith .  ynorm - 2-norm of search length
415761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
4165e42470aSBarry Smith 
417c4a48953SLois Curfman McInnes    Options Database Key:
418c4a48953SLois Curfman McInnes $  -snes_line_search quadratic
419c4a48953SLois Curfman McInnes 
4205e42470aSBarry Smith    Notes:
421f59ffdedSLois Curfman McInnes    Use SNESSetLineSearchRoutine()
422f59ffdedSLois Curfman McInnes    to set this routine within the SNES_NLS method.
4235e42470aSBarry Smith 
424f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
425f59ffdedSLois Curfman McInnes 
426f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
4275e42470aSBarry Smith @*/
4285e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
429761aaf1bSLois Curfman McInnes                     double fnorm, double *ynorm, double *gnorm,int *flag)
4305e76c431SBarry Smith {
4315e42470aSBarry Smith   double  steptol, initslope;
4325e42470aSBarry Smith   double  lambdaprev, gnormprev;
4336b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
4345e42470aSBarry Smith   Scalar  cinitslope,clambda;
4356b5873e3SBarry Smith #endif
4365e42470aSBarry Smith   int     ierr,count;
4375e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
4385e42470aSBarry Smith   Scalar  one = 1.0,scale;
4395e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
4405e76c431SBarry Smith 
4417857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
442761aaf1bSLois Curfman McInnes   *flag = 0;
4435e42470aSBarry Smith   alpha   = neP->alpha;
4445e42470aSBarry Smith   maxstep = neP->maxstep;
4455e42470aSBarry Smith   steptol = neP->steptol;
4465e76c431SBarry Smith 
4475e42470aSBarry Smith   VecNorm(y, ynorm );
4485e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
4495e42470aSBarry Smith     scale = maxstep/(*ynorm);
4505e42470aSBarry Smith     VecScale(&scale, y );
4515e42470aSBarry Smith     *ynorm = maxstep;
4525e76c431SBarry Smith   }
4535e42470aSBarry Smith   minlambda = steptol/(*ynorm);
4545e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4555e42470aSBarry Smith   VecDot(f, y, &cinitslope );
4565e42470aSBarry Smith   initslope = real(cinitslope);
4575e42470aSBarry Smith #else
4585e42470aSBarry Smith   VecDot(f, y, &initslope );
4595e42470aSBarry Smith #endif
4605e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
4615e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
4625e42470aSBarry Smith 
4635e42470aSBarry Smith   VecCopy(y, w );
4645e42470aSBarry Smith   VecAXPY(&one, x, w );
46578b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
4665e42470aSBarry Smith   VecNorm(g, gnorm );
4675e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
4685e42470aSBarry Smith       VecCopy(w, y );
4695e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
470d93a2b8dSBarry Smith       goto theend;
4715e42470aSBarry Smith   }
4725e42470aSBarry Smith 
4735e42470aSBarry Smith   /* Fit points with quadratic */
4745e42470aSBarry Smith   lambda = 1.0; count = 0;
4755e42470aSBarry Smith   count = 1;
4765e42470aSBarry Smith   while (1) {
4775e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
4785e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
4795e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
4805e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
4815e42470aSBarry Smith       VecCopy(w, y );
482761aaf1bSLois Curfman McInnes       *flag = -1; break;
4835e42470aSBarry Smith     }
4845e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4855e42470aSBarry Smith     lambdaprev = lambda;
4865e42470aSBarry Smith     gnormprev = *gnorm;
4875e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4885e42470aSBarry Smith       lambda = .1*lambda;
4895e42470aSBarry Smith     } else lambda = lambdatemp;
4905e42470aSBarry Smith     VecCopy(x, w );
4915e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4925e42470aSBarry Smith     clambda = lambda; VecAXPY(&clambda, y, w );
4935e42470aSBarry Smith #else
4945e42470aSBarry Smith     VecAXPY(&lambda, y, w );
4955e42470aSBarry Smith #endif
49678b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
4975e42470aSBarry Smith     VecNorm(g, gnorm );
4985e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
4995e42470aSBarry Smith       VecCopy(w, y );
5005e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
50106259719SBarry Smith       break;
5025e42470aSBarry Smith     }
5035e42470aSBarry Smith     count++;
5045e42470aSBarry Smith   }
505d93a2b8dSBarry Smith   theend:
5067857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
5075e42470aSBarry Smith   return 0;
5085e76c431SBarry Smith }
5095e76c431SBarry Smith /* ------------------------------------------------------------ */
510b1f0a012SBarry Smith /*@
5115e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
512fbe28522SBarry Smith    by the method SNES_LS.
5135e76c431SBarry Smith 
5145e76c431SBarry Smith    Input Parameters:
515eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
5165e76c431SBarry Smith .  func - pointer to int function
5175e76c431SBarry Smith 
518c4a48953SLois Curfman McInnes    Available Routines:
519f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
520f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
521f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
5225e76c431SBarry Smith 
523c4a48953SLois Curfman McInnes     Options Database Keys:
524c4a48953SLois Curfman McInnes $   -snes_line_search [basic,quadratic,cubic]
525c4a48953SLois Curfman McInnes 
5265e76c431SBarry Smith    Calling sequence of func:
527f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
528761aaf1bSLois Curfman McInnes          Vec w, double fnorm, double *ynorm,
529761aaf1bSLois Curfman McInnes          double *gnorm, *flag)
5305e76c431SBarry Smith 
5315e76c431SBarry Smith     Input parameters for func:
5325e42470aSBarry Smith .   snes - nonlinear context
5335e76c431SBarry Smith .   x - current iterate
5345e76c431SBarry Smith .   f - residual evaluated at x
5355e76c431SBarry Smith .   y - search direction (contains new iterate on output)
5365e76c431SBarry Smith .   w - work vector
5375e76c431SBarry Smith .   fnorm - 2-norm of f
5385e76c431SBarry Smith 
5395e76c431SBarry Smith     Output parameters for func:
5405e76c431SBarry Smith .   g - residual evaluated at new iterate y
5415e76c431SBarry Smith .   y - new iterate (contains search direction on input)
5425e76c431SBarry Smith .   gnorm - 2-norm of g
5435e76c431SBarry Smith .   ynorm - 2-norm of search length
544761aaf1bSLois Curfman McInnes .   flag - set to 0 if the line search succeeds; a nonzero integer
545761aaf1bSLois Curfman McInnes            on failure.
546f59ffdedSLois Curfman McInnes 
547f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
548f59ffdedSLois Curfman McInnes 
549f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
5505e76c431SBarry Smith @*/
5515e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
552761aaf1bSLois Curfman McInnes                              double,double *,double*,int*) )
5535e76c431SBarry Smith {
554fbe28522SBarry Smith   if ((snes)->type == SNES_NLS)
5555e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
5565e42470aSBarry Smith   return 0;
5575e76c431SBarry Smith }
5585e42470aSBarry Smith 
5595e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
5605e42470aSBarry Smith {
5615e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5625e42470aSBarry Smith   fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n");
5635e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha);
5645e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep);
5655e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol);
5666b5873e3SBarry Smith   return 0;
5675e42470aSBarry Smith }
5685e42470aSBarry Smith 
569a935fc98SLois Curfman McInnes static int SNESView_LS(PetscObject obj,Viewer viewer)
570a935fc98SLois Curfman McInnes {
571a935fc98SLois Curfman McInnes   SNES    snes = (SNES)obj;
572a935fc98SLois Curfman McInnes   SNES_LS *ls = (SNES_LS *)snes->data;
573a935fc98SLois Curfman McInnes   FILE    *fd = ViewerFileGetPointer_Private(viewer);
574a935fc98SLois Curfman McInnes   char    *cstring;
575a935fc98SLois Curfman McInnes 
576a935fc98SLois Curfman McInnes   if (ls->LineSearch == SNESNoLineSearch)
577a935fc98SLois Curfman McInnes     cstring = "SNESNoLineSearch";
578a935fc98SLois Curfman McInnes   else if (ls->LineSearch == SNESQuadraticLineSearch)
579a935fc98SLois Curfman McInnes     cstring = "SNESQuadraticLineSearch";
580a935fc98SLois Curfman McInnes   else if (ls->LineSearch == SNESCubicLineSearch)
581a935fc98SLois Curfman McInnes     cstring = "SNESCubicLineSearch";
582a935fc98SLois Curfman McInnes   else
583a935fc98SLois Curfman McInnes     cstring = "unknown";
584a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    line search variant: %s\n",cstring);
585a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    alpha=%g, maxstep=%g, steptol=%g\n",
586a935fc98SLois Curfman McInnes      ls->alpha,ls->maxstep,ls->steptol);
587a935fc98SLois Curfman McInnes   return 0;
588a935fc98SLois Curfman McInnes }
589a935fc98SLois Curfman McInnes 
5905e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5915e42470aSBarry Smith {
5925e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5935e42470aSBarry Smith   char    ver[16];
5945e42470aSBarry Smith   double  tmp;
5955e42470aSBarry Smith 
596df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) {
5975e42470aSBarry Smith     ls->alpha = tmp;
5985e42470aSBarry Smith   }
599df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) {
6005e42470aSBarry Smith     ls->maxstep = tmp;
6015e42470aSBarry Smith   }
602df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) {
6035e42470aSBarry Smith     ls->steptol = tmp;
6045e42470aSBarry Smith   }
605df60cc22SBarry Smith   if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) {
6065e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
6075e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
6085e42470aSBarry Smith     }
6095e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
6105e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
6115e42470aSBarry Smith     }
6125e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
6135e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
6145e42470aSBarry Smith     }
6158c48e012SBarry Smith     else {SETERRQ(1,"SNESSetFromOptions_LS: Unknown line search");}
6165e42470aSBarry Smith   }
6175e42470aSBarry Smith   return 0;
6185e42470aSBarry Smith }
6195e42470aSBarry Smith 
6205e42470aSBarry Smith /* ------------------------------------------------------------ */
6215e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
6225e42470aSBarry Smith {
6235e42470aSBarry Smith   SNES_LS *neP;
6245e42470aSBarry Smith 
625fbe28522SBarry Smith   snes->type		= SNES_NLS;
626a935fc98SLois Curfman McInnes   snes->method_class	= SNES_T;
62749e3953dSBarry Smith   snes->setup		= SNESSetUp_LS;
62849e3953dSBarry Smith   snes->solve		= SNESSolve_LS;
6295e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
6305e42470aSBarry Smith   snes->Converged	= SNESDefaultConverged;
63149e3953dSBarry Smith   snes->printhelp       = SNESPrintHelp_LS;
63249e3953dSBarry Smith   snes->setfromoptions  = SNESSetFromOptions_LS;
633a935fc98SLois Curfman McInnes   snes->view            = SNESView_LS;
6345e42470aSBarry Smith 
63578b31e54SBarry Smith   neP			= PETSCNEW(SNES_LS);   CHKPTRQ(neP);
6365e42470aSBarry Smith   snes->data    	= (void *) neP;
6375e42470aSBarry Smith   neP->alpha		= 1.e-4;
6385e42470aSBarry Smith   neP->maxstep		= 1.e8;
6395e42470aSBarry Smith   neP->steptol		= 1.e-12;
6405e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
6415e42470aSBarry Smith   return 0;
6425e42470aSBarry Smith }
6435e42470aSBarry Smith 
6445e42470aSBarry Smith 
6455e42470aSBarry Smith 
6465e42470aSBarry Smith 
647