xref: /petsc/src/snes/impls/ls/ls.c (revision b16a3bb15a65da75afb90758415d4d6dfcaf0620)
15e76c431SBarry Smith #ifndef lint
2*b16a3bb1SBarry Smith static char vcid[] = "$Id: ls.c,v 1.38 1995/08/17 01:22:34 curfman Exp bsmith $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6fbe28522SBarry Smith #include "ls.h"
7*b16a3bb1SBarry Smith #include "pinclude/pviewer.h"
8bbb6d6a8SBarry Smith #if defined(HAVE_STRING_H)
9bbb6d6a8SBarry Smith #include <string.h>
10bbb6d6a8SBarry Smith #endif
115e42470aSBarry Smith 
125e42470aSBarry Smith /*
135e42470aSBarry Smith      Implements a line search variant of Newton's Method
145e76c431SBarry Smith     for solving systems of nonlinear equations.
155e76c431SBarry Smith 
165e76c431SBarry Smith     Input parameters:
175e42470aSBarry Smith .   snes - nonlinear context obtained from SNESCreate()
185e76c431SBarry Smith 
195e42470aSBarry Smith     Output Parameters:
20a935fc98SLois Curfman McInnes .   outits  - Number of global iterations until termination.
215e76c431SBarry Smith 
225e76c431SBarry Smith     Notes:
235e76c431SBarry Smith     This implements essentially a truncated Newton method with a
245e76c431SBarry Smith     line search.  By default a cubic backtracking line search
255e76c431SBarry Smith     is employed, as described in the text "Numerical Methods for
265e76c431SBarry Smith     Unconstrained Optimization and Nonlinear Equations" by Dennis
27393d2d9aSLois Curfman McInnes     and Schnabel.
285e76c431SBarry Smith */
295e76c431SBarry Smith 
305e42470aSBarry Smith int SNESSolve_LS(SNES snes,int *outits)
315e76c431SBarry Smith {
325e42470aSBarry Smith   SNES_LS      *neP = (SNES_LS *) snes->data;
33761aaf1bSLois Curfman McInnes   int          maxits, i, history_len, ierr, lits, lsfail;
34df60cc22SBarry Smith   MatStructure flg = ALLMAT_DIFFERENT_NONZERO_PATTERN;
356b5873e3SBarry Smith   double       fnorm, gnorm, xnorm, ynorm, *history;
365e42470aSBarry Smith   Vec          Y, X, F, G, W, TMP;
37336446d4SLois Curfman McInnes   SLES         sles;
38336446d4SLois Curfman McInnes   KSP          ksp;
395e76c431SBarry Smith 
405e42470aSBarry Smith   history	= snes->conv_hist;	/* convergence history */
415e42470aSBarry Smith   history_len	= snes->conv_hist_len;	/* convergence history length */
425e42470aSBarry Smith   maxits	= snes->max_its;	/* maximum number of iterations */
435e42470aSBarry Smith   X		= snes->vec_sol;	/* solution vector */
4439e2f89bSBarry Smith   F		= snes->vec_func;	/* residual vector */
455e42470aSBarry Smith   Y		= snes->work[0];	/* work vectors */
465e42470aSBarry Smith   G		= snes->work[1];
475e42470aSBarry Smith   W		= snes->work[2];
485e76c431SBarry Smith 
4978b31e54SBarry Smith   ierr = SNESComputeInitialGuess(snes,X); CHKERRQ(ierr); /* X <- X_0        */
50393d2d9aSLois Curfman McInnes   ierr = VecNorm(X,&xnorm); CHKERRQ(ierr);               /* xnorm = || X || */
5178b31e54SBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);   /* (+/-) F(X)      */
52393d2d9aSLois Curfman McInnes   ierr = VecNorm(F,&fnorm); CHKERRQ(ierr);	         /* fnorm <- ||F||  */
535e42470aSBarry Smith   snes->norm = fnorm;
545e76c431SBarry Smith   if (history && history_len > 0) history[0] = fnorm;
55bbb6d6a8SBarry Smith   if (snes->monitor)
56bbb6d6a8SBarry Smith     {ierr = (*snes->monitor)(snes,0,fnorm,snes->monP); CHKERRQ(ierr);}
575e76c431SBarry Smith 
58393d2d9aSLois Curfman McInnes   /* Set the KSP stopping criterion to use the Eisenstat-Walker method */
59336446d4SLois Curfman McInnes   if (snes->ksp_ewconv) {
60336446d4SLois Curfman McInnes     ierr = SNESGetSLES(snes,&sles); CHKERRQ(ierr);
61336446d4SLois Curfman McInnes     ierr = SLESGetKSP(sles,&ksp); CHKERRQ(ierr);
62336446d4SLois Curfman McInnes     ierr = KSPSetConvergenceTest(ksp,SNES_KSP_EW_Converged_Private,
63336446d4SLois Curfman McInnes            (void *)snes);  CHKERRQ(ierr);
64336446d4SLois Curfman McInnes   }
65336446d4SLois Curfman McInnes 
665e76c431SBarry Smith   for ( i=0; i<maxits; i++ ) {
675e42470aSBarry Smith     snes->iter = i+1;
685e76c431SBarry Smith 
695e76c431SBarry Smith     /* Solve J Y = -F, where J is Jacobian matrix */
70df60cc22SBarry Smith     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,
71bbb6d6a8SBarry Smith                                &flg); CHKERRQ(ierr);
72a935fc98SLois Curfman McInnes     ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,
73a935fc98SLois Curfman McInnes                                flg); CHKERRQ(ierr);
7478b31e54SBarry Smith     ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr);
7581b6cf68SLois Curfman McInnes     ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr);
76761aaf1bSLois Curfman McInnes     ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail);
77761aaf1bSLois Curfman McInnes     if (lsfail) snes->nfailures++;
7878b31e54SBarry Smith     CHKERRQ(ierr);
795e76c431SBarry Smith 
8039e2f89bSBarry Smith     TMP = F; F = G; snes->vec_func_always = F; G = TMP;
8139e2f89bSBarry Smith     TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
825e76c431SBarry Smith     fnorm = gnorm;
835e76c431SBarry Smith 
845e42470aSBarry Smith     snes->norm = fnorm;
855e76c431SBarry Smith     if (history && history_len > i+1) history[i+1] = fnorm;
86393d2d9aSLois Curfman McInnes     ierr = VecNorm(X,&xnorm); CHKERRQ(ierr);	/* xnorm = || X || */
87bbb6d6a8SBarry Smith     if (snes->monitor)
88393d2d9aSLois Curfman McInnes       {ierr = (*snes->monitor)(snes,i+1,fnorm,snes->monP); CHKERRQ(ierr);}
895e76c431SBarry Smith 
905e76c431SBarry Smith     /* Test for convergence */
91bbb6d6a8SBarry Smith     if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
9239e2f89bSBarry Smith       if (X != snes->vec_sol) {
93393d2d9aSLois Curfman McInnes         ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
9439e2f89bSBarry Smith         snes->vec_sol_always = snes->vec_sol;
9539e2f89bSBarry Smith         snes->vec_func_always = snes->vec_func;
9639e2f89bSBarry Smith       }
975e76c431SBarry Smith       break;
985e76c431SBarry Smith     }
995e76c431SBarry Smith   }
10052392280SLois Curfman McInnes   if (i == maxits) {
10152392280SLois Curfman McInnes     PLogInfo((PetscObject)snes,
10252392280SLois Curfman McInnes       "Maximum number of iterations has been reached: %d\n",maxits);
10352392280SLois Curfman McInnes     i--;
10452392280SLois Curfman McInnes   }
1055e42470aSBarry Smith   *outits = i+1;
1065e42470aSBarry Smith   return 0;
1075e76c431SBarry Smith }
1085e76c431SBarry Smith /* ------------------------------------------------------------ */
1095e42470aSBarry Smith int SNESSetUp_LS(SNES snes )
1105e76c431SBarry Smith {
1115e42470aSBarry Smith   int ierr;
11281b6cf68SLois Curfman McInnes   snes->nwork = 4;
11378b31e54SBarry Smith   ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work); CHKERRQ(ierr);
1145e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work);
11581b6cf68SLois Curfman McInnes   snes->vec_sol_update_always = snes->work[3];
1165e42470aSBarry Smith   return 0;
1175e76c431SBarry Smith }
1185e76c431SBarry Smith /* ------------------------------------------------------------ */
1195e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
1205e76c431SBarry Smith {
1215e42470aSBarry Smith   SNES snes = (SNES) obj;
122393d2d9aSLois Curfman McInnes   int  ierr;
123393d2d9aSLois Curfman McInnes   ierr = VecFreeVecs(snes->work,snes->nwork); CHKERRQ(ierr);
12478b31e54SBarry Smith   PETSCFREE(snes->data);
1255e42470aSBarry Smith   return 0;
1265e76c431SBarry Smith }
1275e76c431SBarry Smith /* ------------------------------------------------------------ */
1285e76c431SBarry Smith /*ARGSUSED*/
1295e76c431SBarry Smith /*@
1305e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1315e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1325e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1335e76c431SBarry Smith 
1345e76c431SBarry Smith    Input Parameters:
1355e42470aSBarry Smith .  snes - nonlinear context
1365e76c431SBarry Smith .  x - current iterate
1375e76c431SBarry Smith .  f - residual evaluated at x
1385e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1395e76c431SBarry Smith .  w - work vector
1405e76c431SBarry Smith .  fnorm - 2-norm of f
1415e76c431SBarry Smith 
142c4a48953SLois Curfman McInnes    Output Parameters:
1435e76c431SBarry Smith .  g - residual evaluated at new iterate y
1445e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1455e76c431SBarry Smith .  gnorm - 2-norm of g
1465e76c431SBarry Smith .  ynorm - 2-norm of search length
147761aaf1bSLois Curfman McInnes .  flag - set to 0, indicating a successful line search
1485e76c431SBarry Smith 
149c4a48953SLois Curfman McInnes    Options Database Key:
150c4a48953SLois Curfman McInnes $  -snes_line_search basic
151c4a48953SLois Curfman McInnes 
15228ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
15328ae5a21SLois Curfman McInnes 
154f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
155a935fc98SLois Curfman McInnes           SNESSetLineSearchRoutine()
1565e76c431SBarry Smith @*/
1575e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
158761aaf1bSLois Curfman McInnes               double fnorm, double *ynorm, double *gnorm,int *flag )
1595e76c431SBarry Smith {
1605e42470aSBarry Smith   int    ierr;
1615e42470aSBarry Smith   Scalar one = 1.0;
162761aaf1bSLois Curfman McInnes   *flag = 0;
1637857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
164393d2d9aSLois Curfman McInnes   ierr = VecNorm(y,ynorm); CHKERRQ(ierr);              /* ynorm = || y || */
165393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&one,x,y); CHKERRQ(ierr);             /* y <- x + y      */
166393d2d9aSLois Curfman McInnes   ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); /* (+/-) F(y)      */
167393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm); CHKERRQ(ierr);              /* gnorm = || g || */
1687857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
169761aaf1bSLois Curfman McInnes   return 0;
1705e76c431SBarry Smith }
1715e76c431SBarry Smith /* ------------------------------------------------------------------ */
1725e76c431SBarry Smith /*@
173f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
174f59ffdedSLois Curfman McInnes    is the default line search method.
1755e76c431SBarry Smith 
1765e76c431SBarry Smith    Input Parameters:
1775e42470aSBarry Smith .  snes - nonlinear context
1785e76c431SBarry Smith .  x - current iterate
1795e76c431SBarry Smith .  f - residual evaluated at x
1805e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1815e76c431SBarry Smith .  w - work vector
1825e76c431SBarry Smith .  fnorm - 2-norm of f
1835e76c431SBarry Smith 
184393d2d9aSLois Curfman McInnes    Output Parameters:
1855e76c431SBarry Smith .  g - residual evaluated at new iterate y
1865e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1875e76c431SBarry Smith .  gnorm - 2-norm of g
1885e76c431SBarry Smith .  ynorm - 2-norm of search length
189761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
1905e76c431SBarry Smith 
191c4a48953SLois Curfman McInnes    Options Database Key:
192c4a48953SLois Curfman McInnes $  -snes_line_search cubic
193c4a48953SLois Curfman McInnes 
1945e76c431SBarry Smith    Notes:
1955e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
1965e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
1975e76c431SBarry Smith 
19828ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
19928ae5a21SLois Curfman McInnes 
200f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
2015e76c431SBarry Smith @*/
2025e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
203761aaf1bSLois Curfman McInnes                 double fnorm, double *ynorm, double *gnorm,int *flag)
2045e76c431SBarry Smith {
2055e42470aSBarry Smith   double  steptol, initslope;
2065e42470aSBarry Smith   double  lambdaprev, gnormprev;
2075e76c431SBarry Smith   double  a, b, d, t1, t2;
2086b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2095e42470aSBarry Smith   Scalar  cinitslope,clambda;
2106b5873e3SBarry Smith #endif
2115e42470aSBarry Smith   int     ierr, count;
2125e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2135e42470aSBarry Smith   Scalar  one = 1.0,scale;
2145e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2155e76c431SBarry Smith 
2167857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
217761aaf1bSLois Curfman McInnes   *flag = 0;
2185e76c431SBarry Smith   alpha   = neP->alpha;
2195e76c431SBarry Smith   maxstep = neP->maxstep;
2205e76c431SBarry Smith   steptol = neP->steptol;
2215e76c431SBarry Smith 
222393d2d9aSLois Curfman McInnes   ierr = VecNorm(y,ynorm); CHKERRQ(ierr);
2235e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2245e42470aSBarry Smith     scale = maxstep/(*ynorm);
2256b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2266b5873e3SBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale));
2276b5873e3SBarry Smith #else
2285e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
2296b5873e3SBarry Smith #endif
230393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
2315e76c431SBarry Smith     *ynorm = maxstep;
2325e76c431SBarry Smith   }
2335e76c431SBarry Smith   minlambda = steptol/(*ynorm);
2345e42470aSBarry Smith #if defined(PETSC_COMPLEX)
235393d2d9aSLois Curfman McInnes   ierr = VecDot(f,y,&cinitslope); CHKERRQ(ierr);
2365e42470aSBarry Smith   initslope = real(cinitslope);
2375e42470aSBarry Smith #else
238393d2d9aSLois Curfman McInnes   VecDot(f,y,&initslope); CHKERRQ(ierr);
2395e42470aSBarry Smith #endif
2405e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2415e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2425e76c431SBarry Smith 
243393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
244393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&one,x,w); CHKERRQ(ierr);
24578b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
246393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm);
2475e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
248393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
2495e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Using full step\n");
250d93a2b8dSBarry Smith     goto theend;
2515e76c431SBarry Smith   }
2525e76c431SBarry Smith 
2535e76c431SBarry Smith   /* Fit points with quadratic */
2545e76c431SBarry Smith   lambda = 1.0; count = 0;
2555e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
2565e76c431SBarry Smith   lambdaprev = lambda;
2575e76c431SBarry Smith   gnormprev = *gnorm;
2585e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
2595e76c431SBarry Smith     lambda = .1*lambda;
2605e76c431SBarry Smith   } else lambda = lambdatemp;
261393d2d9aSLois Curfman McInnes   ierr = VecCopy(x,w); CHKERRQ(ierr);
2625e42470aSBarry Smith #if defined(PETSC_COMPLEX)
263393d2d9aSLois Curfman McInnes   clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
2645e42470aSBarry Smith #else
265393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
2665e42470aSBarry Smith #endif
26778b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
268393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
2695e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
270393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
2715e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
272d93a2b8dSBarry Smith     goto theend;
2735e76c431SBarry Smith   }
2745e76c431SBarry Smith 
2755e76c431SBarry Smith   /* Fit points with cubic */
2765e76c431SBarry Smith   count = 1;
2775e76c431SBarry Smith   while (1) {
2785e76c431SBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
2795e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
2805e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
2815e76c431SBarry Smith               fnorm,*gnorm, *ynorm,lambda);
282393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
283761aaf1bSLois Curfman McInnes       *flag = -1; break;
2845e76c431SBarry Smith     }
2855e76c431SBarry Smith     t1 = *gnorm - fnorm - lambda*initslope;
2865e76c431SBarry Smith     t2 = gnormprev  - fnorm - lambdaprev*initslope;
2875e76c431SBarry Smith     a = (t1/(lambda*lambda) -
2885e76c431SBarry Smith               t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2895e76c431SBarry Smith     b = (-lambdaprev*t1/(lambda*lambda) +
2905e76c431SBarry Smith               lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2915e76c431SBarry Smith     d = b*b - 3*a*initslope;
2925e76c431SBarry Smith     if (d < 0.0) d = 0.0;
2935e76c431SBarry Smith     if (a == 0.0) {
2945e76c431SBarry Smith       lambdatemp = -initslope/(2.0*b);
2955e76c431SBarry Smith     } else {
2965e76c431SBarry Smith       lambdatemp = (-b + sqrt(d))/(3.0*a);
2975e76c431SBarry Smith     }
2985e76c431SBarry Smith     if (lambdatemp > .5*lambda) {
2995e76c431SBarry Smith       lambdatemp = .5*lambda;
3005e76c431SBarry Smith     }
3015e76c431SBarry Smith     lambdaprev = lambda;
3025e76c431SBarry Smith     gnormprev = *gnorm;
3035e76c431SBarry Smith     if (lambdatemp <= .1*lambda) {
3045e76c431SBarry Smith       lambda = .1*lambda;
3055e76c431SBarry Smith     }
3065e76c431SBarry Smith     else lambda = lambdatemp;
307393d2d9aSLois Curfman McInnes     ierr = VecCopy( x, w ); CHKERRQ(ierr);
3085e42470aSBarry Smith #if defined(PETSC_COMPLEX)
309393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
3105e42470aSBarry Smith #else
311393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
3125e42470aSBarry Smith #endif
31378b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
314393d2d9aSLois Curfman McInnes     ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
3155e76c431SBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
316393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
3175e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
318761aaf1bSLois Curfman McInnes       *flag = -1; break;
3195e76c431SBarry Smith     }
3205e76c431SBarry Smith     count++;
3215e76c431SBarry Smith   }
322d93a2b8dSBarry Smith   theend:
3237857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3245e42470aSBarry Smith   return 0;
3255e76c431SBarry Smith }
32652392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
3275e42470aSBarry Smith /*@
3285e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3295e76c431SBarry Smith 
3305e42470aSBarry Smith    Input Parameters:
331f59ffdedSLois Curfman McInnes .  snes - the SNES context
3325e42470aSBarry Smith .  x - current iterate
3335e42470aSBarry Smith .  f - residual evaluated at x
3345e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3355e42470aSBarry Smith .  w - work vector
3365e42470aSBarry Smith .  fnorm - 2-norm of f
3375e42470aSBarry Smith 
338c4a48953SLois Curfman McInnes    Output Parameters:
3395e42470aSBarry Smith .  g - residual evaluated at new iterate y
3405e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3415e42470aSBarry Smith .  gnorm - 2-norm of g
3425e42470aSBarry Smith .  ynorm - 2-norm of search length
343761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
3445e42470aSBarry Smith 
345c4a48953SLois Curfman McInnes    Options Database Key:
346c4a48953SLois Curfman McInnes $  -snes_line_search quadratic
347c4a48953SLois Curfman McInnes 
3485e42470aSBarry Smith    Notes:
349f59ffdedSLois Curfman McInnes    Use SNESSetLineSearchRoutine()
350f59ffdedSLois Curfman McInnes    to set this routine within the SNES_NLS method.
3515e42470aSBarry Smith 
352f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
353f59ffdedSLois Curfman McInnes 
354f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
3555e42470aSBarry Smith @*/
3565e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
357761aaf1bSLois Curfman McInnes                     double fnorm, double *ynorm, double *gnorm,int *flag)
3585e76c431SBarry Smith {
3595e42470aSBarry Smith   double  steptol, initslope;
3605e42470aSBarry Smith   double  lambdaprev, gnormprev;
3616b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
3625e42470aSBarry Smith   Scalar  cinitslope,clambda;
3636b5873e3SBarry Smith #endif
3645e42470aSBarry Smith   int     ierr,count;
3655e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
3665e42470aSBarry Smith   Scalar  one = 1.0,scale;
3675e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
3685e76c431SBarry Smith 
3697857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
370761aaf1bSLois Curfman McInnes   *flag = 0;
3715e42470aSBarry Smith   alpha   = neP->alpha;
3725e42470aSBarry Smith   maxstep = neP->maxstep;
3735e42470aSBarry Smith   steptol = neP->steptol;
3745e76c431SBarry Smith 
3755e42470aSBarry Smith   VecNorm(y, ynorm );
3765e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
3775e42470aSBarry Smith     scale = maxstep/(*ynorm);
378393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
3795e42470aSBarry Smith     *ynorm = maxstep;
3805e76c431SBarry Smith   }
3815e42470aSBarry Smith   minlambda = steptol/(*ynorm);
3825e42470aSBarry Smith #if defined(PETSC_COMPLEX)
383393d2d9aSLois Curfman McInnes   ierr = VecDot(f,y,&cinitslope); CHKERRQ(ierr);
3845e42470aSBarry Smith   initslope = real(cinitslope);
3855e42470aSBarry Smith #else
386393d2d9aSLois Curfman McInnes   ierr = VecDot(f,y,&initslope); CHKERRQ(ierr);
3875e42470aSBarry Smith #endif
3885e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
3895e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
3905e42470aSBarry Smith 
391393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
392393d2d9aSLois Curfman McInnes   ierr = VecAXPY(&one,x,w); CHKERRQ(ierr);
39378b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
394393d2d9aSLois Curfman McInnes   ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
3955e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
396393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
3975e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Using full step\n");
398d93a2b8dSBarry Smith     goto theend;
3995e42470aSBarry Smith   }
4005e42470aSBarry Smith 
4015e42470aSBarry Smith   /* Fit points with quadratic */
4025e42470aSBarry Smith   lambda = 1.0; count = 0;
4035e42470aSBarry Smith   count = 1;
4045e42470aSBarry Smith   while (1) {
4055e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
4065e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
4075e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
4085e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
409393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
410761aaf1bSLois Curfman McInnes       *flag = -1; break;
4115e42470aSBarry Smith     }
4125e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4135e42470aSBarry Smith     lambdaprev = lambda;
4145e42470aSBarry Smith     gnormprev = *gnorm;
4155e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4165e42470aSBarry Smith       lambda = .1*lambda;
4175e42470aSBarry Smith     } else lambda = lambdatemp;
418393d2d9aSLois Curfman McInnes     ierr = VecCopy(x,w); CHKERRQ(ierr);
4195e42470aSBarry Smith #if defined(PETSC_COMPLEX)
420393d2d9aSLois Curfman McInnes     clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
4215e42470aSBarry Smith #else
422393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
4235e42470aSBarry Smith #endif
42478b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
425393d2d9aSLois Curfman McInnes     ierr = VecNorm(g,gnorm); CHKERRQ(ierr);
4265e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
427393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
4285e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
42906259719SBarry Smith       break;
4305e42470aSBarry Smith     }
4315e42470aSBarry Smith     count++;
4325e42470aSBarry Smith   }
433d93a2b8dSBarry Smith   theend:
4347857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4355e42470aSBarry Smith   return 0;
4365e76c431SBarry Smith }
4375e76c431SBarry Smith /* ------------------------------------------------------------ */
438b1f0a012SBarry Smith /*@
4395e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
440fbe28522SBarry Smith    by the method SNES_LS.
4415e76c431SBarry Smith 
4425e76c431SBarry Smith    Input Parameters:
443eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
4445e76c431SBarry Smith .  func - pointer to int function
4455e76c431SBarry Smith 
446c4a48953SLois Curfman McInnes    Available Routines:
447f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
448f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
449f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
4505e76c431SBarry Smith 
451c4a48953SLois Curfman McInnes     Options Database Keys:
452c4a48953SLois Curfman McInnes $   -snes_line_search [basic,quadratic,cubic]
453c4a48953SLois Curfman McInnes 
4545e76c431SBarry Smith    Calling sequence of func:
455f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
456761aaf1bSLois Curfman McInnes          Vec w, double fnorm, double *ynorm,
457761aaf1bSLois Curfman McInnes          double *gnorm, *flag)
4585e76c431SBarry Smith 
4595e76c431SBarry Smith     Input parameters for func:
4605e42470aSBarry Smith .   snes - nonlinear context
4615e76c431SBarry Smith .   x - current iterate
4625e76c431SBarry Smith .   f - residual evaluated at x
4635e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4645e76c431SBarry Smith .   w - work vector
4655e76c431SBarry Smith .   fnorm - 2-norm of f
4665e76c431SBarry Smith 
4675e76c431SBarry Smith     Output parameters for func:
4685e76c431SBarry Smith .   g - residual evaluated at new iterate y
4695e76c431SBarry Smith .   y - new iterate (contains search direction on input)
4705e76c431SBarry Smith .   gnorm - 2-norm of g
4715e76c431SBarry Smith .   ynorm - 2-norm of search length
472761aaf1bSLois Curfman McInnes .   flag - set to 0 if the line search succeeds; a nonzero integer
473761aaf1bSLois Curfman McInnes            on failure.
474f59ffdedSLois Curfman McInnes 
475f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
476f59ffdedSLois Curfman McInnes 
477f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
4785e76c431SBarry Smith @*/
4795e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
480761aaf1bSLois Curfman McInnes                              double,double *,double*,int*) )
4815e76c431SBarry Smith {
482fbe28522SBarry Smith   if ((snes)->type == SNES_NLS)
4835e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
4845e42470aSBarry Smith   return 0;
4855e76c431SBarry Smith }
48652392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
4875e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
4885e42470aSBarry Smith {
4895e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
49052392280SLois Curfman McInnes   char    *p;
49152392280SLois Curfman McInnes   if (snes->prefix) p = snes->prefix; else p = "-";
49252392280SLois Curfman McInnes   MPIU_printf(snes->comm," method ls (system of nonlinear equations):\n");
49352392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search [basic,quadratic,cubic]\n",p);
49452392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_alpha alpha (default %g)\n",p,ls->alpha);
49552392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_maxstep max (default %g)\n",p,ls->maxstep);
49652392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_steptol tol (default %g)\n",p,ls->steptol);
4976b5873e3SBarry Smith   return 0;
4985e42470aSBarry Smith }
49952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
500a935fc98SLois Curfman McInnes static int SNESView_LS(PetscObject obj,Viewer viewer)
501a935fc98SLois Curfman McInnes {
502a935fc98SLois Curfman McInnes   SNES    snes = (SNES)obj;
503a935fc98SLois Curfman McInnes   SNES_LS *ls = (SNES_LS *)snes->data;
504a935fc98SLois Curfman McInnes   FILE    *fd = ViewerFileGetPointer_Private(viewer);
505a935fc98SLois Curfman McInnes   char    *cstring;
506a935fc98SLois Curfman McInnes 
507a935fc98SLois Curfman McInnes   if (ls->LineSearch == SNESNoLineSearch)
508a935fc98SLois Curfman McInnes     cstring = "SNESNoLineSearch";
509a935fc98SLois Curfman McInnes   else if (ls->LineSearch == SNESQuadraticLineSearch)
510a935fc98SLois Curfman McInnes     cstring = "SNESQuadraticLineSearch";
511a935fc98SLois Curfman McInnes   else if (ls->LineSearch == SNESCubicLineSearch)
512a935fc98SLois Curfman McInnes     cstring = "SNESCubicLineSearch";
513a935fc98SLois Curfman McInnes   else
514a935fc98SLois Curfman McInnes     cstring = "unknown";
515a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    line search variant: %s\n",cstring);
516a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    alpha=%g, maxstep=%g, steptol=%g\n",
517a935fc98SLois Curfman McInnes      ls->alpha,ls->maxstep,ls->steptol);
518a935fc98SLois Curfman McInnes   return 0;
519a935fc98SLois Curfman McInnes }
52052392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
5215e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5225e42470aSBarry Smith {
5235e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5245e42470aSBarry Smith   char    ver[16];
5255e42470aSBarry Smith   double  tmp;
5265e42470aSBarry Smith 
527df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) {
5285e42470aSBarry Smith     ls->alpha = tmp;
5295e42470aSBarry Smith   }
530df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) {
5315e42470aSBarry Smith     ls->maxstep = tmp;
5325e42470aSBarry Smith   }
533df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) {
5345e42470aSBarry Smith     ls->steptol = tmp;
5355e42470aSBarry Smith   }
536df60cc22SBarry Smith   if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) {
5375e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
5385e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
5395e42470aSBarry Smith     }
5405e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
5415e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
5425e42470aSBarry Smith     }
5435e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
5445e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
5455e42470aSBarry Smith     }
5468c48e012SBarry Smith     else {SETERRQ(1,"SNESSetFromOptions_LS: Unknown line search");}
5475e42470aSBarry Smith   }
5485e42470aSBarry Smith   return 0;
5495e42470aSBarry Smith }
5505e42470aSBarry Smith /* ------------------------------------------------------------ */
5515e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
5525e42470aSBarry Smith {
5535e42470aSBarry Smith   SNES_LS *neP;
5545e42470aSBarry Smith 
5551a3481a4SLois Curfman McInnes   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1,
5561a3481a4SLois Curfman McInnes     "SNESCreate_LS: Valid for SNES_NONLINEAR_EQUATIONS problems only");
55725c7317fSLois Curfman McInnes   snes->type		= SNES_EQ_NLS;
55849e3953dSBarry Smith   snes->setup		= SNESSetUp_LS;
55949e3953dSBarry Smith   snes->solve		= SNESSolve_LS;
5605e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
561bbb6d6a8SBarry Smith   snes->converged	= SNESDefaultConverged;
56249e3953dSBarry Smith   snes->printhelp       = SNESPrintHelp_LS;
56349e3953dSBarry Smith   snes->setfromoptions  = SNESSetFromOptions_LS;
564a935fc98SLois Curfman McInnes   snes->view            = SNESView_LS;
5655e42470aSBarry Smith 
56678b31e54SBarry Smith   neP			= PETSCNEW(SNES_LS);   CHKPTRQ(neP);
567ff782a9fSLois Curfman McInnes   PLogObjectMemory(snes,sizeof(SNES_LS));
5685e42470aSBarry Smith   snes->data    	= (void *) neP;
5695e42470aSBarry Smith   neP->alpha		= 1.e-4;
5705e42470aSBarry Smith   neP->maxstep		= 1.e8;
5715e42470aSBarry Smith   neP->steptol		= 1.e-12;
5725e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
5735e42470aSBarry Smith   return 0;
5745e42470aSBarry Smith }
5755e42470aSBarry Smith 
5765e42470aSBarry Smith 
5775e42470aSBarry Smith 
5785e42470aSBarry Smith 
579