xref: /petsc/src/snes/impls/ls/ls.c (revision 5e42470ac62cb63ed26e96b335b06ee82e25fc39)
15e76c431SBarry Smith #ifndef lint
2*5e42470aSBarry Smith static char vcid[] = "$Id: newls1.c,v 1.1 1995/03/20 22:59:54 bsmith Exp bsmith $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6*5e42470aSBarry Smith #include "newls1.h"
75e76c431SBarry Smith 
8*5e42470aSBarry Smith int SNESNewtonDefaultMonitor(SNES,int,Vec,Vec, double ,void*);
9*5e42470aSBarry Smith int SNESNewtonDefaultConverged(SNES, double ,double ,double,void * );
10*5e42470aSBarry Smith 
11*5e42470aSBarry Smith /*
12*5e42470aSBarry Smith      Implements a line search variant of Newton's Method
135e76c431SBarry Smith     for solving systems of nonlinear equations.
145e76c431SBarry Smith 
155e76c431SBarry Smith     Input parameters:
16*5e42470aSBarry Smith .   snes - nonlinear context obtained from SNESCreate()
175e76c431SBarry Smith 
18*5e42470aSBarry Smith     Output Parameters:
19*5e42470aSBarry Smith .   its  - Number of global iterations until termination.
205e76c431SBarry Smith 
215e76c431SBarry Smith     Notes:
22*5e42470aSBarry Smith     See SNESCreate() and SNESSetUp() for information on the definition and
235e76c431SBarry Smith     initialization of the nonlinear solver context.
245e76c431SBarry Smith 
255e76c431SBarry Smith     This implements essentially a truncated Newton method with a
265e76c431SBarry Smith     line search.  By default a cubic backtracking line search
275e76c431SBarry Smith     is employed, as described in the text "Numerical Methods for
285e76c431SBarry Smith     Unconstrained Optimization and Nonlinear Equations" by Dennis
29*5e42470aSBarry Smith     and Schnabel.  See the examples in src/snes/examples.
305e76c431SBarry Smith */
315e76c431SBarry Smith 
32*5e42470aSBarry Smith int SNESSolve_LS( SNES snes, int *outits )
335e76c431SBarry Smith {
34*5e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
35*5e42470aSBarry Smith   int     maxits, i, iters, line, nlconv, history_len,ierr,lits;
365e76c431SBarry Smith   double  fnorm, gnorm, gpnorm, xnorm, ynorm, *history;
37*5e42470aSBarry Smith   Vec     Y, X, F, G, W, TMP;
385e76c431SBarry Smith 
39*5e42470aSBarry Smith   history	= snes->conv_hist;	/* convergence history */
40*5e42470aSBarry Smith   history_len	= snes->conv_hist_len;	/* convergence history length */
41*5e42470aSBarry Smith   maxits	= snes->max_its;	/* maximum number of iterations */
42*5e42470aSBarry Smith   X		= snes->vec_sol;		/* solution vector */
43*5e42470aSBarry Smith   F		= snes->vec_res;		/* residual vector */
44*5e42470aSBarry Smith   Y		= snes->work[0];		/* work vectors */
45*5e42470aSBarry Smith   G		= snes->work[1];
46*5e42470aSBarry Smith   W		= snes->work[2];
475e76c431SBarry Smith 
48*5e42470aSBarry Smith   ierr = SNESComputeInitialGuess(snes,X); CHKERR(ierr);  /* X <- X_0 */
49*5e42470aSBarry Smith   VecNorm( X, &xnorm );		       /* xnorm = || X || */
50*5e42470aSBarry Smith   ierr = SNESComputeResidual(snes,X,F); CHKERR(ierr); /* (+/-) F(X) */
51*5e42470aSBarry Smith   VecNorm(F, &fnorm );	        	/* fnorm <- || F || */
52*5e42470aSBarry Smith   snes->norm = fnorm;
535e76c431SBarry Smith   if (history && history_len > 0) history[0] = fnorm;
54*5e42470aSBarry Smith   (*snes->Monitor)(snes,0,X,F,fnorm,snes->monP);  /* Monitor progress */
555e76c431SBarry Smith 
565e76c431SBarry Smith   for ( i=0; i<maxits; i++ ) {
57*5e42470aSBarry Smith        snes->iter = i+1;
585e76c431SBarry Smith 
595e76c431SBarry Smith        /* Solve J Y = -F, where J is Jacobian matrix */
60*5e42470aSBarry Smith        (*snes->ComputeJacobian)(X,&snes->jacobian,snes->jacP);
61*5e42470aSBarry Smith        ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian,0);
62*5e42470aSBarry Smith        ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERR(ierr);
63*5e42470aSBarry Smith        ierr = (*neP->LineSearch)(snes, X, F, G, Y, W, fnorm, &ynorm, &gnorm );
645e76c431SBarry Smith 
655e76c431SBarry Smith        TMP = F; F = G; G = TMP;
665e76c431SBarry Smith        TMP = X; X = Y; Y = TMP;
675e76c431SBarry Smith        fnorm = gnorm;
685e76c431SBarry Smith 
69*5e42470aSBarry Smith        snes->norm = fnorm;
705e76c431SBarry Smith        if (history && history_len > i+1) history[i+1] = fnorm;
71*5e42470aSBarry Smith        VecNorm( X, &xnorm );		/* xnorm = || X || */
72*5e42470aSBarry Smith        (*snes->Monitor)(snes,i+1,X,F,fnorm,snes->monP);  /* Monitor progress */
735e76c431SBarry Smith 
745e76c431SBarry Smith        /* Test for convergence */
75*5e42470aSBarry Smith        if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) {
76*5e42470aSBarry Smith            if (X != snes->vec_sol) VecCopy( X, snes->vec_sol );
775e76c431SBarry Smith            break;
785e76c431SBarry Smith        }
795e76c431SBarry Smith   }
805e76c431SBarry Smith   if (i == maxits) i--;
81*5e42470aSBarry Smith   *outits = i+1;
82*5e42470aSBarry Smith   return 0;
835e76c431SBarry Smith }
845e76c431SBarry Smith /* ------------------------------------------------------------ */
855e76c431SBarry Smith /*ARGSUSED*/
86*5e42470aSBarry Smith int SNESSetUp_LS(SNES snes )
875e76c431SBarry Smith {
88*5e42470aSBarry Smith   SNES_LS *ctx = (SNES_LS *)snes->data;
89*5e42470aSBarry Smith   int             ierr;
90*5e42470aSBarry Smith   snes->nwork = 3;
91*5e42470aSBarry Smith   ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr);
92*5e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work );
93*5e42470aSBarry Smith   return 0;
945e76c431SBarry Smith }
955e76c431SBarry Smith /* ------------------------------------------------------------ */
965e76c431SBarry Smith /*ARGSUSED*/
97*5e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj)
985e76c431SBarry Smith {
99*5e42470aSBarry Smith   SNES snes = (SNES) obj;
100*5e42470aSBarry Smith   SLESDestroy(snes->sles);
101*5e42470aSBarry Smith   VecFreeVecs(snes->work, snes->nwork );
102*5e42470aSBarry Smith   PLogObjectDestroy(obj);
103*5e42470aSBarry Smith   PETSCHEADERDESTROY(obj);
104*5e42470aSBarry Smith   return 0;
1055e76c431SBarry Smith }
106*5e42470aSBarry Smith /*@
107*5e42470aSBarry Smith    SNESDefaultMonitor - Default monitor for NLE solvers.  Prints the
108*5e42470aSBarry Smith    residual norm at each iteration.
109*5e42470aSBarry Smith 
110*5e42470aSBarry Smith    Input Parameters:
111*5e42470aSBarry Smith .  nlP - nonlinear context
112*5e42470aSBarry Smith .  x - current iterate
113*5e42470aSBarry Smith .  f - current residual (+/-)
114*5e42470aSBarry Smith .  fnorm - 2-norm residual value (may be estimated).
115*5e42470aSBarry Smith 
116*5e42470aSBarry Smith    Notes:
117*5e42470aSBarry Smith    f is either the residual or its negative, depending on the user's
118*5e42470aSBarry Smith    preference, as set with NLSetResidualRoutine().
119*5e42470aSBarry Smith 
120*5e42470aSBarry Smith 
121*5e42470aSBarry Smith @*/
122*5e42470aSBarry Smith int SNESDefaultMonitor(SNES snes,int its, Vec x,Vec f,double fnorm,void *dummy)
123*5e42470aSBarry Smith {
124*5e42470aSBarry Smith   fprintf( stdout, "iter = %d, residual norm %g \n",its,fnorm);
125*5e42470aSBarry Smith   return 0;
126*5e42470aSBarry Smith }
127*5e42470aSBarry Smith 
128*5e42470aSBarry Smith /*@
129*5e42470aSBarry Smith    SNESDefaultConverged - Default test for monitoring the convergence
130*5e42470aSBarry Smith    of the NLE solvers.
131*5e42470aSBarry Smith 
132*5e42470aSBarry Smith    Input Parameters:
133*5e42470aSBarry Smith .  nlP - nonlinear context
134*5e42470aSBarry Smith .  xnorm - 2-norm of current iterate
135*5e42470aSBarry Smith .  pnorm - 2-norm of current step
136*5e42470aSBarry Smith .  fnorm - 2-norm of residual
137*5e42470aSBarry Smith 
138*5e42470aSBarry Smith    Returns:
139*5e42470aSBarry Smith $  2  if  ( fnorm < atol ),
140*5e42470aSBarry Smith $  3  if  ( pnorm < xtol*xnorm ),
141*5e42470aSBarry Smith $ -2  if  ( nres > max_res ),
142*5e42470aSBarry Smith $  0  otherwise,
143*5e42470aSBarry Smith 
144*5e42470aSBarry Smith    where
145*5e42470aSBarry Smith $    atol    - absolute residual norm tolerance,
146*5e42470aSBarry Smith $              set with NLSetAbsConvergenceTol()
147*5e42470aSBarry Smith $    max_res - maximum number of residual evaluations,
148*5e42470aSBarry Smith $              set with NLSetMaxResidualEvaluations()
149*5e42470aSBarry Smith $    nres    - number of residual evaluations
150*5e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
151*5e42470aSBarry Smith 
152*5e42470aSBarry Smith $              set with NLSetMaxResidualEvaluations()
153*5e42470aSBarry Smith $    nres    - number of residual evaluations
154*5e42470aSBarry Smith $    xtol    - relative residual norm tolerance,
155*5e42470aSBarry Smith $              set with NLSetRelConvergenceTol()
156*5e42470aSBarry Smith 
157*5e42470aSBarry Smith @*/
158*5e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm,
159*5e42470aSBarry Smith                          void *dummy)
160*5e42470aSBarry Smith {
161*5e42470aSBarry Smith   if (fnorm < snes->atol) {
162*5e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Converged due to absolute residual norm %g < %g\n",
163*5e42470aSBarry Smith                    fnorm,snes->atol);
164*5e42470aSBarry Smith     return 2;
165*5e42470aSBarry Smith   }
166*5e42470aSBarry Smith   if (pnorm < snes->xtol*(xnorm)) {
167*5e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Converged due to small update length  %g < %g*%g\n",
168*5e42470aSBarry Smith                    pnorm,snes->xtol,xnorm);
169*5e42470aSBarry Smith     return 3;
170*5e42470aSBarry Smith   }
171*5e42470aSBarry Smith   return 0;
172*5e42470aSBarry Smith }
173*5e42470aSBarry Smith 
1745e76c431SBarry Smith /* ------------------------------------------------------------ */
1755e76c431SBarry Smith /*ARGSUSED*/
1765e76c431SBarry Smith /*@
177*5e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1785e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1795e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1805e76c431SBarry Smith 
1815e76c431SBarry Smith    Input Parameters:
182*5e42470aSBarry Smith .  snes - nonlinear context
1835e76c431SBarry Smith .  x - current iterate
1845e76c431SBarry Smith .  f - residual evaluated at x
1855e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1865e76c431SBarry Smith .  w - work vector
1875e76c431SBarry Smith .  fnorm - 2-norm of f
1885e76c431SBarry Smith 
1895e76c431SBarry Smith    Output parameters:
1905e76c431SBarry Smith .  g - residual evaluated at new iterate y
1915e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1925e76c431SBarry Smith .  gnorm - 2-norm of g
1935e76c431SBarry Smith .  ynorm - 2-norm of search length
1945e76c431SBarry Smith 
1955e76c431SBarry Smith    Returns:
1965e76c431SBarry Smith    1, indicating success of the step.
1975e76c431SBarry Smith 
1985e76c431SBarry Smith @*/
199*5e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
200*5e42470aSBarry Smith                              double fnorm, double *ynorm, double *gnorm )
2015e76c431SBarry Smith {
202*5e42470aSBarry Smith   int    ierr;
203*5e42470aSBarry Smith   Scalar one = 1.0;
204*5e42470aSBarry Smith   VecNorm(y, ynorm );	/* ynorm = || y ||    */
205*5e42470aSBarry Smith   VecAXPY(&one, x, y );	/* y <- x + y         */
206*5e42470aSBarry Smith   ierr = SNESComputeResidual(snes,y,g); CHKERR(ierr);
207*5e42470aSBarry Smith   VecNorm( g, gnorm ); 	/* gnorm = || g ||    */
2085e76c431SBarry Smith   return 1;
2095e76c431SBarry Smith }
2105e76c431SBarry Smith /* ------------------------------------------------------------------ */
2115e76c431SBarry Smith /*@
212*5e42470aSBarry Smith    SNESCubicLineSearch - This routine performs a cubic line search.
2135e76c431SBarry Smith 
2145e76c431SBarry Smith    Input Parameters:
215*5e42470aSBarry Smith .  snes - nonlinear context
2165e76c431SBarry Smith .  x - current iterate
2175e76c431SBarry Smith .  f - residual evaluated at x
2185e76c431SBarry Smith .  y - search direction (contains new iterate on output)
2195e76c431SBarry Smith .  w - work vector
2205e76c431SBarry Smith .  fnorm - 2-norm of f
2215e76c431SBarry Smith 
2225e76c431SBarry Smith    Output parameters:
2235e76c431SBarry Smith .  g - residual evaluated at new iterate y
2245e76c431SBarry Smith .  y - new iterate (contains search direction on input)
2255e76c431SBarry Smith .  gnorm - 2-norm of g
2265e76c431SBarry Smith .  ynorm - 2-norm of search length
2275e76c431SBarry Smith 
2285e76c431SBarry Smith    Returns:
2295e76c431SBarry Smith    1 if the line search succeeds; 0 if the line search fails.
2305e76c431SBarry Smith 
2315e76c431SBarry Smith    Notes:
2325e76c431SBarry Smith    Use either NLSetStepLineSearchRoutines() or NLSetLineSearchRoutine()
233*5e42470aSBarry Smith    to set this routine within the SNES_NLS1 method.
2345e76c431SBarry Smith 
2355e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
2365e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
2375e76c431SBarry Smith 
2385e76c431SBarry Smith @*/
239*5e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
240*5e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
2415e76c431SBarry Smith {
242*5e42470aSBarry Smith   double  steptol, initslope;
243*5e42470aSBarry Smith   double  lambdaprev, gnormprev;
2445e76c431SBarry Smith   double  a, b, d, t1, t2;
245*5e42470aSBarry Smith   Scalar  cinitslope,clambda;
246*5e42470aSBarry Smith   int     ierr,count;
247*5e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
248*5e42470aSBarry Smith   Scalar  one = 1.0,scale;
249*5e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2505e76c431SBarry Smith 
2515e76c431SBarry Smith   alpha   = neP->alpha;
2525e76c431SBarry Smith   maxstep = neP->maxstep;
2535e76c431SBarry Smith   steptol = neP->steptol;
2545e76c431SBarry Smith 
255*5e42470aSBarry Smith   VecNorm(y, ynorm );
2565e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
257*5e42470aSBarry Smith     scale = maxstep/(*ynorm);
258*5e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
259*5e42470aSBarry Smith     VecScale(&scale, y );
2605e76c431SBarry Smith     *ynorm = maxstep;
2615e76c431SBarry Smith   }
2625e76c431SBarry Smith   minlambda = steptol/(*ynorm);
263*5e42470aSBarry Smith #if defined(PETSC_COMPLEX)
264*5e42470aSBarry Smith   VecDot(f, y, &cinitslope );
265*5e42470aSBarry Smith   initslope = real(cinitslope);
266*5e42470aSBarry Smith #else
267*5e42470aSBarry Smith   VecDot(f, y, &initslope );
268*5e42470aSBarry Smith #endif
2695e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2705e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2715e76c431SBarry Smith 
272*5e42470aSBarry Smith   VecCopy(y, w );
273*5e42470aSBarry Smith   VecAXPY(&one, x, w );
274*5e42470aSBarry Smith   ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
275*5e42470aSBarry Smith   VecNorm(g, gnorm );
2765e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
277*5e42470aSBarry Smith       VecCopy(w, y );
278*5e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
279*5e42470aSBarry Smith       return 0;
2805e76c431SBarry Smith   }
2815e76c431SBarry Smith 
2825e76c431SBarry Smith   /* Fit points with quadratic */
2835e76c431SBarry Smith   lambda = 1.0; count = 0;
2845e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
2855e76c431SBarry Smith   lambdaprev = lambda;
2865e76c431SBarry Smith   gnormprev = *gnorm;
2875e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
2885e76c431SBarry Smith       lambda = .1*lambda;
2895e76c431SBarry Smith   } else lambda = lambdatemp;
290*5e42470aSBarry Smith   VecCopy(x, w );
291*5e42470aSBarry Smith #if defined(PETSC_COMPLEX)
292*5e42470aSBarry Smith   clambda = lambda; VecAXPY(&clambda, y, w );
293*5e42470aSBarry Smith #else
294*5e42470aSBarry Smith   VecAXPY(&lambda, y, w );
295*5e42470aSBarry Smith #endif
296*5e42470aSBarry Smith   ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
297*5e42470aSBarry Smith   VecNorm(g, gnorm );
2985e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
299*5e42470aSBarry Smith       VecCopy(w, y );
300*5e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
301*5e42470aSBarry Smith       return 0;
3025e76c431SBarry Smith   }
3035e76c431SBarry Smith 
3045e76c431SBarry Smith   /* Fit points with cubic */
3055e76c431SBarry Smith   count = 1;
3065e76c431SBarry Smith   while (1) {
3075e76c431SBarry Smith       if (lambda <= minlambda) { /* bad luck; use full step */
308*5e42470aSBarry Smith            PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
309*5e42470aSBarry Smith            PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
3105e76c431SBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
311*5e42470aSBarry Smith            VecCopy(w, y );
3125e76c431SBarry Smith            return 0;
3135e76c431SBarry Smith       }
3145e76c431SBarry Smith       t1 = *gnorm - fnorm - lambda*initslope;
3155e76c431SBarry Smith       t2 = gnormprev  - fnorm - lambdaprev*initslope;
3165e76c431SBarry Smith       a = (t1/(lambda*lambda) -
3175e76c431SBarry Smith                 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3185e76c431SBarry Smith       b = (-lambdaprev*t1/(lambda*lambda) +
3195e76c431SBarry Smith                 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
3205e76c431SBarry Smith       d = b*b - 3*a*initslope;
3215e76c431SBarry Smith       if (d < 0.0) d = 0.0;
3225e76c431SBarry Smith       if (a == 0.0) {
3235e76c431SBarry Smith          lambdatemp = -initslope/(2.0*b);
3245e76c431SBarry Smith       } else {
3255e76c431SBarry Smith          lambdatemp = (-b + sqrt(d))/(3.0*a);
3265e76c431SBarry Smith       }
3275e76c431SBarry Smith       if (lambdatemp > .5*lambda) {
3285e76c431SBarry Smith          lambdatemp = .5*lambda;
3295e76c431SBarry Smith       }
3305e76c431SBarry Smith       lambdaprev = lambda;
3315e76c431SBarry Smith       gnormprev = *gnorm;
3325e76c431SBarry Smith       if (lambdatemp <= .1*lambda) {
3335e76c431SBarry Smith          lambda = .1*lambda;
3345e76c431SBarry Smith       }
3355e76c431SBarry Smith       else lambda = lambdatemp;
336*5e42470aSBarry Smith       VecCopy( x, w );
337*5e42470aSBarry Smith #if defined(PETSC_COMPLEX)
338*5e42470aSBarry Smith       VecAXPY(&clambda, y, w );
339*5e42470aSBarry Smith #else
340*5e42470aSBarry Smith       VecAXPY(&lambda, y, w );
341*5e42470aSBarry Smith #endif
342*5e42470aSBarry Smith       ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
343*5e42470aSBarry Smith       VecNorm(g, gnorm );
3445e76c431SBarry Smith       if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
345*5e42470aSBarry Smith          VecCopy(w, y );
346*5e42470aSBarry Smith          PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
347*5e42470aSBarry Smith          return 0;
3485e76c431SBarry Smith       }
3495e76c431SBarry Smith       count++;
3505e76c431SBarry Smith    }
351*5e42470aSBarry Smith   return 0;
3525e76c431SBarry Smith }
353*5e42470aSBarry Smith /*@
354*5e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3555e76c431SBarry Smith 
356*5e42470aSBarry Smith    Input Parameters:
357*5e42470aSBarry Smith .  snes - nonlinear context
358*5e42470aSBarry Smith .  x - current iterate
359*5e42470aSBarry Smith .  f - residual evaluated at x
360*5e42470aSBarry Smith .  y - search direction (contains new iterate on output)
361*5e42470aSBarry Smith .  w - work vector
362*5e42470aSBarry Smith .  fnorm - 2-norm of f
363*5e42470aSBarry Smith 
364*5e42470aSBarry Smith    Output parameters:
365*5e42470aSBarry Smith .  g - residual evaluated at new iterate y
366*5e42470aSBarry Smith .  y - new iterate (contains search direction on input)
367*5e42470aSBarry Smith .  gnorm - 2-norm of g
368*5e42470aSBarry Smith .  ynorm - 2-norm of search length
369*5e42470aSBarry Smith 
370*5e42470aSBarry Smith    Returns:
371*5e42470aSBarry Smith    1 if the line search succeeds; 0 if the line search fails.
372*5e42470aSBarry Smith 
373*5e42470aSBarry Smith    Notes:
374*5e42470aSBarry Smith    Use SNESSetLineSearchRoutines()
375*5e42470aSBarry Smith    to set this routine within the SNES_NLS1 method.
376*5e42470aSBarry Smith 
377*5e42470aSBarry Smith @*/
378*5e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
379*5e42470aSBarry Smith                               double fnorm, double *ynorm, double *gnorm )
3805e76c431SBarry Smith {
381*5e42470aSBarry Smith   double  steptol, initslope;
382*5e42470aSBarry Smith   double  lambdaprev, gnormprev;
383*5e42470aSBarry Smith   double  a, b, d, t1, t2;
384*5e42470aSBarry Smith   Scalar  cinitslope,clambda;
385*5e42470aSBarry Smith   int     ierr,count;
386*5e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
387*5e42470aSBarry Smith   Scalar  one = 1.0,scale;
388*5e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
3895e76c431SBarry Smith 
390*5e42470aSBarry Smith   alpha   = neP->alpha;
391*5e42470aSBarry Smith   maxstep = neP->maxstep;
392*5e42470aSBarry Smith   steptol = neP->steptol;
3935e76c431SBarry Smith 
394*5e42470aSBarry Smith   VecNorm(y, ynorm );
395*5e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
396*5e42470aSBarry Smith     scale = maxstep/(*ynorm);
397*5e42470aSBarry Smith     VecScale(&scale, y );
398*5e42470aSBarry Smith     *ynorm = maxstep;
3995e76c431SBarry Smith   }
400*5e42470aSBarry Smith   minlambda = steptol/(*ynorm);
401*5e42470aSBarry Smith #if defined(PETSC_COMPLEX)
402*5e42470aSBarry Smith   VecDot(f, y, &cinitslope );
403*5e42470aSBarry Smith   initslope = real(cinitslope);
404*5e42470aSBarry Smith #else
405*5e42470aSBarry Smith   VecDot(f, y, &initslope );
406*5e42470aSBarry Smith #endif
407*5e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
408*5e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
409*5e42470aSBarry Smith 
410*5e42470aSBarry Smith   VecCopy(y, w );
411*5e42470aSBarry Smith   VecAXPY(&one, x, w );
412*5e42470aSBarry Smith   ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
413*5e42470aSBarry Smith   VecNorm(g, gnorm );
414*5e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
415*5e42470aSBarry Smith       VecCopy(w, y );
416*5e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
417*5e42470aSBarry Smith       return 0;
418*5e42470aSBarry Smith   }
419*5e42470aSBarry Smith 
420*5e42470aSBarry Smith   /* Fit points with quadratic */
421*5e42470aSBarry Smith   lambda = 1.0; count = 0;
422*5e42470aSBarry Smith   count = 1;
423*5e42470aSBarry Smith   while (1) {
424*5e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
425*5e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
426*5e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
427*5e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
428*5e42470aSBarry Smith       VecCopy(w, y );
429*5e42470aSBarry Smith       return 0;
430*5e42470aSBarry Smith     }
431*5e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
432*5e42470aSBarry Smith     lambdaprev = lambda;
433*5e42470aSBarry Smith     gnormprev = *gnorm;
434*5e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
435*5e42470aSBarry Smith       lambda = .1*lambda;
436*5e42470aSBarry Smith     } else lambda = lambdatemp;
437*5e42470aSBarry Smith     VecCopy(x, w );
438*5e42470aSBarry Smith #if defined(PETSC_COMPLEX)
439*5e42470aSBarry Smith     clambda = lambda; VecAXPY(&clambda, y, w );
440*5e42470aSBarry Smith #else
441*5e42470aSBarry Smith     VecAXPY(&lambda, y, w );
442*5e42470aSBarry Smith #endif
443*5e42470aSBarry Smith     ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr);
444*5e42470aSBarry Smith     VecNorm(g, gnorm );
445*5e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
446*5e42470aSBarry Smith       VecCopy(w, y );
447*5e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
448*5e42470aSBarry Smith       return 0;
449*5e42470aSBarry Smith     }
450*5e42470aSBarry Smith     count++;
451*5e42470aSBarry Smith   }
452*5e42470aSBarry Smith 
453*5e42470aSBarry Smith   return 0;
4545e76c431SBarry Smith }
4555e76c431SBarry Smith /* ------------------------------------------------------------ */
4565e76c431SBarry Smith /*@C
457*5e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
458*5e42470aSBarry Smith    by the method SNES_NLS1.
4595e76c431SBarry Smith 
4605e76c431SBarry Smith    Input Parameters:
461*5e42470aSBarry Smith .  snes - nonlinear context obtained from NLCreate()
4625e76c431SBarry Smith .  func - pointer to int function
4635e76c431SBarry Smith 
4645e76c431SBarry Smith    Possible routines:
465*5e42470aSBarry Smith    SNESCubicLineSearch() - default line search
466*5e42470aSBarry Smith    SNESNoLineSearch() - the full Newton step (actually not a line search)
4675e76c431SBarry Smith 
4685e76c431SBarry Smith    Calling sequence of func:
469*5e42470aSBarry Smith .  func (SNES snes, Vec x, Vec f, Vec g, Vec y,
470*5e42470aSBarry Smith          Vec w, double fnorm, double *ynorm, double *gnorm )
4715e76c431SBarry Smith 
4725e76c431SBarry Smith     Input parameters for func:
473*5e42470aSBarry Smith .   snes - nonlinear context
4745e76c431SBarry Smith .   x - current iterate
4755e76c431SBarry Smith .   f - residual evaluated at x
4765e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4775e76c431SBarry Smith .   w - work vector
4785e76c431SBarry Smith .   fnorm - 2-norm of f
4795e76c431SBarry Smith 
4805e76c431SBarry Smith     Output parameters for func:
4815e76c431SBarry Smith .   g - residual evaluated at new iterate y
4825e76c431SBarry Smith .   y - new iterate (contains search direction on input)
4835e76c431SBarry Smith .   gnorm - 2-norm of g
4845e76c431SBarry Smith .   ynorm - 2-norm of search length
4855e76c431SBarry Smith 
4865e76c431SBarry Smith     Returned by func:
4875e76c431SBarry Smith     1 if the line search succeeds; 0 if the line search fails.
4885e76c431SBarry Smith @*/
489*5e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
490*5e42470aSBarry Smith                              double,double *,double*) )
4915e76c431SBarry Smith {
492*5e42470aSBarry Smith   if ((snes)->type == SNES_NLS1)
493*5e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
494*5e42470aSBarry Smith   return 0;
4955e76c431SBarry Smith }
496*5e42470aSBarry Smith 
497*5e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
498*5e42470aSBarry Smith {
499*5e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
500*5e42470aSBarry Smith   fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n");
501*5e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha);
502*5e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep);
503*5e42470aSBarry Smith   fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol);
504*5e42470aSBarry Smith }
505*5e42470aSBarry Smith 
506*5e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
507*5e42470aSBarry Smith {
508*5e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
509*5e42470aSBarry Smith   char    ver[16];
510*5e42470aSBarry Smith   double  tmp;
511*5e42470aSBarry Smith 
512*5e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_alpa",&tmp)) {
513*5e42470aSBarry Smith     ls->alpha = tmp;
514*5e42470aSBarry Smith   }
515*5e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_maxstep",&tmp)) {
516*5e42470aSBarry Smith     ls->maxstep = tmp;
517*5e42470aSBarry Smith   }
518*5e42470aSBarry Smith   if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_steptol",&tmp)) {
519*5e42470aSBarry Smith     ls->steptol = tmp;
520*5e42470aSBarry Smith   }
521*5e42470aSBarry Smith   if (OptionsGetString(0,snes->prefix,"-snes_line_search",ver,16)) {
522*5e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
523*5e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
524*5e42470aSBarry Smith     }
525*5e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
526*5e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
527*5e42470aSBarry Smith     }
528*5e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
529*5e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
530*5e42470aSBarry Smith     }
531*5e42470aSBarry Smith     else {SETERR(1,"Unknown line search?");}
532*5e42470aSBarry Smith   }
533*5e42470aSBarry Smith   return 0;
534*5e42470aSBarry Smith }
535*5e42470aSBarry Smith 
536*5e42470aSBarry Smith /* ------------------------------------------------------------ */
537*5e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
538*5e42470aSBarry Smith {
539*5e42470aSBarry Smith   SNES_LS *neP;
540*5e42470aSBarry Smith 
541*5e42470aSBarry Smith   snes->type		= SNES_NLS1;
542*5e42470aSBarry Smith   snes->Setup		= SNESSetUp_LS;
543*5e42470aSBarry Smith   snes->Solver		= SNESSolve_LS;
544*5e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
545*5e42470aSBarry Smith   snes->Monitor  	= SNESDefaultMonitor;
546*5e42470aSBarry Smith   snes->Converged	= SNESDefaultConverged;
547*5e42470aSBarry Smith   snes->PrintHelp       = SNESPrintHelp_LS;
548*5e42470aSBarry Smith   snes->SetFromOptions  = SNESSetFromOptions_LS;
549*5e42470aSBarry Smith 
550*5e42470aSBarry Smith   neP			= NEW(SNES_LS);   CHKPTR(neP);
551*5e42470aSBarry Smith   snes->data    	= (void *) neP;
552*5e42470aSBarry Smith   neP->alpha		= 1.e-4;
553*5e42470aSBarry Smith   neP->maxstep		= 1.e8;
554*5e42470aSBarry Smith   neP->steptol		= 1.e-12;
555*5e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
556*5e42470aSBarry Smith   return 0;
557*5e42470aSBarry Smith }
558*5e42470aSBarry Smith 
559*5e42470aSBarry Smith 
560*5e42470aSBarry Smith 
561*5e42470aSBarry Smith 
562