xref: /petsc/src/snes/impls/ls/ls.c (revision ddd1276785a76ea4a3b2575e2a65f0ba44dae580)
15e76c431SBarry Smith #ifndef lint
2*ddd12767SBarry Smith static char vcid[] = "$Id: ls.c,v 1.45 1995/10/01 21:53:32 bsmith Exp bsmith $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6fbe28522SBarry Smith #include "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 
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;
34336446d4SLois Curfman McInnes   SLES         sles;
35336446d4SLois Curfman McInnes   KSP          ksp;
365e76c431SBarry Smith 
375e42470aSBarry Smith   history	= snes->conv_hist;	/* convergence history */
385e42470aSBarry Smith   history_len	= snes->conv_hist_len;	/* convergence history length */
395e42470aSBarry Smith   maxits	= snes->max_its;	/* maximum number of iterations */
405e42470aSBarry Smith   X		= snes->vec_sol;	/* solution vector */
4139e2f89bSBarry Smith   F		= snes->vec_func;	/* residual vector */
425e42470aSBarry Smith   Y		= snes->work[0];	/* work vectors */
435e42470aSBarry Smith   G		= snes->work[1];
445e42470aSBarry Smith   W		= snes->work[2];
455e76c431SBarry Smith 
4678b31e54SBarry Smith   ierr = SNESComputeInitialGuess(snes,X); CHKERRQ(ierr); /* X <- X_0        */
47393d2d9aSLois Curfman McInnes   ierr = VecNorm(X,&xnorm); CHKERRQ(ierr);               /* xnorm = || X || */
4878b31e54SBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);   /* (+/-) F(X)      */
49393d2d9aSLois Curfman McInnes   ierr = VecNorm(F,&fnorm); CHKERRQ(ierr);	         /* fnorm <- ||F||  */
505e42470aSBarry Smith   snes->norm = fnorm;
515e76c431SBarry Smith   if (history && history_len > 0) history[0] = fnorm;
52*ddd12767SBarry Smith   if (snes->monitor) {ierr = (*snes->monitor)(snes,0,fnorm,snes->monP); CHKERRQ(ierr);}
535e76c431SBarry Smith 
54393d2d9aSLois Curfman McInnes   /* Set the KSP stopping criterion to use the Eisenstat-Walker method */
55336446d4SLois Curfman McInnes   if (snes->ksp_ewconv) {
56336446d4SLois Curfman McInnes     ierr = SNESGetSLES(snes,&sles); CHKERRQ(ierr);
57336446d4SLois Curfman McInnes     ierr = SLESGetKSP(sles,&ksp); CHKERRQ(ierr);
58*ddd12767SBarry Smith     ierr = KSPSetConvergenceTest(ksp,SNES_KSP_EW_Converged_Private,(void *)snes);
59*ddd12767SBarry Smith            CHKERRQ(ierr);
60336446d4SLois Curfman McInnes   }
61336446d4SLois Curfman McInnes 
625e76c431SBarry Smith   for ( i=0; i<maxits; i++ ) {
635e42470aSBarry Smith     snes->iter = i+1;
645e76c431SBarry Smith 
655e76c431SBarry Smith     /* Solve J Y = -F, where J is Jacobian matrix */
66*ddd12767SBarry Smith     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
67*ddd12767SBarry Smith            CHKERRQ(ierr);
68*ddd12767SBarry Smith     ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg);
69*ddd12767SBarry Smith            CHKERRQ(ierr);
7078b31e54SBarry Smith     ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr);
7181b6cf68SLois Curfman McInnes     ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr);
72*ddd12767SBarry Smith     ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail);CHKERRQ(ierr);
73761aaf1bSLois Curfman McInnes     if (lsfail) snes->nfailures++;
745e76c431SBarry Smith 
7539e2f89bSBarry Smith     TMP = F; F = G; snes->vec_func_always = F; G = TMP;
7639e2f89bSBarry Smith     TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
775e76c431SBarry Smith     fnorm = gnorm;
785e76c431SBarry Smith 
795e42470aSBarry Smith     snes->norm = fnorm;
805e76c431SBarry Smith     if (history && history_len > i+1) history[i+1] = fnorm;
81393d2d9aSLois Curfman McInnes     ierr = VecNorm(X,&xnorm); CHKERRQ(ierr);	/* xnorm = || X || */
82*ddd12767SBarry Smith     if (snes->monitor) {ierr = (*snes->monitor)(snes,i+1,fnorm,snes->monP);CHKERRQ(ierr);}
835e76c431SBarry Smith 
845e76c431SBarry Smith     /* Test for convergence */
85bbb6d6a8SBarry Smith     if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
8639e2f89bSBarry Smith       if (X != snes->vec_sol) {
87393d2d9aSLois Curfman McInnes         ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
8839e2f89bSBarry Smith         snes->vec_sol_always = snes->vec_sol;
8939e2f89bSBarry Smith         snes->vec_func_always = snes->vec_func;
9039e2f89bSBarry Smith       }
915e76c431SBarry Smith       break;
925e76c431SBarry Smith     }
935e76c431SBarry Smith   }
9452392280SLois Curfman McInnes   if (i == maxits) {
9552392280SLois Curfman McInnes     PLogInfo((PetscObject)snes,
96*ddd12767SBarry Smith       "SNESSolve_LS: Maximum number of iterations has been reached: %d\n",maxits);
9752392280SLois Curfman McInnes     i--;
9852392280SLois Curfman McInnes   }
995e42470aSBarry Smith   *outits = i+1;
1005e42470aSBarry Smith   return 0;
1015e76c431SBarry Smith }
1025e76c431SBarry Smith /* ------------------------------------------------------------ */
1035e42470aSBarry Smith int SNESSetUp_LS(SNES snes )
1045e76c431SBarry Smith {
1055e42470aSBarry Smith   int ierr;
10681b6cf68SLois Curfman McInnes   snes->nwork = 4;
10778b31e54SBarry Smith   ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work); CHKERRQ(ierr);
1085e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work);
10981b6cf68SLois Curfman McInnes   snes->vec_sol_update_always = snes->work[3];
1105e42470aSBarry Smith   return 0;
1115e76c431SBarry Smith }
1125e76c431SBarry Smith /* ------------------------------------------------------------ */
1135e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
1145e76c431SBarry Smith {
1155e42470aSBarry Smith   SNES snes = (SNES) obj;
116393d2d9aSLois Curfman McInnes   int  ierr;
117393d2d9aSLois Curfman McInnes   ierr = VecFreeVecs(snes->work,snes->nwork); CHKERRQ(ierr);
11878b31e54SBarry Smith   PETSCFREE(snes->data);
1195e42470aSBarry Smith   return 0;
1205e76c431SBarry Smith }
1215e76c431SBarry Smith /* ------------------------------------------------------------ */
1225e76c431SBarry Smith /*ARGSUSED*/
1234b828684SBarry Smith /*@C
1245e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1255e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1265e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1275e76c431SBarry Smith 
1285e76c431SBarry Smith    Input Parameters:
1295e42470aSBarry Smith .  snes - nonlinear context
1305e76c431SBarry Smith .  x - current iterate
1315e76c431SBarry Smith .  f - residual evaluated at x
1325e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1335e76c431SBarry Smith .  w - work vector
1345e76c431SBarry Smith .  fnorm - 2-norm of f
1355e76c431SBarry Smith 
136c4a48953SLois Curfman McInnes    Output Parameters:
1375e76c431SBarry Smith .  g - residual evaluated at new iterate y
1385e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1395e76c431SBarry Smith .  gnorm - 2-norm of g
1405e76c431SBarry Smith .  ynorm - 2-norm of search length
141761aaf1bSLois Curfman McInnes .  flag - set to 0, indicating a successful line search
1425e76c431SBarry Smith 
143c4a48953SLois Curfman McInnes    Options Database Key:
144c4a48953SLois Curfman McInnes $  -snes_line_search basic
145c4a48953SLois Curfman McInnes 
14628ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
14728ae5a21SLois Curfman McInnes 
148f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
149a935fc98SLois Curfman McInnes           SNESSetLineSearchRoutine()
1505e76c431SBarry Smith @*/
1515e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
152761aaf1bSLois Curfman McInnes                      double fnorm, double *ynorm, double *gnorm,int *flag )
1535e76c431SBarry Smith {
1545e42470aSBarry Smith   int    ierr;
1555e42470aSBarry Smith   Scalar one = 1.0;
156761aaf1bSLois Curfman McInnes   *flag = 0;
1577857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
158393d2d9aSLois Curfman McInnes   ierr = VecNorm(y,ynorm); CHKERRQ(ierr);              /* ynorm = || y || */
159393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&one,x,y); CHKERRQ(ierr);             /* y <- x + y      */
160393d2d9aSLois Curfman McInnes   ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); /* (+/-) F(y)      */
161393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm); CHKERRQ(ierr);              /* gnorm = || g || */
1627857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
163761aaf1bSLois Curfman McInnes   return 0;
1645e76c431SBarry Smith }
1655e76c431SBarry Smith /* ------------------------------------------------------------------ */
1664b828684SBarry Smith /*@C
167f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
168f59ffdedSLois Curfman McInnes    is the default line search method.
1695e76c431SBarry Smith 
1705e76c431SBarry Smith    Input Parameters:
1715e42470aSBarry Smith .  snes - nonlinear context
1725e76c431SBarry Smith .  x - current iterate
1735e76c431SBarry Smith .  f - residual evaluated at x
1745e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1755e76c431SBarry Smith .  w - work vector
1765e76c431SBarry Smith .  fnorm - 2-norm of f
1775e76c431SBarry Smith 
178393d2d9aSLois Curfman McInnes    Output Parameters:
1795e76c431SBarry Smith .  g - residual evaluated at new iterate y
1805e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1815e76c431SBarry Smith .  gnorm - 2-norm of g
1825e76c431SBarry Smith .  ynorm - 2-norm of search length
183761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
1845e76c431SBarry Smith 
185c4a48953SLois Curfman McInnes    Options Database Key:
186c4a48953SLois Curfman McInnes $  -snes_line_search cubic
187c4a48953SLois Curfman McInnes 
1885e76c431SBarry Smith    Notes:
1895e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
1905e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
1915e76c431SBarry Smith 
19228ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
19328ae5a21SLois Curfman McInnes 
194f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
1955e76c431SBarry Smith @*/
1965e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
197761aaf1bSLois Curfman McInnes                 double fnorm, double *ynorm, double *gnorm,int *flag)
1985e76c431SBarry Smith {
199*ddd12767SBarry Smith   double  steptol, initslope,lambdaprev, gnormprev,a, b, d, t1, t2;
200*ddd12767SBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2016b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2025e42470aSBarry Smith   Scalar  cinitslope,clambda;
2036b5873e3SBarry Smith #endif
2045e42470aSBarry Smith   int     ierr, count;
2055e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2065e42470aSBarry Smith   Scalar  one = 1.0,scale;
2075e76c431SBarry Smith 
2087857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
209761aaf1bSLois Curfman McInnes   *flag   = 0;
2105e76c431SBarry Smith   alpha   = neP->alpha;
2115e76c431SBarry Smith   maxstep = neP->maxstep;
2125e76c431SBarry Smith   steptol = neP->steptol;
2135e76c431SBarry Smith 
214393d2d9aSLois Curfman McInnes   ierr = VecNorm(y,ynorm); CHKERRQ(ierr);
2155e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2165e42470aSBarry Smith     scale = maxstep/(*ynorm);
2176b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
218*ddd12767SBarry Smith     PLogInfo((PetscObject)snes,"SNESCubicLineSearch: Scaling step by %g\n",real(scale));
2196b5873e3SBarry Smith #else
220*ddd12767SBarry Smith     PLogInfo((PetscObject)snes,"SNESCubicLineSearch: Scaling step by %g\n",scale);
2216b5873e3SBarry Smith #endif
222393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
2235e76c431SBarry Smith     *ynorm = maxstep;
2245e76c431SBarry Smith   }
2255e76c431SBarry Smith   minlambda = steptol/(*ynorm);
2265e42470aSBarry Smith #if defined(PETSC_COMPLEX)
227393d2d9aSLois Curfman McInnes   ierr = VecDot(f,y,&cinitslope); CHKERRQ(ierr);
2285e42470aSBarry Smith   initslope = real(cinitslope);
2295e42470aSBarry Smith #else
230393d2d9aSLois Curfman McInnes   VecDot(f,y,&initslope); CHKERRQ(ierr);
2315e42470aSBarry Smith #endif
2325e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2335e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2345e76c431SBarry Smith 
235393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
236393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&one,x,w); CHKERRQ(ierr);
23778b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
238393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm);
2395e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
240393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
2414b828684SBarry Smith     PLogInfo((PetscObject)snes,"SNESCubicLineSearch: Using full step\n");
242d93a2b8dSBarry Smith     goto theend;
2435e76c431SBarry Smith   }
2445e76c431SBarry Smith 
2455e76c431SBarry Smith   /* Fit points with quadratic */
2465e76c431SBarry Smith   lambda = 1.0; count = 0;
2475e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
2485e76c431SBarry Smith   lambdaprev = lambda;
2495e76c431SBarry Smith   gnormprev = *gnorm;
250*ddd12767SBarry Smith   if (lambdatemp <= .1*lambda) lambda = .1*lambda;
251*ddd12767SBarry Smith   else lambda = lambdatemp;
252393d2d9aSLois Curfman McInnes   ierr = VecCopy(x,w); CHKERRQ(ierr);
2535e42470aSBarry Smith #if defined(PETSC_COMPLEX)
254393d2d9aSLois Curfman McInnes   clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
2555e42470aSBarry Smith #else
256393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
2575e42470aSBarry Smith #endif
25878b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
259393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
2605e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
261393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
2624b828684SBarry Smith     PLogInfo((PetscObject)snes,
2634b828684SBarry Smith              "SNESCubicLineSearch: Quadratically determined step, lambda %g\n",lambda);
264d93a2b8dSBarry Smith     goto theend;
2655e76c431SBarry Smith   }
2665e76c431SBarry Smith 
2675e76c431SBarry Smith   /* Fit points with cubic */
2685e76c431SBarry Smith   count = 1;
2695e76c431SBarry Smith   while (1) {
2705e76c431SBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
2714b828684SBarry Smith       PLogInfo((PetscObject)snes,
2724b828684SBarry Smith          "SNESCubicLineSearch:Unable to find good step length! %d \n",count);
2734b828684SBarry Smith       PLogInfo((PetscObject)snes,
274*ddd12767SBarry Smith          "SNESCubicLineSearch:f %g fnew %g ynorm %g lambda %g\n",fnorm,*gnorm,*ynorm,lambda);
275393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
276761aaf1bSLois Curfman McInnes       *flag = -1; break;
2775e76c431SBarry Smith     }
2785e76c431SBarry Smith     t1 = *gnorm - fnorm - lambda*initslope;
2795e76c431SBarry Smith     t2 = gnormprev  - fnorm - lambdaprev*initslope;
280*ddd12767SBarry Smith     a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2815e76c431SBarry Smith     b = (-lambdaprev*t1/(lambda*lambda) +
2825e76c431SBarry Smith                              lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2835e76c431SBarry Smith     d = b*b - 3*a*initslope;
2845e76c431SBarry Smith     if (d < 0.0) d = 0.0;
2855e76c431SBarry Smith     if (a == 0.0) {
2865e76c431SBarry Smith       lambdatemp = -initslope/(2.0*b);
2875e76c431SBarry Smith     } else {
2885e76c431SBarry Smith       lambdatemp = (-b + sqrt(d))/(3.0*a);
2895e76c431SBarry Smith     }
2905e76c431SBarry Smith     if (lambdatemp > .5*lambda) {
2915e76c431SBarry Smith       lambdatemp = .5*lambda;
2925e76c431SBarry Smith     }
2935e76c431SBarry Smith     lambdaprev = lambda;
2945e76c431SBarry Smith     gnormprev = *gnorm;
2955e76c431SBarry Smith     if (lambdatemp <= .1*lambda) {
2965e76c431SBarry Smith       lambda = .1*lambda;
2975e76c431SBarry Smith     }
2985e76c431SBarry Smith     else lambda = lambdatemp;
299393d2d9aSLois Curfman McInnes     ierr = VecCopy( x, w ); CHKERRQ(ierr);
3005e42470aSBarry Smith #if defined(PETSC_COMPLEX)
301393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
3025e42470aSBarry Smith #else
303393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
3045e42470aSBarry Smith #endif
30578b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
306393d2d9aSLois Curfman McInnes     ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
3075e76c431SBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
308393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
3094b828684SBarry Smith       PLogInfo((PetscObject)snes,
3104b828684SBarry Smith                 "SNESCubicLineSearch: Cubically determined step, lambda %g\n",lambda);
311761aaf1bSLois Curfman McInnes       *flag = -1; break;
3125e76c431SBarry Smith     }
3135e76c431SBarry Smith     count++;
3145e76c431SBarry Smith   }
315d93a2b8dSBarry Smith   theend:
3167857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3175e42470aSBarry Smith   return 0;
3185e76c431SBarry Smith }
31952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
3204b828684SBarry Smith /*@C
3215e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3225e76c431SBarry Smith 
3235e42470aSBarry Smith    Input Parameters:
324f59ffdedSLois Curfman McInnes .  snes - the SNES context
3255e42470aSBarry Smith .  x - current iterate
3265e42470aSBarry Smith .  f - residual evaluated at x
3275e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3285e42470aSBarry Smith .  w - work vector
3295e42470aSBarry Smith .  fnorm - 2-norm of f
3305e42470aSBarry Smith 
331c4a48953SLois Curfman McInnes    Output Parameters:
3325e42470aSBarry Smith .  g - residual evaluated at new iterate y
3335e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3345e42470aSBarry Smith .  gnorm - 2-norm of g
3355e42470aSBarry Smith .  ynorm - 2-norm of search length
336761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
3375e42470aSBarry Smith 
338c4a48953SLois Curfman McInnes    Options Database Key:
339c4a48953SLois Curfman McInnes $  -snes_line_search quadratic
340c4a48953SLois Curfman McInnes 
3415e42470aSBarry Smith    Notes:
342f59ffdedSLois Curfman McInnes    Use SNESSetLineSearchRoutine()
343*ddd12767SBarry Smith    to set this routine within the SNES_EQ_NLS method.
3445e42470aSBarry Smith 
345f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
346f59ffdedSLois Curfman McInnes 
347f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
3485e42470aSBarry Smith @*/
3495e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
350761aaf1bSLois Curfman McInnes                            double fnorm, double *ynorm, double *gnorm,int *flag)
3515e76c431SBarry Smith {
352*ddd12767SBarry Smith   double  steptol,initslope,lambdaprev,gnormprev,maxstep,minlambda,alpha,lambda,lambdatemp;
3536b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
3545e42470aSBarry Smith   Scalar  cinitslope,clambda;
3556b5873e3SBarry Smith #endif
3565e42470aSBarry Smith   int     ierr,count;
3575e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
3585e42470aSBarry Smith   Scalar  one = 1.0,scale;
3595e76c431SBarry Smith 
3607857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
361761aaf1bSLois Curfman McInnes   *flag = 0;
3625e42470aSBarry Smith   alpha   = neP->alpha;
3635e42470aSBarry Smith   maxstep = neP->maxstep;
3645e42470aSBarry Smith   steptol = neP->steptol;
3655e76c431SBarry Smith 
3665e42470aSBarry Smith   VecNorm(y, ynorm );
3675e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
3685e42470aSBarry Smith     scale = maxstep/(*ynorm);
369393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
3705e42470aSBarry Smith     *ynorm = maxstep;
3715e76c431SBarry Smith   }
3725e42470aSBarry Smith   minlambda = steptol/(*ynorm);
3735e42470aSBarry Smith #if defined(PETSC_COMPLEX)
374393d2d9aSLois Curfman McInnes   ierr = VecDot(f,y,&cinitslope); CHKERRQ(ierr);
3755e42470aSBarry Smith   initslope = real(cinitslope);
3765e42470aSBarry Smith #else
377393d2d9aSLois Curfman McInnes   ierr = VecDot(f,y,&initslope); CHKERRQ(ierr);
3785e42470aSBarry Smith #endif
3795e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
3805e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
3815e42470aSBarry Smith 
382393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
383393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&one,x,w); CHKERRQ(ierr);
38478b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
385393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
3865e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
387393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
3884b828684SBarry Smith     PLogInfo((PetscObject)snes,"SNESQuadraticLineSearch: Using full step\n");
389d93a2b8dSBarry Smith     goto theend;
3905e42470aSBarry Smith   }
3915e42470aSBarry Smith 
3925e42470aSBarry Smith   /* Fit points with quadratic */
3935e42470aSBarry Smith   lambda = 1.0; count = 0;
3945e42470aSBarry Smith   count = 1;
3955e42470aSBarry Smith   while (1) {
3965e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
3974b828684SBarry Smith       PLogInfo((PetscObject)snes,
3984b828684SBarry Smith           "SNESQuadraticLineSearch:Unable to find good step length! %d \n",count);
3994b828684SBarry Smith       PLogInfo((PetscObject)snes,
400*ddd12767SBarry Smith       "SNESQuadraticLineSearch:f %g fnew %g ynorm %g lambda %g\n",fnorm,*gnorm,*ynorm,lambda);
401393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
402761aaf1bSLois Curfman McInnes       *flag = -1; break;
4035e42470aSBarry Smith     }
4045e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4055e42470aSBarry Smith     lambdaprev = lambda;
4065e42470aSBarry Smith     gnormprev = *gnorm;
4075e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4085e42470aSBarry Smith       lambda = .1*lambda;
4095e42470aSBarry Smith     } else lambda = lambdatemp;
410393d2d9aSLois Curfman McInnes     ierr = VecCopy(x,w); CHKERRQ(ierr);
4115e42470aSBarry Smith #if defined(PETSC_COMPLEX)
412393d2d9aSLois Curfman McInnes     clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
4135e42470aSBarry Smith #else
414393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
4155e42470aSBarry Smith #endif
41678b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
417393d2d9aSLois Curfman McInnes     ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
4185e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
419393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
4204b828684SBarry Smith       PLogInfo((PetscObject)snes,
421*ddd12767SBarry Smith         "SNESQuadraticLineSearch:Quadratically determined step, lambda %g\n",lambda);
42206259719SBarry Smith       break;
4235e42470aSBarry Smith     }
4245e42470aSBarry Smith     count++;
4255e42470aSBarry Smith   }
426d93a2b8dSBarry Smith   theend:
4277857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4285e42470aSBarry Smith   return 0;
4295e76c431SBarry Smith }
4305e76c431SBarry Smith /* ------------------------------------------------------------ */
431b1f0a012SBarry Smith /*@
4325e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
433fbe28522SBarry Smith    by the method SNES_LS.
4345e76c431SBarry Smith 
4355e76c431SBarry Smith    Input Parameters:
436eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
4375e76c431SBarry Smith .  func - pointer to int function
4385e76c431SBarry Smith 
439c4a48953SLois Curfman McInnes    Available Routines:
440f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
441f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
442f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
4435e76c431SBarry Smith 
444c4a48953SLois Curfman McInnes     Options Database Keys:
445c4a48953SLois Curfman McInnes $   -snes_line_search [basic,quadratic,cubic]
446c4a48953SLois Curfman McInnes 
4475e76c431SBarry Smith    Calling sequence of func:
448f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
449761aaf1bSLois Curfman McInnes          Vec w, double fnorm, double *ynorm,
450761aaf1bSLois Curfman McInnes          double *gnorm, *flag)
4515e76c431SBarry Smith 
4525e76c431SBarry Smith     Input parameters for func:
4535e42470aSBarry Smith .   snes - nonlinear context
4545e76c431SBarry Smith .   x - current iterate
4555e76c431SBarry Smith .   f - residual evaluated at x
4565e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4575e76c431SBarry Smith .   w - work vector
4585e76c431SBarry Smith .   fnorm - 2-norm of f
4595e76c431SBarry Smith 
4605e76c431SBarry Smith     Output parameters for func:
4615e76c431SBarry Smith .   g - residual evaluated at new iterate y
4625e76c431SBarry Smith .   y - new iterate (contains search direction on input)
4635e76c431SBarry Smith .   gnorm - 2-norm of g
4645e76c431SBarry Smith .   ynorm - 2-norm of search length
465761aaf1bSLois Curfman McInnes .   flag - set to 0 if the line search succeeds; a nonzero integer
466761aaf1bSLois Curfman McInnes            on failure.
467f59ffdedSLois Curfman McInnes 
468f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
469f59ffdedSLois Curfman McInnes 
470f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
4715e76c431SBarry Smith @*/
4725e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
473761aaf1bSLois Curfman McInnes                              double,double *,double*,int*) )
4745e76c431SBarry Smith {
475*ddd12767SBarry Smith   if ((snes)->type == SNES_EQ_NLS)
4765e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
4775e42470aSBarry Smith   return 0;
4785e76c431SBarry Smith }
47952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
4805e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
4815e42470aSBarry Smith {
4825e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
48352392280SLois Curfman McInnes   char    *p;
48452392280SLois Curfman McInnes   if (snes->prefix) p = snes->prefix; else p = "-";
48552392280SLois Curfman McInnes   MPIU_printf(snes->comm," method ls (system of nonlinear equations):\n");
48652392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search [basic,quadratic,cubic]\n",p);
48752392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_alpha alpha (default %g)\n",p,ls->alpha);
48852392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_maxstep max (default %g)\n",p,ls->maxstep);
48952392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_steptol tol (default %g)\n",p,ls->steptol);
4906b5873e3SBarry Smith   return 0;
4915e42470aSBarry Smith }
49252392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
493a935fc98SLois Curfman McInnes static int SNESView_LS(PetscObject obj,Viewer viewer)
494a935fc98SLois Curfman McInnes {
495a935fc98SLois Curfman McInnes   SNES    snes = (SNES)obj;
496a935fc98SLois Curfman McInnes   SNES_LS *ls = (SNES_LS *)snes->data;
497d636dbe3SBarry Smith   FILE    *fd;
498a935fc98SLois Curfman McInnes   char    *cstring;
49951695f54SLois Curfman McInnes   int     ierr;
500a935fc98SLois Curfman McInnes 
501a447f0b5SLois Curfman McInnes   ierr = ViewerFileGetPointer_Private(viewer,&fd); CHKERRQ(ierr);
502*ddd12767SBarry Smith   if (ls->LineSearch == SNESNoLineSearch) cstring = "SNESNoLineSearch";
503*ddd12767SBarry Smith   else if (ls->LineSearch == SNESQuadraticLineSearch) cstring = "SNESQuadraticLineSearch";
504*ddd12767SBarry Smith   else if (ls->LineSearch == SNESCubicLineSearch) cstring = "SNESCubicLineSearch";
505*ddd12767SBarry Smith   else cstring = "unknown";
506a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    line search variant: %s\n",cstring);
507a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    alpha=%g, maxstep=%g, steptol=%g\n",
508a935fc98SLois Curfman McInnes                ls->alpha,ls->maxstep,ls->steptol);
509a935fc98SLois Curfman McInnes   return 0;
510a935fc98SLois Curfman McInnes }
51152392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
5125e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5135e42470aSBarry Smith {
5145e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5155e42470aSBarry Smith   char    ver[16];
5165e42470aSBarry Smith   double  tmp;
5175e42470aSBarry Smith 
518df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) {
5195e42470aSBarry Smith     ls->alpha = tmp;
5205e42470aSBarry Smith   }
521df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) {
5225e42470aSBarry Smith     ls->maxstep = tmp;
5235e42470aSBarry Smith   }
524df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) {
5255e42470aSBarry Smith     ls->steptol = tmp;
5265e42470aSBarry Smith   }
527df60cc22SBarry Smith   if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) {
52848d91487SBarry Smith     if (!PetscStrcmp(ver,"basic")) {
5295e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
5305e42470aSBarry Smith     }
53148d91487SBarry Smith     else if (!PetscStrcmp(ver,"quadratic")) {
5325e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
5335e42470aSBarry Smith     }
53448d91487SBarry Smith     else if (!PetscStrcmp(ver,"cubic")) {
5355e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
5365e42470aSBarry Smith     }
5378c48e012SBarry Smith     else {SETERRQ(1,"SNESSetFromOptions_LS:Unknown line search");}
5385e42470aSBarry Smith   }
5395e42470aSBarry Smith   return 0;
5405e42470aSBarry Smith }
5415e42470aSBarry Smith /* ------------------------------------------------------------ */
5425e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
5435e42470aSBarry Smith {
5445e42470aSBarry Smith   SNES_LS *neP;
5455e42470aSBarry Smith 
546*ddd12767SBarry Smith   if (snes->method_class != SNES_NONLINEAR_EQUATIONS)
547*ddd12767SBarry Smith     SETERRQ(1,"SNESCreate_LS:For SNES_NONLINEAR_EQUATIONS only");
54825c7317fSLois Curfman McInnes   snes->type		= SNES_EQ_NLS;
54949e3953dSBarry Smith   snes->setup		= SNESSetUp_LS;
55049e3953dSBarry Smith   snes->solve		= SNESSolve_LS;
5515e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
552bbb6d6a8SBarry Smith   snes->converged	= SNESDefaultConverged;
55349e3953dSBarry Smith   snes->printhelp       = SNESPrintHelp_LS;
55449e3953dSBarry Smith   snes->setfromoptions  = SNESSetFromOptions_LS;
555a935fc98SLois Curfman McInnes   snes->view            = SNESView_LS;
5565e42470aSBarry Smith 
55778b31e54SBarry Smith   neP			= PETSCNEW(SNES_LS);   CHKPTRQ(neP);
558ff782a9fSLois Curfman McInnes   PLogObjectMemory(snes,sizeof(SNES_LS));
5595e42470aSBarry Smith   snes->data    	= (void *) neP;
5605e42470aSBarry Smith   neP->alpha		= 1.e-4;
5615e42470aSBarry Smith   neP->maxstep		= 1.e8;
5625e42470aSBarry Smith   neP->steptol		= 1.e-12;
5635e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
5645e42470aSBarry Smith   return 0;
5655e42470aSBarry Smith }
5665e42470aSBarry Smith 
5675e42470aSBarry Smith 
5685e42470aSBarry Smith 
5695e42470aSBarry Smith 
570