xref: /petsc/src/snes/impls/ls/ls.c (revision 336446d4b89e5efeab813bee47fad02eb7b35adf)
15e76c431SBarry Smith #ifndef lint
2*336446d4SLois Curfman McInnes static char vcid[] = "$Id: ls.c,v 1.35 1995/07/27 03:01:17 curfman Exp curfman $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
6fbe28522SBarry Smith #include "ls.h"
7a935fc98SLois Curfman McInnes #include "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
275e42470aSBarry Smith     and Schnabel.  See the examples in src/snes/examples.
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;
37*336446d4SLois Curfman McInnes   SLES         sles;
38*336446d4SLois 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 */
505e42470aSBarry Smith   VecNorm(X,&xnorm);		                         /* xnorm = || X || */
5178b31e54SBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);   /* (+/-) F(X) */
525e42470aSBarry Smith   VecNorm(F,&fnorm);	        	                 /* 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 
58*336446d4SLois Curfman McInnes   /* Set the KSP stopping criteria to use the Eisenstat-Walker method */
59*336446d4SLois Curfman McInnes   if (snes->ksp_ewconv) {
60*336446d4SLois Curfman McInnes     ierr = SNESGetSLES(snes,&sles); CHKERRQ(ierr);
61*336446d4SLois Curfman McInnes     ierr = SLESGetKSP(sles,&ksp); CHKERRQ(ierr);
62*336446d4SLois Curfman McInnes     ierr = KSPSetConvergenceTest(ksp,SNES_KSP_EW_Converged_Private,
63*336446d4SLois Curfman McInnes            (void *)snes);  CHKERRQ(ierr);
64*336446d4SLois Curfman McInnes   }
65*336446d4SLois 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;
865e42470aSBarry Smith        VecNorm(X,&xnorm);		/* xnorm = || X || */
87bbb6d6a8SBarry Smith        if (snes->monitor)
88bbb6d6a8SBarry Smith          {(*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) {
9339e2f89bSBarry Smith              VecCopy(X,snes->vec_sol);
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;
1225e42470aSBarry Smith   VecFreeVecs(snes->work,snes->nwork);
12378b31e54SBarry Smith   PETSCFREE(snes->data);
1245e42470aSBarry Smith   return 0;
1255e76c431SBarry Smith }
1265e76c431SBarry Smith /* ------------------------------------------------------------ */
1275e76c431SBarry Smith /*ARGSUSED*/
1285e76c431SBarry Smith /*@
1295e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1305e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1315e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1325e76c431SBarry Smith 
1335e76c431SBarry Smith    Input Parameters:
1345e42470aSBarry Smith .  snes - nonlinear context
1355e76c431SBarry Smith .  x - current iterate
1365e76c431SBarry Smith .  f - residual evaluated at x
1375e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1385e76c431SBarry Smith .  w - work vector
1395e76c431SBarry Smith .  fnorm - 2-norm of f
1405e76c431SBarry Smith 
141c4a48953SLois Curfman McInnes    Output Parameters:
1425e76c431SBarry Smith .  g - residual evaluated at new iterate y
1435e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1445e76c431SBarry Smith .  gnorm - 2-norm of g
1455e76c431SBarry Smith .  ynorm - 2-norm of search length
146761aaf1bSLois Curfman McInnes .  flag - set to 0, indicating a successful line search
1475e76c431SBarry Smith 
148c4a48953SLois Curfman McInnes    Options Database Key:
149c4a48953SLois Curfman McInnes $  -snes_line_search basic
150c4a48953SLois Curfman McInnes 
15128ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
15228ae5a21SLois Curfman McInnes 
153f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
154a935fc98SLois Curfman McInnes           SNESSetLineSearchRoutine()
1555e76c431SBarry Smith @*/
1565e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
157761aaf1bSLois Curfman McInnes               double fnorm, double *ynorm, double *gnorm,int *flag )
1585e76c431SBarry Smith {
1595e42470aSBarry Smith   int    ierr;
1605e42470aSBarry Smith   Scalar one = 1.0;
161761aaf1bSLois Curfman McInnes   *flag = 0;
1627857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
1635e42470aSBarry Smith   VecNorm(y,ynorm);                            /* ynorm = || y || */
1645e42470aSBarry Smith   VecAXPY(&one,x,y);	                       /* y <- x + y      */
16578b31e54SBarry Smith   ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr);
1665e42470aSBarry Smith   VecNorm(g,gnorm); 	                       /* gnorm = || g || */
1677857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
168761aaf1bSLois Curfman McInnes   return 0;
1695e76c431SBarry Smith }
1705e76c431SBarry Smith /* ------------------------------------------------------------------ */
1715e76c431SBarry Smith /*@
172f59ffdedSLois Curfman McInnes    SNESCubicLineSearch - This routine performs a cubic line search and
173f59ffdedSLois Curfman McInnes    is the default line search method.
1745e76c431SBarry Smith 
1755e76c431SBarry Smith    Input Parameters:
1765e42470aSBarry Smith .  snes - nonlinear context
1775e76c431SBarry Smith .  x - current iterate
1785e76c431SBarry Smith .  f - residual evaluated at x
1795e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1805e76c431SBarry Smith .  w - work vector
1815e76c431SBarry Smith .  fnorm - 2-norm of f
1825e76c431SBarry Smith 
1835e76c431SBarry Smith    Output parameters:
1845e76c431SBarry Smith .  g - residual evaluated at new iterate y
1855e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1865e76c431SBarry Smith .  gnorm - 2-norm of g
1875e76c431SBarry Smith .  ynorm - 2-norm of search length
188761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
1895e76c431SBarry Smith 
190c4a48953SLois Curfman McInnes    Options Database Key:
191c4a48953SLois Curfman McInnes $  -snes_line_search cubic
192c4a48953SLois Curfman McInnes 
1935e76c431SBarry Smith    Notes:
1945e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
1955e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
1965e76c431SBarry Smith 
19728ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
19828ae5a21SLois Curfman McInnes 
199f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
2005e76c431SBarry Smith @*/
2015e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
202761aaf1bSLois Curfman McInnes                 double fnorm, double *ynorm, double *gnorm,int *flag)
2035e76c431SBarry Smith {
2045e42470aSBarry Smith   double  steptol, initslope;
2055e42470aSBarry Smith   double  lambdaprev, gnormprev;
2065e76c431SBarry Smith   double  a, b, d, t1, t2;
2076b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2085e42470aSBarry Smith   Scalar  cinitslope,clambda;
2096b5873e3SBarry Smith #endif
2105e42470aSBarry Smith   int     ierr,count;
2115e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2125e42470aSBarry Smith   Scalar  one = 1.0,scale;
2135e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
2145e76c431SBarry Smith 
2157857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
216761aaf1bSLois Curfman McInnes   *flag = 0;
2175e76c431SBarry Smith   alpha   = neP->alpha;
2185e76c431SBarry Smith   maxstep = neP->maxstep;
2195e76c431SBarry Smith   steptol = neP->steptol;
2205e76c431SBarry Smith 
2215e42470aSBarry Smith   VecNorm(y, ynorm );
2225e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2235e42470aSBarry Smith     scale = maxstep/(*ynorm);
2246b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2256b5873e3SBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale));
2266b5873e3SBarry Smith #else
2275e42470aSBarry Smith     PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale);
2286b5873e3SBarry Smith #endif
2295e42470aSBarry Smith     VecScale(&scale, y );
2305e76c431SBarry Smith     *ynorm = maxstep;
2315e76c431SBarry Smith   }
2325e76c431SBarry Smith   minlambda = steptol/(*ynorm);
2335e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2345e42470aSBarry Smith   VecDot(f, y, &cinitslope );
2355e42470aSBarry Smith   initslope = real(cinitslope);
2365e42470aSBarry Smith #else
2375e42470aSBarry Smith   VecDot(f, y, &initslope );
2385e42470aSBarry Smith #endif
2395e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2405e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2415e76c431SBarry Smith 
2425e42470aSBarry Smith   VecCopy(y, w );
2435e42470aSBarry Smith   VecAXPY(&one, x, w );
24478b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
2455e42470aSBarry Smith   VecNorm(g, gnorm );
2465e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
2475e42470aSBarry Smith       VecCopy(w, y );
2485e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
249d93a2b8dSBarry Smith       goto theend;
2505e76c431SBarry Smith   }
2515e76c431SBarry Smith 
2525e76c431SBarry Smith   /* Fit points with quadratic */
2535e76c431SBarry Smith   lambda = 1.0; count = 0;
2545e76c431SBarry Smith   lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
2555e76c431SBarry Smith   lambdaprev = lambda;
2565e76c431SBarry Smith   gnormprev = *gnorm;
2575e76c431SBarry Smith   if (lambdatemp <= .1*lambda) {
2585e76c431SBarry Smith       lambda = .1*lambda;
2595e76c431SBarry Smith   } else lambda = lambdatemp;
2605e42470aSBarry Smith   VecCopy(x, w );
2615e42470aSBarry Smith #if defined(PETSC_COMPLEX)
2625e42470aSBarry Smith   clambda = lambda; VecAXPY(&clambda, y, w );
2635e42470aSBarry Smith #else
2645e42470aSBarry Smith   VecAXPY(&lambda, y, w );
2655e42470aSBarry Smith #endif
26678b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
2675e42470aSBarry Smith   VecNorm(g, gnorm );
2685e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
2695e42470aSBarry Smith       VecCopy(w, y );
2705e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
271d93a2b8dSBarry Smith       goto theend;
2725e76c431SBarry Smith   }
2735e76c431SBarry Smith 
2745e76c431SBarry Smith   /* Fit points with cubic */
2755e76c431SBarry Smith   count = 1;
2765e76c431SBarry Smith   while (1) {
2775e76c431SBarry Smith       if (lambda <= minlambda) { /* bad luck; use full step */
2785e42470aSBarry Smith            PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
2795e42470aSBarry Smith            PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
2805e76c431SBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
2815e42470aSBarry Smith            VecCopy(w, y );
282761aaf1bSLois Curfman McInnes            *flag = -1; break;
2835e76c431SBarry Smith       }
2845e76c431SBarry Smith       t1 = *gnorm - fnorm - lambda*initslope;
2855e76c431SBarry Smith       t2 = gnormprev  - fnorm - lambdaprev*initslope;
2865e76c431SBarry Smith       a = (t1/(lambda*lambda) -
2875e76c431SBarry Smith                 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2885e76c431SBarry Smith       b = (-lambdaprev*t1/(lambda*lambda) +
2895e76c431SBarry Smith                 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2905e76c431SBarry Smith       d = b*b - 3*a*initslope;
2915e76c431SBarry Smith       if (d < 0.0) d = 0.0;
2925e76c431SBarry Smith       if (a == 0.0) {
2935e76c431SBarry Smith          lambdatemp = -initslope/(2.0*b);
2945e76c431SBarry Smith       } else {
2955e76c431SBarry Smith          lambdatemp = (-b + sqrt(d))/(3.0*a);
2965e76c431SBarry Smith       }
2975e76c431SBarry Smith       if (lambdatemp > .5*lambda) {
2985e76c431SBarry Smith          lambdatemp = .5*lambda;
2995e76c431SBarry Smith       }
3005e76c431SBarry Smith       lambdaprev = lambda;
3015e76c431SBarry Smith       gnormprev = *gnorm;
3025e76c431SBarry Smith       if (lambdatemp <= .1*lambda) {
3035e76c431SBarry Smith          lambda = .1*lambda;
3045e76c431SBarry Smith       }
3055e76c431SBarry Smith       else lambda = lambdatemp;
3065e42470aSBarry Smith       VecCopy( x, w );
3075e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3085e42470aSBarry Smith       VecAXPY(&clambda, y, w );
3095e42470aSBarry Smith #else
3105e42470aSBarry Smith       VecAXPY(&lambda, y, w );
3115e42470aSBarry Smith #endif
31278b31e54SBarry Smith       ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
3135e42470aSBarry Smith       VecNorm(g, gnorm );
3145e76c431SBarry Smith       if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
3155e42470aSBarry Smith          VecCopy(w, y );
3165e42470aSBarry Smith          PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda);
317761aaf1bSLois Curfman McInnes          *flag = -1; break;
3185e76c431SBarry Smith       }
3195e76c431SBarry Smith       count++;
3205e76c431SBarry Smith    }
321d93a2b8dSBarry Smith   theend:
3227857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3235e42470aSBarry Smith   return 0;
3245e76c431SBarry Smith }
32552392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
3265e42470aSBarry Smith /*@
3275e42470aSBarry Smith    SNESQuadraticLineSearch - This routine performs a cubic line search.
3285e76c431SBarry Smith 
3295e42470aSBarry Smith    Input Parameters:
330f59ffdedSLois Curfman McInnes .  snes - the SNES context
3315e42470aSBarry Smith .  x - current iterate
3325e42470aSBarry Smith .  f - residual evaluated at x
3335e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3345e42470aSBarry Smith .  w - work vector
3355e42470aSBarry Smith .  fnorm - 2-norm of f
3365e42470aSBarry Smith 
337c4a48953SLois Curfman McInnes    Output Parameters:
3385e42470aSBarry Smith .  g - residual evaluated at new iterate y
3395e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3405e42470aSBarry Smith .  gnorm - 2-norm of g
3415e42470aSBarry Smith .  ynorm - 2-norm of search length
342761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
3435e42470aSBarry Smith 
344c4a48953SLois Curfman McInnes    Options Database Key:
345c4a48953SLois Curfman McInnes $  -snes_line_search quadratic
346c4a48953SLois Curfman McInnes 
3475e42470aSBarry Smith    Notes:
348f59ffdedSLois Curfman McInnes    Use SNESSetLineSearchRoutine()
349f59ffdedSLois Curfman McInnes    to set this routine within the SNES_NLS method.
3505e42470aSBarry Smith 
351f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
352f59ffdedSLois Curfman McInnes 
353f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine()
3545e42470aSBarry Smith @*/
3555e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
356761aaf1bSLois Curfman McInnes                     double fnorm, double *ynorm, double *gnorm,int *flag)
3575e76c431SBarry Smith {
3585e42470aSBarry Smith   double  steptol, initslope;
3595e42470aSBarry Smith   double  lambdaprev, gnormprev;
3606b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
3615e42470aSBarry Smith   Scalar  cinitslope,clambda;
3626b5873e3SBarry Smith #endif
3635e42470aSBarry Smith   int     ierr,count;
3645e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
3655e42470aSBarry Smith   Scalar  one = 1.0,scale;
3665e42470aSBarry Smith   double  maxstep,minlambda,alpha,lambda,lambdatemp;
3675e76c431SBarry Smith 
3687857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
369761aaf1bSLois Curfman McInnes   *flag = 0;
3705e42470aSBarry Smith   alpha   = neP->alpha;
3715e42470aSBarry Smith   maxstep = neP->maxstep;
3725e42470aSBarry Smith   steptol = neP->steptol;
3735e76c431SBarry Smith 
3745e42470aSBarry Smith   VecNorm(y, ynorm );
3755e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
3765e42470aSBarry Smith     scale = maxstep/(*ynorm);
3775e42470aSBarry Smith     VecScale(&scale, y );
3785e42470aSBarry Smith     *ynorm = maxstep;
3795e76c431SBarry Smith   }
3805e42470aSBarry Smith   minlambda = steptol/(*ynorm);
3815e42470aSBarry Smith #if defined(PETSC_COMPLEX)
3825e42470aSBarry Smith   VecDot(f, y, &cinitslope );
3835e42470aSBarry Smith   initslope = real(cinitslope);
3845e42470aSBarry Smith #else
3855e42470aSBarry Smith   VecDot(f, y, &initslope );
3865e42470aSBarry Smith #endif
3875e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
3885e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
3895e42470aSBarry Smith 
3905e42470aSBarry Smith   VecCopy(y, w );
3915e42470aSBarry Smith   VecAXPY(&one, x, w );
39278b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
3935e42470aSBarry Smith   VecNorm(g, gnorm );
3945e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
3955e42470aSBarry Smith       VecCopy(w, y );
3965e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Using full step\n");
397d93a2b8dSBarry Smith       goto theend;
3985e42470aSBarry Smith   }
3995e42470aSBarry Smith 
4005e42470aSBarry Smith   /* Fit points with quadratic */
4015e42470aSBarry Smith   lambda = 1.0; count = 0;
4025e42470aSBarry Smith   count = 1;
4035e42470aSBarry Smith   while (1) {
4045e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
4055e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count);
4065e42470aSBarry Smith       PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n",
4075e42470aSBarry Smith                    fnorm,*gnorm, *ynorm,lambda);
4085e42470aSBarry Smith       VecCopy(w, y );
409761aaf1bSLois Curfman McInnes       *flag = -1; break;
4105e42470aSBarry Smith     }
4115e42470aSBarry Smith     lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope));
4125e42470aSBarry Smith     lambdaprev = lambda;
4135e42470aSBarry Smith     gnormprev = *gnorm;
4145e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4155e42470aSBarry Smith       lambda = .1*lambda;
4165e42470aSBarry Smith     } else lambda = lambdatemp;
4175e42470aSBarry Smith     VecCopy(x, w );
4185e42470aSBarry Smith #if defined(PETSC_COMPLEX)
4195e42470aSBarry Smith     clambda = lambda; VecAXPY(&clambda, y, w );
4205e42470aSBarry Smith #else
4215e42470aSBarry Smith     VecAXPY(&lambda, y, w );
4225e42470aSBarry Smith #endif
42378b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
4245e42470aSBarry Smith     VecNorm(g, gnorm );
4255e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
4265e42470aSBarry Smith       VecCopy(w, y );
4275e42470aSBarry Smith       PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda);
42806259719SBarry Smith       break;
4295e42470aSBarry Smith     }
4305e42470aSBarry Smith     count++;
4315e42470aSBarry Smith   }
432d93a2b8dSBarry Smith   theend:
4337857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4345e42470aSBarry Smith   return 0;
4355e76c431SBarry Smith }
4365e76c431SBarry Smith /* ------------------------------------------------------------ */
437b1f0a012SBarry Smith /*@
4385e42470aSBarry Smith    SNESSetLineSearchRoutine - Sets the line search routine to be used
439fbe28522SBarry Smith    by the method SNES_LS.
4405e76c431SBarry Smith 
4415e76c431SBarry Smith    Input Parameters:
442eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
4435e76c431SBarry Smith .  func - pointer to int function
4445e76c431SBarry Smith 
445c4a48953SLois Curfman McInnes    Available Routines:
446f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
447f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
448f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
4495e76c431SBarry Smith 
450c4a48953SLois Curfman McInnes     Options Database Keys:
451c4a48953SLois Curfman McInnes $   -snes_line_search [basic,quadratic,cubic]
452c4a48953SLois Curfman McInnes 
4535e76c431SBarry Smith    Calling sequence of func:
454f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
455761aaf1bSLois Curfman McInnes          Vec w, double fnorm, double *ynorm,
456761aaf1bSLois Curfman McInnes          double *gnorm, *flag)
4575e76c431SBarry Smith 
4585e76c431SBarry Smith     Input parameters for func:
4595e42470aSBarry Smith .   snes - nonlinear context
4605e76c431SBarry Smith .   x - current iterate
4615e76c431SBarry Smith .   f - residual evaluated at x
4625e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4635e76c431SBarry Smith .   w - work vector
4645e76c431SBarry Smith .   fnorm - 2-norm of f
4655e76c431SBarry Smith 
4665e76c431SBarry Smith     Output parameters for func:
4675e76c431SBarry Smith .   g - residual evaluated at new iterate y
4685e76c431SBarry Smith .   y - new iterate (contains search direction on input)
4695e76c431SBarry Smith .   gnorm - 2-norm of g
4705e76c431SBarry Smith .   ynorm - 2-norm of search length
471761aaf1bSLois Curfman McInnes .   flag - set to 0 if the line search succeeds; a nonzero integer
472761aaf1bSLois Curfman McInnes            on failure.
473f59ffdedSLois Curfman McInnes 
474f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
475f59ffdedSLois Curfman McInnes 
476f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
4775e76c431SBarry Smith @*/
4785e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
479761aaf1bSLois Curfman McInnes                              double,double *,double*,int*) )
4805e76c431SBarry Smith {
481fbe28522SBarry Smith   if ((snes)->type == SNES_NLS)
4825e42470aSBarry Smith     ((SNES_LS *)(snes->data))->LineSearch = func;
4835e42470aSBarry Smith   return 0;
4845e76c431SBarry Smith }
48552392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
4865e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes)
4875e42470aSBarry Smith {
4885e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
48952392280SLois Curfman McInnes   char    *p;
49052392280SLois Curfman McInnes   if (snes->prefix) p = snes->prefix; else p = "-";
49152392280SLois Curfman McInnes   MPIU_printf(snes->comm," method ls (system of nonlinear equations):\n");
49252392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search [basic,quadratic,cubic]\n",p);
49352392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_alpha alpha (default %g)\n",p,ls->alpha);
49452392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_maxstep max (default %g)\n",p,ls->maxstep);
49552392280SLois Curfman McInnes   MPIU_printf(snes->comm,"   %ssnes_line_search_steptol tol (default %g)\n",p,ls->steptol);
4966b5873e3SBarry Smith   return 0;
4975e42470aSBarry Smith }
49852392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
499a935fc98SLois Curfman McInnes static int SNESView_LS(PetscObject obj,Viewer viewer)
500a935fc98SLois Curfman McInnes {
501a935fc98SLois Curfman McInnes   SNES    snes = (SNES)obj;
502a935fc98SLois Curfman McInnes   SNES_LS *ls = (SNES_LS *)snes->data;
503a935fc98SLois Curfman McInnes   FILE    *fd = ViewerFileGetPointer_Private(viewer);
504a935fc98SLois Curfman McInnes   char    *cstring;
505a935fc98SLois Curfman McInnes 
506a935fc98SLois Curfman McInnes   if (ls->LineSearch == SNESNoLineSearch)
507a935fc98SLois Curfman McInnes     cstring = "SNESNoLineSearch";
508a935fc98SLois Curfman McInnes   else if (ls->LineSearch == SNESQuadraticLineSearch)
509a935fc98SLois Curfman McInnes     cstring = "SNESQuadraticLineSearch";
510a935fc98SLois Curfman McInnes   else if (ls->LineSearch == SNESCubicLineSearch)
511a935fc98SLois Curfman McInnes     cstring = "SNESCubicLineSearch";
512a935fc98SLois Curfman McInnes   else
513a935fc98SLois Curfman McInnes     cstring = "unknown";
514a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    line search variant: %s\n",cstring);
515a935fc98SLois Curfman McInnes   MPIU_fprintf(snes->comm,fd,"    alpha=%g, maxstep=%g, steptol=%g\n",
516a935fc98SLois Curfman McInnes      ls->alpha,ls->maxstep,ls->steptol);
517a935fc98SLois Curfman McInnes   return 0;
518a935fc98SLois Curfman McInnes }
51952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
5205e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes)
5215e42470aSBarry Smith {
5225e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5235e42470aSBarry Smith   char    ver[16];
5245e42470aSBarry Smith   double  tmp;
5255e42470aSBarry Smith 
526df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) {
5275e42470aSBarry Smith     ls->alpha = tmp;
5285e42470aSBarry Smith   }
529df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) {
5305e42470aSBarry Smith     ls->maxstep = tmp;
5315e42470aSBarry Smith   }
532df60cc22SBarry Smith   if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) {
5335e42470aSBarry Smith     ls->steptol = tmp;
5345e42470aSBarry Smith   }
535df60cc22SBarry Smith   if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) {
5365e42470aSBarry Smith     if (!strcmp(ver,"basic")) {
5375e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESNoLineSearch);
5385e42470aSBarry Smith     }
5395e42470aSBarry Smith     else if (!strcmp(ver,"quadratic")) {
5405e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch);
5415e42470aSBarry Smith     }
5425e42470aSBarry Smith     else if (!strcmp(ver,"cubic")) {
5435e42470aSBarry Smith       SNESSetLineSearchRoutine(snes,SNESCubicLineSearch);
5445e42470aSBarry Smith     }
5458c48e012SBarry Smith     else {SETERRQ(1,"SNESSetFromOptions_LS: Unknown line search");}
5465e42470aSBarry Smith   }
5475e42470aSBarry Smith   return 0;
5485e42470aSBarry Smith }
5495e42470aSBarry Smith /* ------------------------------------------------------------ */
5505e42470aSBarry Smith int SNESCreate_LS(SNES  snes )
5515e42470aSBarry Smith {
5525e42470aSBarry Smith   SNES_LS *neP;
5535e42470aSBarry Smith 
5541a3481a4SLois Curfman McInnes   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1,
5551a3481a4SLois Curfman McInnes     "SNESCreate_LS: Valid for SNES_NONLINEAR_EQUATIONS problems only");
55625c7317fSLois Curfman McInnes   snes->type		= SNES_EQ_NLS;
55749e3953dSBarry Smith   snes->setup		= SNESSetUp_LS;
55849e3953dSBarry Smith   snes->solve		= SNESSolve_LS;
5595e42470aSBarry Smith   snes->destroy		= SNESDestroy_LS;
560bbb6d6a8SBarry Smith   snes->converged	= SNESDefaultConverged;
56149e3953dSBarry Smith   snes->printhelp       = SNESPrintHelp_LS;
56249e3953dSBarry Smith   snes->setfromoptions  = SNESSetFromOptions_LS;
563a935fc98SLois Curfman McInnes   snes->view            = SNESView_LS;
5645e42470aSBarry Smith 
56578b31e54SBarry Smith   neP			= PETSCNEW(SNES_LS);   CHKPTRQ(neP);
5665e42470aSBarry Smith   snes->data    	= (void *) neP;
5675e42470aSBarry Smith   neP->alpha		= 1.e-4;
5685e42470aSBarry Smith   neP->maxstep		= 1.e8;
5695e42470aSBarry Smith   neP->steptol		= 1.e-12;
5705e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
5715e42470aSBarry Smith   return 0;
5725e42470aSBarry Smith }
5735e42470aSBarry Smith 
5745e42470aSBarry Smith 
5755e42470aSBarry Smith 
5765e42470aSBarry Smith 
577