xref: /petsc/src/snes/impls/ls/ls.c (revision 70f55243aafb320636e2a54ff30cab5d1e8d3d7b)
15e76c431SBarry Smith #ifndef lint
2*70f55243SBarry Smith static char vcid[] = "$Id: ls.c,v 1.68 1996/04/20 04:21:35 bsmith Exp bsmith $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6*70f55243SBarry Smith #include "src/snes/impls/ls/ls.h"
7b16a3bb1SBarry Smith #include "pinclude/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
24393d2d9aSLois Curfman McInnes     and Schnabel.
255e76c431SBarry Smith */
265e76c431SBarry Smith 
27f63b844aSLois Curfman McInnes int SNESSolve_EQ_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;
31112a2221SBarry Smith   MatStructure  flg = 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 
44cddf8d76SBarry Smith   ierr = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr);               /* xnorm = || X || */
455334005bSBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);          /*  F(X)      */
46cddf8d76SBarry Smith   ierr = VecNorm(F,NORM_2,&fnorm); CHKERRQ(ierr);	        /* fnorm <- ||F||  */
475e42470aSBarry Smith   snes->norm = fnorm;
485e76c431SBarry Smith   if (history && history_len > 0) history[0] = fnorm;
4994a424c1SBarry Smith   SNESMonitor(snes,0,fnorm);
505e76c431SBarry Smith 
51d034289bSBarry Smith   /* set parameter for default relative tolerance convergence test */
52d034289bSBarry Smith   snes->ttol = fnorm*snes->rtol;
53d034289bSBarry 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 */
585334005bSBarry Smith     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
595334005bSBarry Smith     ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg); CHKERRQ(ierr);
6078b31e54SBarry Smith     ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr);
6181b6cf68SLois Curfman McInnes     ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr);
62ddd12767SBarry Smith     ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail);CHKERRQ(ierr);
63761aaf1bSLois Curfman McInnes     if (lsfail) snes->nfailures++;
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;
71cddf8d76SBarry Smith     ierr = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr);	/* xnorm = || X || */
7294a424c1SBarry Smith     SNESMonitor(snes,i+1,fnorm);
735e76c431SBarry Smith 
745e76c431SBarry Smith     /* Test for convergence */
75bbb6d6a8SBarry Smith     if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
7639e2f89bSBarry Smith       if (X != snes->vec_sol) {
77393d2d9aSLois Curfman McInnes         ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
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   }
8452392280SLois Curfman McInnes   if (i == maxits) {
8594a424c1SBarry Smith     PLogInfo(snes,
86f63b844aSLois Curfman McInnes       "SNESSolve_EQ_LS: Maximum number of iterations has been reached: %d\n",maxits);
8752392280SLois Curfman McInnes     i--;
8852392280SLois Curfman McInnes   }
895e42470aSBarry Smith   *outits = i+1;
905e42470aSBarry Smith   return 0;
915e76c431SBarry Smith }
925e76c431SBarry Smith /* ------------------------------------------------------------ */
93f63b844aSLois Curfman McInnes int SNESSetUp_EQ_LS(SNES snes )
945e76c431SBarry Smith {
955e42470aSBarry Smith   int ierr;
9681b6cf68SLois Curfman McInnes   snes->nwork = 4;
97d7e8b826SBarry Smith   ierr = VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work); CHKERRQ(ierr);
985e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work);
9981b6cf68SLois Curfman McInnes   snes->vec_sol_update_always = snes->work[3];
1005e42470aSBarry Smith   return 0;
1015e76c431SBarry Smith }
1025e76c431SBarry Smith /* ------------------------------------------------------------ */
103f63b844aSLois Curfman McInnes int SNESDestroy_EQ_LS(PetscObject obj)
1045e76c431SBarry Smith {
1055e42470aSBarry Smith   SNES snes = (SNES) obj;
106393d2d9aSLois Curfman McInnes   int  ierr;
10721c89e3eSBarry Smith   if (snes->work) {
1084b0e389bSBarry Smith     ierr = VecDestroyVecs(snes->work,snes->nwork); CHKERRQ(ierr);
10921c89e3eSBarry Smith   }
1100452661fSBarry Smith   PetscFree(snes->data);
1115e42470aSBarry Smith   return 0;
1125e76c431SBarry Smith }
1135e76c431SBarry Smith /* ------------------------------------------------------------ */
1145e76c431SBarry Smith /*ARGSUSED*/
1154b828684SBarry Smith /*@C
1165e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1175e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1185e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1195e76c431SBarry Smith 
1205e76c431SBarry Smith    Input Parameters:
1215e42470aSBarry Smith .  snes - nonlinear context
1225e76c431SBarry Smith .  x - current iterate
1235e76c431SBarry Smith .  f - residual evaluated at x
1245e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1255e76c431SBarry Smith .  w - work vector
1265e76c431SBarry Smith .  fnorm - 2-norm of f
1275e76c431SBarry Smith 
128c4a48953SLois Curfman McInnes    Output Parameters:
1295e76c431SBarry Smith .  g - residual evaluated at new iterate y
1305e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1315e76c431SBarry Smith .  gnorm - 2-norm of g
1325e76c431SBarry Smith .  ynorm - 2-norm of search length
133761aaf1bSLois Curfman McInnes .  flag - set to 0, indicating a successful line search
1345e76c431SBarry Smith 
135c4a48953SLois Curfman McInnes    Options Database Key:
136c4a48953SLois Curfman McInnes $  -snes_line_search basic
137c4a48953SLois Curfman McInnes 
13828ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
13928ae5a21SLois Curfman McInnes 
140f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
14177c4ece6SBarry Smith           SNESSetLineSearch()
1425e76c431SBarry Smith @*/
1435e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
144761aaf1bSLois Curfman McInnes                      double fnorm, double *ynorm, double *gnorm,int *flag )
1455e76c431SBarry Smith {
1465e42470aSBarry Smith   int    ierr;
1475334005bSBarry Smith   Scalar mone = -1.0;
1485334005bSBarry Smith 
149761aaf1bSLois Curfman McInnes   *flag = 0;
1507857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
151cddf8d76SBarry Smith   ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr);              /* ynorm = || y || */
1525334005bSBarry Smith   ierr = VecAYPX(&mone,x,y); CHKERRQ(ierr);                   /* y <- x + y      */
1535334005bSBarry Smith   ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr);        /*  F(y)      */
154cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);              /* gnorm = || g || */
1557857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
156761aaf1bSLois Curfman McInnes   return 0;
1575e76c431SBarry Smith }
1585e76c431SBarry Smith /* ------------------------------------------------------------------ */
1594b828684SBarry Smith /*@C
160f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
161f59ffdedSLois Curfman McInnes    is the default line search method.
1625e76c431SBarry Smith 
1635e76c431SBarry Smith    Input Parameters:
1645e42470aSBarry Smith .  snes - nonlinear context
1655e76c431SBarry Smith .  x - current iterate
1665e76c431SBarry Smith .  f - residual evaluated at x
1675e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1685e76c431SBarry Smith .  w - work vector
1695e76c431SBarry Smith .  fnorm - 2-norm of f
1705e76c431SBarry Smith 
171393d2d9aSLois Curfman McInnes    Output Parameters:
1725e76c431SBarry Smith .  g - residual evaluated at new iterate y
1735e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1745e76c431SBarry Smith .  gnorm - 2-norm of g
1755e76c431SBarry Smith .  ynorm - 2-norm of search length
176761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
1775e76c431SBarry Smith 
178c4a48953SLois Curfman McInnes    Options Database Key:
179c4a48953SLois Curfman McInnes $  -snes_line_search cubic
180c4a48953SLois Curfman McInnes 
1815e76c431SBarry Smith    Notes:
1825e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
1835e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
1845e76c431SBarry Smith 
18528ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
18628ae5a21SLois Curfman McInnes 
18777c4ece6SBarry Smith .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearch()
1885e76c431SBarry Smith @*/
1895e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
190761aaf1bSLois Curfman McInnes                         double fnorm, double *ynorm, double *gnorm,int *flag)
1915e76c431SBarry Smith {
192ddd12767SBarry Smith   double  steptol, initslope,lambdaprev, gnormprev,a, b, d, t1, t2;
193ddd12767SBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
1946b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
1955e42470aSBarry Smith   Scalar  cinitslope,clambda;
1966b5873e3SBarry Smith #endif
1975e42470aSBarry Smith   int     ierr, count;
1985e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
1995334005bSBarry Smith   Scalar  mone = -1.0,scale;
2005e76c431SBarry Smith 
2017857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
202761aaf1bSLois Curfman McInnes   *flag   = 0;
2035e76c431SBarry Smith   alpha   = neP->alpha;
2045e76c431SBarry Smith   maxstep = neP->maxstep;
2055e76c431SBarry Smith   steptol = neP->steptol;
2065e76c431SBarry Smith 
207cddf8d76SBarry Smith   ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr);
2085e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2095e42470aSBarry Smith     scale = maxstep/(*ynorm);
2106b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
21194a424c1SBarry Smith     PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",real(scale));
2126b5873e3SBarry Smith #else
21394a424c1SBarry Smith     PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",scale);
2146b5873e3SBarry Smith #endif
215393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
2165e76c431SBarry Smith     *ynorm = maxstep;
2175e76c431SBarry Smith   }
2185e76c431SBarry Smith   minlambda = steptol/(*ynorm);
219a703fe33SLois Curfman McInnes   ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr);
2205e42470aSBarry Smith #if defined(PETSC_COMPLEX)
221a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr);
2225e42470aSBarry Smith   initslope = real(cinitslope);
2235e42470aSBarry Smith #else
224a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&initslope); CHKERRQ(ierr);
2255e42470aSBarry Smith #endif
2265e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2275e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2285e76c431SBarry Smith 
229393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
2305334005bSBarry Smith   ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr);
23178b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
232cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm);
2335e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
234393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
23594a424c1SBarry Smith     PLogInfo(snes,"SNESCubicLineSearch: Using full step\n");
236d93a2b8dSBarry Smith     goto theend;
2375e76c431SBarry Smith   }
2385e76c431SBarry Smith 
2395e76c431SBarry Smith   /* Fit points with quadratic */
2405e76c431SBarry Smith   lambda = 1.0; count = 0;
241a703fe33SLois Curfman McInnes   lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
2425e76c431SBarry Smith   lambdaprev = lambda;
2435e76c431SBarry Smith   gnormprev = *gnorm;
244ddd12767SBarry Smith   if (lambdatemp <= .1*lambda) lambda = .1*lambda;
245ddd12767SBarry Smith   else lambda = lambdatemp;
246393d2d9aSLois Curfman McInnes   ierr   = VecCopy(x,w); CHKERRQ(ierr);
2475334005bSBarry Smith   lambda = -lambda;
2485e42470aSBarry Smith #if defined(PETSC_COMPLEX)
249393d2d9aSLois Curfman McInnes   clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
2505e42470aSBarry Smith #else
251393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
2525e42470aSBarry Smith #endif
25378b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
254cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
2555e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
256393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
25794a424c1SBarry Smith     PLogInfo(snes,
2584b828684SBarry Smith              "SNESCubicLineSearch: Quadratically determined step, lambda %g\n",lambda);
259d93a2b8dSBarry Smith     goto theend;
2605e76c431SBarry Smith   }
2615e76c431SBarry Smith 
2625e76c431SBarry Smith   /* Fit points with cubic */
2635e76c431SBarry Smith   count = 1;
2645e76c431SBarry Smith   while (1) {
2655e76c431SBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
26694a424c1SBarry Smith       PLogInfo(snes,
2674b828684SBarry Smith          "SNESCubicLineSearch:Unable to find good step length! %d \n",count);
26894a424c1SBarry Smith       PLogInfo(snes,
269052efed2SBarry Smith          "SNESCubicLineSearch:f %g fnew %g ynorm %g lambda %g initial slope %g\n",fnorm,*gnorm,*ynorm,lambda,initslope);
270393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
271761aaf1bSLois Curfman McInnes       *flag = -1; break;
2725e76c431SBarry Smith     }
2735e76c431SBarry Smith     t1 = *gnorm - fnorm - lambda*initslope;
2745e76c431SBarry Smith     t2 = gnormprev  - fnorm - lambdaprev*initslope;
275ddd12767SBarry Smith     a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2765e76c431SBarry Smith     b = (-lambdaprev*t1/(lambda*lambda) +
2775e76c431SBarry Smith                              lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2785e76c431SBarry Smith     d = b*b - 3*a*initslope;
2795e76c431SBarry Smith     if (d < 0.0) d = 0.0;
2805e76c431SBarry Smith     if (a == 0.0) {
2815e76c431SBarry Smith       lambdatemp = -initslope/(2.0*b);
2825e76c431SBarry Smith     } else {
2835e76c431SBarry Smith       lambdatemp = (-b + sqrt(d))/(3.0*a);
2845e76c431SBarry Smith     }
2855e76c431SBarry Smith     if (lambdatemp > .5*lambda) {
2865e76c431SBarry Smith       lambdatemp = .5*lambda;
2875e76c431SBarry Smith     }
2885e76c431SBarry Smith     lambdaprev = lambda;
2895e76c431SBarry Smith     gnormprev = *gnorm;
2905e76c431SBarry Smith     if (lambdatemp <= .1*lambda) {
2915e76c431SBarry Smith       lambda = .1*lambda;
2925e76c431SBarry Smith     }
2935e76c431SBarry Smith     else lambda = lambdatemp;
294393d2d9aSLois Curfman McInnes     ierr = VecCopy( x, w ); CHKERRQ(ierr);
2955334005bSBarry Smith     lambda = -lambda;
2965e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2975334005bSBarry Smith     clambda = lambda;
298393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
2995e42470aSBarry Smith #else
300393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
3015e42470aSBarry Smith #endif
30278b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
303cddf8d76SBarry Smith     ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
3045e76c431SBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
305393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
30694a424c1SBarry Smith       PLogInfo(snes,
3074b828684SBarry Smith                 "SNESCubicLineSearch: Cubically determined step, lambda %g\n",lambda);
308761aaf1bSLois Curfman McInnes       *flag = -1; break;
3095e76c431SBarry Smith     }
3105e76c431SBarry Smith     count++;
3115e76c431SBarry Smith   }
312d93a2b8dSBarry Smith   theend:
3137857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3145e42470aSBarry Smith   return 0;
3155e76c431SBarry Smith }
31652392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
3174b828684SBarry Smith /*@C
3185e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3195e76c431SBarry Smith 
3205e42470aSBarry Smith    Input Parameters:
321f59ffdedSLois Curfman McInnes .  snes - the SNES context
3225e42470aSBarry Smith .  x - current iterate
3235e42470aSBarry Smith .  f - residual evaluated at x
3245e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3255e42470aSBarry Smith .  w - work vector
3265e42470aSBarry Smith .  fnorm - 2-norm of f
3275e42470aSBarry Smith 
328c4a48953SLois Curfman McInnes    Output Parameters:
3295e42470aSBarry Smith .  g - residual evaluated at new iterate y
3305e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3315e42470aSBarry Smith .  gnorm - 2-norm of g
3325e42470aSBarry Smith .  ynorm - 2-norm of search length
333761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
3345e42470aSBarry Smith 
335c4a48953SLois Curfman McInnes    Options Database Key:
336c4a48953SLois Curfman McInnes $  -snes_line_search quadratic
337c4a48953SLois Curfman McInnes 
3385e42470aSBarry Smith    Notes:
33977c4ece6SBarry Smith    Use SNESSetLineSearch()
340f63b844aSLois Curfman McInnes    to set this routine within the SNES_EQ_LS method.
3415e42470aSBarry Smith 
342f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
343f59ffdedSLois Curfman McInnes 
34477c4ece6SBarry Smith .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearch()
3455e42470aSBarry Smith @*/
3465e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
347761aaf1bSLois Curfman McInnes                            double fnorm, double *ynorm, double *gnorm,int *flag)
3485e76c431SBarry Smith {
349ddd12767SBarry Smith   double  steptol,initslope,lambdaprev,gnormprev,maxstep,minlambda,alpha,lambda,lambdatemp;
3506b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
3515e42470aSBarry Smith   Scalar  cinitslope,clambda;
3526b5873e3SBarry Smith #endif
3535e42470aSBarry Smith   int     ierr,count;
3545e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
3555334005bSBarry Smith   Scalar  mone = -1.0,scale;
3565e76c431SBarry Smith 
3577857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
358761aaf1bSLois Curfman McInnes   *flag = 0;
3595e42470aSBarry Smith   alpha   = neP->alpha;
3605e42470aSBarry Smith   maxstep = neP->maxstep;
3615e42470aSBarry Smith   steptol = neP->steptol;
3625e76c431SBarry Smith 
363cddf8d76SBarry Smith   VecNorm(y, NORM_2,ynorm );
3645e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
3655e42470aSBarry Smith     scale = maxstep/(*ynorm);
366393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
3675e42470aSBarry Smith     *ynorm = maxstep;
3685e76c431SBarry Smith   }
3695e42470aSBarry Smith   minlambda = steptol/(*ynorm);
370a703fe33SLois Curfman McInnes   ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr);
3715e42470aSBarry Smith #if defined(PETSC_COMPLEX)
372a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr);
3735e42470aSBarry Smith   initslope = real(cinitslope);
3745e42470aSBarry Smith #else
375a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&initslope); CHKERRQ(ierr);
3765e42470aSBarry Smith #endif
3775e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
3785e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
3795e42470aSBarry Smith 
380393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
3815334005bSBarry Smith   ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr);
38278b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
383cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
3845e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
385393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
38694a424c1SBarry Smith     PLogInfo(snes,"SNESQuadraticLineSearch: Using full step\n");
387d93a2b8dSBarry Smith     goto theend;
3885e42470aSBarry Smith   }
3895e42470aSBarry Smith 
3905e42470aSBarry Smith   /* Fit points with quadratic */
3915e42470aSBarry Smith   lambda = 1.0; count = 0;
3925e42470aSBarry Smith   count = 1;
3935e42470aSBarry Smith   while (1) {
3945e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
39594a424c1SBarry Smith       PLogInfo(snes,
3964b828684SBarry Smith           "SNESQuadraticLineSearch:Unable to find good step length! %d \n",count);
39794a424c1SBarry Smith       PLogInfo(snes,
398ddd12767SBarry Smith       "SNESQuadraticLineSearch:f %g fnew %g ynorm %g lambda %g\n",fnorm,*gnorm,*ynorm,lambda);
399393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
400761aaf1bSLois Curfman McInnes       *flag = -1; break;
4015e42470aSBarry Smith     }
402a703fe33SLois Curfman McInnes     lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
4035e42470aSBarry Smith     lambdaprev = lambda;
4045e42470aSBarry Smith     gnormprev = *gnorm;
4055e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4065e42470aSBarry Smith       lambda = .1*lambda;
4075e42470aSBarry Smith     } else lambda = lambdatemp;
408393d2d9aSLois Curfman McInnes     ierr = VecCopy(x,w); CHKERRQ(ierr);
4095334005bSBarry Smith     lambda = -lambda;
4105e42470aSBarry Smith #if defined(PETSC_COMPLEX)
411393d2d9aSLois Curfman McInnes     clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
4125e42470aSBarry Smith #else
413393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
4145e42470aSBarry Smith #endif
41578b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
416cddf8d76SBarry Smith     ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
4175e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
418393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
41994a424c1SBarry Smith       PLogInfo(snes,
420ddd12767SBarry Smith         "SNESQuadraticLineSearch:Quadratically determined step, lambda %g\n",lambda);
42106259719SBarry Smith       break;
4225e42470aSBarry Smith     }
4235e42470aSBarry Smith     count++;
4245e42470aSBarry Smith   }
425d93a2b8dSBarry Smith   theend:
4267857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4275e42470aSBarry Smith   return 0;
4285e76c431SBarry Smith }
4295e76c431SBarry Smith /* ------------------------------------------------------------ */
430c9e6a524SLois Curfman McInnes /*@C
43177c4ece6SBarry Smith    SNESSetLineSearch - Sets the line search routine to be used
432f63b844aSLois Curfman McInnes    by the method SNES_EQ_LS.
4335e76c431SBarry Smith 
4345e76c431SBarry Smith    Input Parameters:
435eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
4365e76c431SBarry Smith .  func - pointer to int function
4375e76c431SBarry Smith 
438c4a48953SLois Curfman McInnes    Available Routines:
439f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
440f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
441f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
4425e76c431SBarry Smith 
443c4a48953SLois Curfman McInnes     Options Database Keys:
444c4a48953SLois Curfman McInnes $   -snes_line_search [basic,quadratic,cubic]
445c4a48953SLois Curfman McInnes 
4465e76c431SBarry Smith    Calling sequence of func:
447f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
448761aaf1bSLois Curfman McInnes          Vec w, double fnorm, double *ynorm,
449761aaf1bSLois Curfman McInnes          double *gnorm, *flag)
4505e76c431SBarry Smith 
4515e76c431SBarry Smith     Input parameters for func:
4525e42470aSBarry Smith .   snes - nonlinear context
4535e76c431SBarry Smith .   x - current iterate
4545e76c431SBarry Smith .   f - residual evaluated at x
4555e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4565e76c431SBarry Smith .   w - work vector
4575e76c431SBarry Smith .   fnorm - 2-norm of f
4585e76c431SBarry Smith 
4595e76c431SBarry Smith     Output parameters for func:
4605e76c431SBarry Smith .   g - residual evaluated at new iterate y
4615e76c431SBarry Smith .   y - new iterate (contains search direction on input)
4625e76c431SBarry Smith .   gnorm - 2-norm of g
4635e76c431SBarry Smith .   ynorm - 2-norm of search length
464761aaf1bSLois Curfman McInnes .   flag - set to 0 if the line search succeeds; a nonzero integer
465761aaf1bSLois Curfman McInnes            on failure.
466f59ffdedSLois Curfman McInnes 
467f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
468f59ffdedSLois Curfman McInnes 
469f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
4705e76c431SBarry Smith @*/
47177c4ece6SBarry Smith int SNESSetLineSearch(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
472761aaf1bSLois Curfman McInnes                              double,double*,double*,int*))
4735e76c431SBarry Smith {
474f63b844aSLois Curfman McInnes   if ((snes)->type == SNES_EQ_LS) ((SNES_LS *)(snes->data))->LineSearch = func;
4755e42470aSBarry Smith   return 0;
4765e76c431SBarry Smith }
47752392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
478f63b844aSLois Curfman McInnes static int SNESPrintHelp_EQ_LS(SNES snes,char *p)
4795e42470aSBarry Smith {
4805e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
4816daaf66cSBarry Smith 
482f63b844aSLois Curfman McInnes   PetscPrintf(snes->comm," method SNES_EQ_LS (ls) for systems of nonlinear equations:\n");
48377c4ece6SBarry Smith   PetscPrintf(snes->comm,"   %ssnes_line_search [basic,quadratic,cubic]\n",p);
4849aca352dSLois Curfman McInnes   PetscPrintf(snes->comm,"   %ssnes_line_search_alpha <alpha> (default %g)\n",p,ls->alpha);
4859aca352dSLois Curfman McInnes   PetscPrintf(snes->comm,"   %ssnes_line_search_maxstep <max> (default %g)\n",p,ls->maxstep);
4869aca352dSLois Curfman McInnes   PetscPrintf(snes->comm,"   %ssnes_line_search_steptol <tol> (default %g)\n",p,ls->steptol);
4876b5873e3SBarry Smith   return 0;
4885e42470aSBarry Smith }
48952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
490f63b844aSLois Curfman McInnes static int SNESView_EQ_LS(PetscObject obj,Viewer viewer)
491a935fc98SLois Curfman McInnes {
492a935fc98SLois Curfman McInnes   SNES       snes = (SNES)obj;
493a935fc98SLois Curfman McInnes   SNES_LS    *ls = (SNES_LS *)snes->data;
494d636dbe3SBarry Smith   FILE       *fd;
49519bcc07fSBarry Smith   char       *cstr;
49651695f54SLois Curfman McInnes   int        ierr;
49719bcc07fSBarry Smith   ViewerType vtype;
498a935fc98SLois Curfman McInnes 
49919bcc07fSBarry Smith   ierr = ViewerGetType(viewer,&vtype); CHKERRQ(ierr);
50019bcc07fSBarry Smith   if (vtype  == ASCII_FILE_VIEWER || vtype == ASCII_FILES_VIEWER) {
50190ace30eSBarry Smith     ierr = ViewerASCIIGetPointer(viewer,&fd); CHKERRQ(ierr);
50219bcc07fSBarry Smith     if (ls->LineSearch == SNESNoLineSearch) cstr = "SNESNoLineSearch";
50319bcc07fSBarry Smith     else if (ls->LineSearch == SNESQuadraticLineSearch) cstr = "SNESQuadraticLineSearch";
50419bcc07fSBarry Smith     else if (ls->LineSearch == SNESCubicLineSearch) cstr = "SNESCubicLineSearch";
50519bcc07fSBarry Smith     else cstr = "unknown";
50677c4ece6SBarry Smith     PetscFPrintf(snes->comm,fd,"    line search variant: %s\n",cstr);
50777c4ece6SBarry Smith     PetscFPrintf(snes->comm,fd,"    alpha=%g, maxstep=%g, steptol=%g\n",
508a935fc98SLois Curfman McInnes                  ls->alpha,ls->maxstep,ls->steptol);
50919bcc07fSBarry Smith   }
510a935fc98SLois Curfman McInnes   return 0;
511a935fc98SLois Curfman McInnes }
51252392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
513f63b844aSLois Curfman McInnes static int SNESSetFromOptions_EQ_LS(SNES snes)
5145e42470aSBarry Smith {
5155e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5165e42470aSBarry Smith   char    ver[16];
5175e42470aSBarry Smith   double  tmp;
51825cdf11fSBarry Smith   int     ierr,flg;
5195e42470aSBarry Smith 
520ee8b94d1SSatish Balay   ierr = OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp, &flg);CHKERRQ(ierr);
52125cdf11fSBarry Smith   if (flg) {
5225e42470aSBarry Smith     ls->alpha = tmp;
5235e42470aSBarry Smith   }
524ee8b94d1SSatish Balay   ierr = OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp, &flg);CHKERRQ(ierr);
52525cdf11fSBarry Smith   if (flg) {
5265e42470aSBarry Smith     ls->maxstep = tmp;
5275e42470aSBarry Smith   }
528ee8b94d1SSatish Balay   ierr = OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp, &flg);CHKERRQ(ierr);
52925cdf11fSBarry Smith   if (flg) {
5305e42470aSBarry Smith     ls->steptol = tmp;
5315e42470aSBarry Smith   }
532ee8b94d1SSatish Balay   ierr = OptionsGetString(snes->prefix,"-snes_line_search",ver,16, &flg); CHKERRQ(ierr);
53325cdf11fSBarry Smith   if (flg) {
53448d91487SBarry Smith     if (!PetscStrcmp(ver,"basic")) {
53577c4ece6SBarry Smith       SNESSetLineSearch(snes,SNESNoLineSearch);
5365e42470aSBarry Smith     }
53748d91487SBarry Smith     else if (!PetscStrcmp(ver,"quadratic")) {
53877c4ece6SBarry Smith       SNESSetLineSearch(snes,SNESQuadraticLineSearch);
5395e42470aSBarry Smith     }
54048d91487SBarry Smith     else if (!PetscStrcmp(ver,"cubic")) {
54177c4ece6SBarry Smith       SNESSetLineSearch(snes,SNESCubicLineSearch);
5425e42470aSBarry Smith     }
543f63b844aSLois Curfman McInnes     else {SETERRQ(1,"SNESSetFromOptions_EQ_LS:Unknown line search");}
5445e42470aSBarry Smith   }
5455e42470aSBarry Smith   return 0;
5465e42470aSBarry Smith }
5475e42470aSBarry Smith /* ------------------------------------------------------------ */
548f63b844aSLois Curfman McInnes int SNESCreate_EQ_LS(SNES  snes )
5495e42470aSBarry Smith {
5505e42470aSBarry Smith   SNES_LS *neP;
5515e42470aSBarry Smith 
552ddd12767SBarry Smith   if (snes->method_class != SNES_NONLINEAR_EQUATIONS)
553f63b844aSLois Curfman McInnes     SETERRQ(1,"SNESCreate_EQ_LS:For SNES_NONLINEAR_EQUATIONS only");
554f63b844aSLois Curfman McInnes   snes->type		= SNES_EQ_LS;
555f63b844aSLois Curfman McInnes   snes->setup		= SNESSetUp_EQ_LS;
556f63b844aSLois Curfman McInnes   snes->solve		= SNESSolve_EQ_LS;
557f63b844aSLois Curfman McInnes   snes->destroy		= SNESDestroy_EQ_LS;
558f63b844aSLois Curfman McInnes   snes->converged	= SNESConverged_EQ_LS;
559f63b844aSLois Curfman McInnes   snes->printhelp       = SNESPrintHelp_EQ_LS;
560f63b844aSLois Curfman McInnes   snes->setfromoptions  = SNESSetFromOptions_EQ_LS;
561f63b844aSLois Curfman McInnes   snes->view            = SNESView_EQ_LS;
5625e42470aSBarry Smith 
5630452661fSBarry Smith   neP			= PetscNew(SNES_LS);   CHKPTRQ(neP);
564ff782a9fSLois Curfman McInnes   PLogObjectMemory(snes,sizeof(SNES_LS));
5655e42470aSBarry Smith   snes->data    	= (void *) neP;
5665e42470aSBarry Smith   neP->alpha		= 1.e-4;
5675e42470aSBarry Smith   neP->maxstep		= 1.e8;
5685e42470aSBarry Smith   neP->steptol		= 1.e-12;
5695e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
5705e42470aSBarry Smith   return 0;
5715e42470aSBarry Smith }
5725e42470aSBarry Smith 
5735e42470aSBarry Smith 
5745e42470aSBarry Smith 
5755e42470aSBarry Smith 
576