xref: /petsc/src/snes/impls/ls/ls.c (revision 51979daa601406d8231c5b66e91c8c6dd8b11411)
15e76c431SBarry Smith #ifndef lint
2*51979daaSLois Curfman McInnes static char vcid[] = "$Id: ls.c,v 1.82 1997/01/20 22:11:40 bsmith Exp curfman $";
35e76c431SBarry Smith #endif
45e76c431SBarry Smith 
55e76c431SBarry Smith #include <math.h>
670f55243SBarry Smith #include "src/snes/impls/ls/ls.h"
7b16a3bb1SBarry Smith #include "pinclude/pviewer.h"
85e42470aSBarry Smith 
95e42470aSBarry Smith /*
105e42470aSBarry Smith      Implements a line search variant of Newton's Method
115e76c431SBarry Smith     for solving systems of nonlinear equations.
125e76c431SBarry Smith 
135e76c431SBarry Smith     Input parameters:
145e42470aSBarry Smith .   snes - nonlinear context obtained from SNESCreate()
155e76c431SBarry Smith 
165e42470aSBarry Smith     Output Parameters:
17a935fc98SLois Curfman McInnes .   outits  - Number of global iterations until termination.
185e76c431SBarry Smith 
195e76c431SBarry Smith     Notes:
205e76c431SBarry Smith     This implements essentially a truncated Newton method with a
215e76c431SBarry Smith     line search.  By default a cubic backtracking line search
225e76c431SBarry Smith     is employed, as described in the text "Numerical Methods for
235e76c431SBarry Smith     Unconstrained Optimization and Nonlinear Equations" by Dennis
24393d2d9aSLois Curfman McInnes     and Schnabel.
255e76c431SBarry Smith */
265e76c431SBarry Smith 
275615d1e5SSatish Balay #undef __FUNC__
285615d1e5SSatish Balay #define __FUNC__ "SNESSolve_EQ_LS"
29f63b844aSLois Curfman McInnes int SNESSolve_EQ_LS(SNES snes,int *outits)
305e76c431SBarry Smith {
315e42470aSBarry Smith   SNES_LS       *neP = (SNES_LS *) snes->data;
32761aaf1bSLois Curfman McInnes   int           maxits, i, history_len, ierr, lits, lsfail;
33112a2221SBarry Smith   MatStructure  flg = DIFFERENT_NONZERO_PATTERN;
346b5873e3SBarry Smith   double        fnorm, gnorm, xnorm, ynorm, *history;
355e42470aSBarry Smith   Vec           Y, X, F, G, W, TMP;
365e76c431SBarry Smith 
375e42470aSBarry Smith   history	= snes->conv_hist;	/* convergence history */
38*51979daaSLois Curfman McInnes   history_len	= snes->conv_hist_size;	/* convergence history length */
395e42470aSBarry Smith   maxits	= snes->max_its;	/* maximum number of iterations */
405e42470aSBarry Smith   X		= snes->vec_sol;	/* solution vector */
4139e2f89bSBarry Smith   F		= snes->vec_func;	/* residual vector */
425e42470aSBarry Smith   Y		= snes->work[0];	/* work vectors */
435e42470aSBarry Smith   G		= snes->work[1];
445e42470aSBarry Smith   W		= snes->work[2];
455e76c431SBarry Smith 
46cddf8d76SBarry Smith   ierr = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr);       /* xnorm = || X || */
4725ed9cd1SBarry Smith   snes->iter = 0;
485334005bSBarry Smith   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);  /*  F(X)      */
49cddf8d76SBarry Smith   ierr = VecNorm(F,NORM_2,&fnorm); CHKERRQ(ierr);	/* fnorm <- ||F||  */
505e42470aSBarry Smith   snes->norm = fnorm;
51*51979daaSLois Curfman McInnes   if (history) history[0] = fnorm;
5294a424c1SBarry Smith   SNESMonitor(snes,0,fnorm);
535e76c431SBarry Smith 
54d034289bSBarry Smith   /* set parameter for default relative tolerance convergence test */
55d034289bSBarry Smith   snes->ttol = fnorm*snes->rtol;
56d034289bSBarry Smith 
575e76c431SBarry Smith   for ( i=0; i<maxits; i++ ) {
585e42470aSBarry Smith     snes->iter = i+1;
595e76c431SBarry Smith 
60ea4d3ed3SLois Curfman McInnes     /* Solve J Y = F, where J is Jacobian matrix */
615334005bSBarry Smith     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg); CHKERRQ(ierr);
625334005bSBarry Smith     ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg); CHKERRQ(ierr);
6378b31e54SBarry Smith     ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr);
647a00f4a9SLois Curfman McInnes     snes->linear_its += PetscAbsInt(lits);
65af1ccdebSLois Curfman McInnes     PLogInfo(snes,"SNESSolve_EQ_LS: iter=%d, linear solve iterations=%d\n",snes->iter,lits);
66ea4d3ed3SLois Curfman McInnes 
67ea4d3ed3SLois Curfman McInnes     /* Compute a (scaled) negative update in the line search routine:
68ea4d3ed3SLois Curfman McInnes          Y <- X - lambda*Y
69ea4d3ed3SLois Curfman McInnes        and evaluate G(Y) = function(Y))
70ea4d3ed3SLois Curfman McInnes     */
7181b6cf68SLois Curfman McInnes     ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr);
72ddd12767SBarry Smith     ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail); CHKERRQ(ierr);
73af1ccdebSLois Curfman McInnes     PLogInfo(snes,"SNESSolve_EQ_LS: fnorm=%g, gnorm=%g, ynorm=%g, lsfail=%d\n",fnorm,gnorm,ynorm,lsfail);
74761aaf1bSLois Curfman McInnes     if (lsfail) snes->nfailures++;
755e76c431SBarry Smith 
7639e2f89bSBarry Smith     TMP = F; F = G; snes->vec_func_always = F; G = TMP;
7739e2f89bSBarry Smith     TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
785e76c431SBarry Smith     fnorm = gnorm;
795e76c431SBarry Smith 
805e42470aSBarry Smith     snes->norm = fnorm;
815e76c431SBarry Smith     if (history && history_len > i+1) history[i+1] = fnorm;
82cddf8d76SBarry Smith     ierr = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr);	/* xnorm = || X || */
8394a424c1SBarry Smith     SNESMonitor(snes,i+1,fnorm);
845e76c431SBarry Smith 
855e76c431SBarry Smith     /* Test for convergence */
86bbb6d6a8SBarry Smith     if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
8716c95cacSBarry Smith       break;
8816c95cacSBarry Smith     }
8916c95cacSBarry Smith   }
9039e2f89bSBarry Smith   if (X != snes->vec_sol) {
91393d2d9aSLois Curfman McInnes     ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
9239e2f89bSBarry Smith     snes->vec_sol_always  = snes->vec_sol;
9339e2f89bSBarry Smith     snes->vec_func_always = snes->vec_func;
9439e2f89bSBarry Smith   }
9552392280SLois Curfman McInnes   if (i == maxits) {
9694a424c1SBarry Smith     PLogInfo(snes,
97f63b844aSLois Curfman McInnes       "SNESSolve_EQ_LS: Maximum number of iterations has been reached: %d\n",maxits);
9852392280SLois Curfman McInnes     i--;
9952392280SLois Curfman McInnes   }
100*51979daaSLois Curfman McInnes   if (history) snes->conv_act_size = (history_len < i+1) ? history_len : i+1;
1015e42470aSBarry Smith   *outits = i+1;
1025e42470aSBarry Smith   return 0;
1035e76c431SBarry Smith }
1045e76c431SBarry Smith /* ------------------------------------------------------------ */
1055615d1e5SSatish Balay #undef __FUNC__
1065615d1e5SSatish Balay #define __FUNC__ "SNESSetUp_EQ_LS"
107f63b844aSLois Curfman McInnes int SNESSetUp_EQ_LS(SNES snes )
1085e76c431SBarry Smith {
1095e42470aSBarry Smith   int ierr;
11081b6cf68SLois Curfman McInnes   snes->nwork = 4;
111d7e8b826SBarry Smith   ierr = VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work);CHKERRQ(ierr);
1125e42470aSBarry Smith   PLogObjectParents(snes,snes->nwork,snes->work);
11381b6cf68SLois Curfman McInnes   snes->vec_sol_update_always = snes->work[3];
1145e42470aSBarry Smith   return 0;
1155e76c431SBarry Smith }
1165e76c431SBarry Smith /* ------------------------------------------------------------ */
1175615d1e5SSatish Balay #undef __FUNC__
1185615d1e5SSatish Balay #define __FUNC__ "SNESDestroy_EQ_LS"
119f63b844aSLois Curfman McInnes int SNESDestroy_EQ_LS(PetscObject obj)
1205e76c431SBarry Smith {
1215e42470aSBarry Smith   SNES snes = (SNES) obj;
122393d2d9aSLois Curfman McInnes   int  ierr;
1235baf8537SBarry Smith   if (snes->nwork) {
1244b0e389bSBarry Smith     ierr = VecDestroyVecs(snes->work,snes->nwork); CHKERRQ(ierr);
12521c89e3eSBarry Smith   }
1260452661fSBarry Smith   PetscFree(snes->data);
1275e42470aSBarry Smith   return 0;
1285e76c431SBarry Smith }
1295e76c431SBarry Smith /* ------------------------------------------------------------ */
1305615d1e5SSatish Balay #undef __FUNC__
1315615d1e5SSatish Balay #define __FUNC__ "SNESNoLineSearch"
1325e76c431SBarry Smith /*ARGSUSED*/
1334b828684SBarry Smith /*@C
1345e42470aSBarry Smith    SNESNoLineSearch - This routine is not a line search at all;
1355e76c431SBarry Smith    it simply uses the full Newton step.  Thus, this routine is intended
1365e76c431SBarry Smith    to serve as a template and is not recommended for general use.
1375e76c431SBarry Smith 
1385e76c431SBarry Smith    Input Parameters:
1395e42470aSBarry Smith .  snes - nonlinear context
1405e76c431SBarry Smith .  x - current iterate
1415e76c431SBarry Smith .  f - residual evaluated at x
1425e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1435e76c431SBarry Smith .  w - work vector
1445e76c431SBarry Smith .  fnorm - 2-norm of f
1455e76c431SBarry Smith 
146c4a48953SLois Curfman McInnes    Output Parameters:
1475e76c431SBarry Smith .  g - residual evaluated at new iterate y
1485e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1495e76c431SBarry Smith .  gnorm - 2-norm of g
1505e76c431SBarry Smith .  ynorm - 2-norm of search length
151761aaf1bSLois Curfman McInnes .  flag - set to 0, indicating a successful line search
1525e76c431SBarry Smith 
153c4a48953SLois Curfman McInnes    Options Database Key:
15409d61ba7SLois Curfman McInnes $  -snes_eq_ls basic
155c4a48953SLois Curfman McInnes 
15628ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
15728ae5a21SLois Curfman McInnes 
158f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(),
15977c4ece6SBarry Smith           SNESSetLineSearch()
1605e76c431SBarry Smith @*/
1615e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
162761aaf1bSLois Curfman McInnes                      double fnorm, double *ynorm, double *gnorm,int *flag )
1635e76c431SBarry Smith {
1645e42470aSBarry Smith   int    ierr;
1655334005bSBarry Smith   Scalar mone = -1.0;
1665334005bSBarry Smith 
167761aaf1bSLois Curfman McInnes   *flag = 0;
1687857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
169cddf8d76SBarry Smith   ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr);       /* ynorm = || y || */
170ea4d3ed3SLois Curfman McInnes   ierr = VecAYPX(&mone,x,y); CHKERRQ(ierr);            /* y <- y - x      */
171ea4d3ed3SLois Curfman McInnes   ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); /* Compute F(y)    */
172cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);       /* gnorm = || g || */
1737857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
174761aaf1bSLois Curfman McInnes   return 0;
1755e76c431SBarry Smith }
1765e76c431SBarry Smith /* ------------------------------------------------------------------ */
1775615d1e5SSatish Balay #undef __FUNC__
1785615d1e5SSatish Balay #define __FUNC__ "SNESCubicLineSearch"
1794b828684SBarry Smith /*@C
180f525115eSLois Curfman McInnes    SNESCubicLineSearch - Performs a cubic line search (default line search method).
1815e76c431SBarry Smith 
1825e76c431SBarry Smith    Input Parameters:
1835e42470aSBarry Smith .  snes - nonlinear context
1845e76c431SBarry Smith .  x - current iterate
1855e76c431SBarry Smith .  f - residual evaluated at x
1865e76c431SBarry Smith .  y - search direction (contains new iterate on output)
1875e76c431SBarry Smith .  w - work vector
1885e76c431SBarry Smith .  fnorm - 2-norm of f
1895e76c431SBarry Smith 
190393d2d9aSLois Curfman McInnes    Output Parameters:
1915e76c431SBarry Smith .  g - residual evaluated at new iterate y
1925e76c431SBarry Smith .  y - new iterate (contains search direction on input)
1935e76c431SBarry Smith .  gnorm - 2-norm of g
1945e76c431SBarry Smith .  ynorm - 2-norm of search length
195761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
1965e76c431SBarry Smith 
197c4a48953SLois Curfman McInnes    Options Database Key:
19809d61ba7SLois Curfman McInnes $  -snes_eq_ls cubic
199c4a48953SLois Curfman McInnes 
2005e76c431SBarry Smith    Notes:
2015e76c431SBarry Smith    This line search is taken from "Numerical Methods for Unconstrained
2025e76c431SBarry Smith    Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
2035e76c431SBarry Smith 
20428ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic
20528ae5a21SLois Curfman McInnes 
20677c4ece6SBarry Smith .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearch()
2075e76c431SBarry Smith @*/
2085e42470aSBarry Smith int SNESCubicLineSearch(SNES snes,Vec x,Vec f,Vec g,Vec y,Vec w,
209761aaf1bSLois Curfman McInnes                         double fnorm,double *ynorm,double *gnorm,int *flag)
2105e76c431SBarry Smith {
211ddd12767SBarry Smith   double  steptol, initslope, lambdaprev, gnormprev, a, b, d, t1, t2;
212ea4d3ed3SLois Curfman McInnes   double  maxstep, minlambda, alpha, lambda, lambdatemp, lambdaneg;
2136b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
2145e42470aSBarry Smith   Scalar  cinitslope, clambda;
2156b5873e3SBarry Smith #endif
2165e42470aSBarry Smith   int     ierr, count;
2175e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
2185334005bSBarry Smith   Scalar  mone = -1.0,scale;
2195e76c431SBarry Smith 
2207857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
221761aaf1bSLois Curfman McInnes   *flag   = 0;
2225e76c431SBarry Smith   alpha   = neP->alpha;
2235e76c431SBarry Smith   maxstep = neP->maxstep;
2245e76c431SBarry Smith   steptol = neP->steptol;
2255e76c431SBarry Smith 
226cddf8d76SBarry Smith   ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr);
2275e76c431SBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
2285e42470aSBarry Smith     scale = maxstep/(*ynorm);
2296b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
23094a424c1SBarry Smith     PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",real(scale));
2316b5873e3SBarry Smith #else
23294a424c1SBarry Smith     PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",scale);
2336b5873e3SBarry Smith #endif
234393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
2355e76c431SBarry Smith     *ynorm = maxstep;
2365e76c431SBarry Smith   }
2375e76c431SBarry Smith   minlambda = steptol/(*ynorm);
238a703fe33SLois Curfman McInnes   ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr);
2395e42470aSBarry Smith #if defined(PETSC_COMPLEX)
240a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr);
2415e42470aSBarry Smith   initslope = real(cinitslope);
2425e42470aSBarry Smith #else
243a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&initslope); CHKERRQ(ierr);
2445e42470aSBarry Smith #endif
2455e76c431SBarry Smith   if (initslope > 0.0) initslope = -initslope;
2465e76c431SBarry Smith   if (initslope == 0.0) initslope = -1.0;
2475e76c431SBarry Smith 
248393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
2495334005bSBarry Smith   ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr);
25078b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
251cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm);
2525e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
253393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
25494a424c1SBarry Smith     PLogInfo(snes,"SNESCubicLineSearch: Using full step\n");
255d93a2b8dSBarry Smith     goto theend;
2565e76c431SBarry Smith   }
2575e76c431SBarry Smith 
2585e76c431SBarry Smith   /* Fit points with quadratic */
2595e76c431SBarry Smith   lambda = 1.0; count = 0;
260a703fe33SLois Curfman McInnes   lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
2615e76c431SBarry Smith   lambdaprev = lambda;
2625e76c431SBarry Smith   gnormprev = *gnorm;
263ddd12767SBarry Smith   if (lambdatemp <= .1*lambda) lambda = .1*lambda;
264ddd12767SBarry Smith   else lambda = lambdatemp;
265393d2d9aSLois Curfman McInnes   ierr   = VecCopy(x,w); CHKERRQ(ierr);
266ea4d3ed3SLois Curfman McInnes   lambdaneg = -lambda;
2675e42470aSBarry Smith #if defined(PETSC_COMPLEX)
268ea4d3ed3SLois Curfman McInnes   clambda = lambdaneg; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
2695e42470aSBarry Smith #else
270ea4d3ed3SLois Curfman McInnes   ierr = VecAXPY(&lambdaneg,y,w); CHKERRQ(ierr);
2715e42470aSBarry Smith #endif
27278b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
273cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
2745e76c431SBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
275393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
276ea4d3ed3SLois Curfman McInnes     PLogInfo(snes,"SNESCubicLineSearch: Quadratically determined step, lambda=%g\n",lambda);
277d93a2b8dSBarry Smith     goto theend;
2785e76c431SBarry Smith   }
2795e76c431SBarry Smith 
2805e76c431SBarry Smith   /* Fit points with cubic */
2815e76c431SBarry Smith   count = 1;
2825e76c431SBarry Smith   while (1) {
2835e76c431SBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
28494a424c1SBarry Smith       PLogInfo(snes,
2854b828684SBarry Smith          "SNESCubicLineSearch:Unable to find good step length! %d \n",count);
28694a424c1SBarry Smith       PLogInfo(snes,
287ea4d3ed3SLois Curfman McInnes          "SNESCubicLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n",
288ea4d3ed3SLois Curfman McInnes              fnorm,*gnorm,*ynorm,lambda,initslope);
289393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
290761aaf1bSLois Curfman McInnes       *flag = -1; break;
2915e76c431SBarry Smith     }
2925e76c431SBarry Smith     t1 = *gnorm - fnorm - lambda*initslope;
2935e76c431SBarry Smith     t2 = gnormprev  - fnorm - lambdaprev*initslope;
294ddd12767SBarry Smith     a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2955e76c431SBarry Smith     b = (-lambdaprev*t1/(lambda*lambda) +
2965e76c431SBarry Smith                              lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2975e76c431SBarry Smith     d = b*b - 3*a*initslope;
2985e76c431SBarry Smith     if (d < 0.0) d = 0.0;
2995e76c431SBarry Smith     if (a == 0.0) {
3005e76c431SBarry Smith       lambdatemp = -initslope/(2.0*b);
3015e76c431SBarry Smith     } else {
3025e76c431SBarry Smith       lambdatemp = (-b + sqrt(d))/(3.0*a);
3035e76c431SBarry Smith     }
3045e76c431SBarry Smith     if (lambdatemp > .5*lambda) {
3055e76c431SBarry Smith       lambdatemp = .5*lambda;
3065e76c431SBarry Smith     }
3075e76c431SBarry Smith     lambdaprev = lambda;
3085e76c431SBarry Smith     gnormprev = *gnorm;
3095e76c431SBarry Smith     if (lambdatemp <= .1*lambda) {
3105e76c431SBarry Smith       lambda = .1*lambda;
3115e76c431SBarry Smith     }
3125e76c431SBarry Smith     else lambda = lambdatemp;
313393d2d9aSLois Curfman McInnes     ierr = VecCopy( x, w ); CHKERRQ(ierr);
314ea4d3ed3SLois Curfman McInnes     lambdaneg = -lambda;
3155e42470aSBarry Smith #if defined(PETSC_COMPLEX)
316ea4d3ed3SLois Curfman McInnes     clambda = lambdaneg;
317393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
3185e42470aSBarry Smith #else
319ea4d3ed3SLois Curfman McInnes     ierr = VecAXPY(&lambdaneg,y,w); CHKERRQ(ierr);
3205e42470aSBarry Smith #endif
32178b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
322cddf8d76SBarry Smith     ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
3235e76c431SBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* is reduction enough */
324393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
325ea4d3ed3SLois Curfman McInnes       PLogInfo(snes,"SNESCubicLineSearch: Cubically determined step, lambda=%g\n",lambda);
326761aaf1bSLois Curfman McInnes       *flag = -1; break;
3275e76c431SBarry Smith     }
3285e76c431SBarry Smith     count++;
3295e76c431SBarry Smith   }
330d93a2b8dSBarry Smith   theend:
3317857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
3325e42470aSBarry Smith   return 0;
3335e76c431SBarry Smith }
33452392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
3355615d1e5SSatish Balay #undef __FUNC__
3365615d1e5SSatish Balay #define __FUNC__ "SNESQuadraticLineSearch"
3374b828684SBarry Smith /*@C
338f525115eSLois Curfman McInnes    SNESQuadraticLineSearch - Performs a quadratic line search.
3395e76c431SBarry Smith 
3405e42470aSBarry Smith    Input Parameters:
341f59ffdedSLois Curfman McInnes .  snes - the SNES context
3425e42470aSBarry Smith .  x - current iterate
3435e42470aSBarry Smith .  f - residual evaluated at x
3445e42470aSBarry Smith .  y - search direction (contains new iterate on output)
3455e42470aSBarry Smith .  w - work vector
3465e42470aSBarry Smith .  fnorm - 2-norm of f
3475e42470aSBarry Smith 
348c4a48953SLois Curfman McInnes    Output Parameters:
3495e42470aSBarry Smith .  g - residual evaluated at new iterate y
3505e42470aSBarry Smith .  y - new iterate (contains search direction on input)
3515e42470aSBarry Smith .  gnorm - 2-norm of g
3525e42470aSBarry Smith .  ynorm - 2-norm of search length
353761aaf1bSLois Curfman McInnes .  flag - 0 if line search succeeds; -1 on failure.
3545e42470aSBarry Smith 
355c4a48953SLois Curfman McInnes    Options Database Key:
35609d61ba7SLois Curfman McInnes $  -snes_eq_ls quadratic
357c4a48953SLois Curfman McInnes 
3585e42470aSBarry Smith    Notes:
35977c4ece6SBarry Smith    Use SNESSetLineSearch()
360f63b844aSLois Curfman McInnes    to set this routine within the SNES_EQ_LS method.
3615e42470aSBarry Smith 
362f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search
363f59ffdedSLois Curfman McInnes 
36477c4ece6SBarry Smith .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearch()
3655e42470aSBarry Smith @*/
3665e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w,
367761aaf1bSLois Curfman McInnes                            double fnorm, double *ynorm, double *gnorm,int *flag)
3685e76c431SBarry Smith {
369ddd12767SBarry Smith   double  steptol,initslope,lambdaprev,gnormprev,maxstep,minlambda,alpha,lambda,lambdatemp;
3706b5873e3SBarry Smith #if defined(PETSC_COMPLEX)
3715e42470aSBarry Smith   Scalar  cinitslope,clambda;
3726b5873e3SBarry Smith #endif
3735e42470aSBarry Smith   int     ierr,count;
3745e42470aSBarry Smith   SNES_LS *neP = (SNES_LS *) snes->data;
3755334005bSBarry Smith   Scalar  mone = -1.0,scale;
3765e76c431SBarry Smith 
3777857610eSBarry Smith   PLogEventBegin(SNES_LineSearch,snes,x,f,g);
378761aaf1bSLois Curfman McInnes   *flag = 0;
3795e42470aSBarry Smith   alpha   = neP->alpha;
3805e42470aSBarry Smith   maxstep = neP->maxstep;
3815e42470aSBarry Smith   steptol = neP->steptol;
3825e76c431SBarry Smith 
383cddf8d76SBarry Smith   VecNorm(y, NORM_2,ynorm );
3845e42470aSBarry Smith   if (*ynorm > maxstep) {	/* Step too big, so scale back */
3855e42470aSBarry Smith     scale = maxstep/(*ynorm);
386393d2d9aSLois Curfman McInnes     ierr = VecScale(&scale,y); CHKERRQ(ierr);
3875e42470aSBarry Smith     *ynorm = maxstep;
3885e76c431SBarry Smith   }
3895e42470aSBarry Smith   minlambda = steptol/(*ynorm);
390a703fe33SLois Curfman McInnes   ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr);
3915e42470aSBarry Smith #if defined(PETSC_COMPLEX)
392a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr);
3935e42470aSBarry Smith   initslope = real(cinitslope);
3945e42470aSBarry Smith #else
395a703fe33SLois Curfman McInnes   ierr = VecDot(f,w,&initslope); CHKERRQ(ierr);
3965e42470aSBarry Smith #endif
3975e42470aSBarry Smith   if (initslope > 0.0) initslope = -initslope;
3985e42470aSBarry Smith   if (initslope == 0.0) initslope = -1.0;
3995e42470aSBarry Smith 
400393d2d9aSLois Curfman McInnes   ierr = VecCopy(y,w); CHKERRQ(ierr);
4015334005bSBarry Smith   ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr);
40278b31e54SBarry Smith   ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
403cddf8d76SBarry Smith   ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
4045e42470aSBarry Smith   if (*gnorm <= fnorm + alpha*initslope) {	/* Sufficient reduction */
405393d2d9aSLois Curfman McInnes     ierr = VecCopy(w,y); CHKERRQ(ierr);
40694a424c1SBarry Smith     PLogInfo(snes,"SNESQuadraticLineSearch: Using full step\n");
407d93a2b8dSBarry Smith     goto theend;
4085e42470aSBarry Smith   }
4095e42470aSBarry Smith 
4105e42470aSBarry Smith   /* Fit points with quadratic */
4115e42470aSBarry Smith   lambda = 1.0; count = 0;
4125e42470aSBarry Smith   count = 1;
4135e42470aSBarry Smith   while (1) {
4145e42470aSBarry Smith     if (lambda <= minlambda) { /* bad luck; use full step */
41594a424c1SBarry Smith       PLogInfo(snes,
4164b828684SBarry Smith           "SNESQuadraticLineSearch:Unable to find good step length! %d \n",count);
41794a424c1SBarry Smith       PLogInfo(snes,
418ea4d3ed3SLois Curfman McInnes       "SNESQuadraticLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n",
419ea4d3ed3SLois Curfman McInnes           fnorm,*gnorm,*ynorm,lambda,initslope);
420393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
421761aaf1bSLois Curfman McInnes       *flag = -1; break;
4225e42470aSBarry Smith     }
423a703fe33SLois Curfman McInnes     lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
4245e42470aSBarry Smith     lambdaprev = lambda;
4255e42470aSBarry Smith     gnormprev = *gnorm;
4265e42470aSBarry Smith     if (lambdatemp <= .1*lambda) {
4275e42470aSBarry Smith       lambda = .1*lambda;
4285e42470aSBarry Smith     } else lambda = lambdatemp;
429393d2d9aSLois Curfman McInnes     ierr = VecCopy(x,w); CHKERRQ(ierr);
4305334005bSBarry Smith     lambda = -lambda;
4315e42470aSBarry Smith #if defined(PETSC_COMPLEX)
432393d2d9aSLois Curfman McInnes     clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr);
4335e42470aSBarry Smith #else
434393d2d9aSLois Curfman McInnes     ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr);
4355e42470aSBarry Smith #endif
43678b31e54SBarry Smith     ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr);
437cddf8d76SBarry Smith     ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr);
4385e42470aSBarry Smith     if (*gnorm <= fnorm + alpha*initslope) {      /* sufficient reduction */
439393d2d9aSLois Curfman McInnes       ierr = VecCopy(w,y); CHKERRQ(ierr);
44094a424c1SBarry Smith       PLogInfo(snes,
441ea4d3ed3SLois Curfman McInnes         "SNESQuadraticLineSearch:Quadratically determined step, lambda=%g\n",lambda);
44206259719SBarry Smith       break;
4435e42470aSBarry Smith     }
4445e42470aSBarry Smith     count++;
4455e42470aSBarry Smith   }
446d93a2b8dSBarry Smith   theend:
4477857610eSBarry Smith   PLogEventEnd(SNES_LineSearch,snes,x,f,g);
4485e42470aSBarry Smith   return 0;
4495e76c431SBarry Smith }
4505e76c431SBarry Smith /* ------------------------------------------------------------ */
4515615d1e5SSatish Balay #undef __FUNC__
4525615d1e5SSatish Balay #define __FUNC__ "SNESSetLineSearch"
453c9e6a524SLois Curfman McInnes /*@C
45477c4ece6SBarry Smith    SNESSetLineSearch - Sets the line search routine to be used
455f63b844aSLois Curfman McInnes    by the method SNES_EQ_LS.
4565e76c431SBarry Smith 
4575e76c431SBarry Smith    Input Parameters:
458eafb4bcbSBarry Smith .  snes - nonlinear context obtained from SNESCreate()
4595e76c431SBarry Smith .  func - pointer to int function
4605e76c431SBarry Smith 
461c4a48953SLois Curfman McInnes    Available Routines:
462f59ffdedSLois Curfman McInnes .  SNESCubicLineSearch() - default line search
463f59ffdedSLois Curfman McInnes .  SNESQuadraticLineSearch() - quadratic line search
464f59ffdedSLois Curfman McInnes .  SNESNoLineSearch() - the full Newton step (actually not a line search)
4655e76c431SBarry Smith 
466c4a48953SLois Curfman McInnes     Options Database Keys:
46709d61ba7SLois Curfman McInnes $   -snes_eq_ls [basic,quadratic,cubic]
468c4a48953SLois Curfman McInnes 
4695e76c431SBarry Smith    Calling sequence of func:
470f59ffdedSLois Curfman McInnes    func (SNES snes, Vec x, Vec f, Vec g, Vec y,
471761aaf1bSLois Curfman McInnes          Vec w, double fnorm, double *ynorm,
472761aaf1bSLois Curfman McInnes          double *gnorm, *flag)
4735e76c431SBarry Smith 
4745e76c431SBarry Smith     Input parameters for func:
4755e42470aSBarry Smith .   snes - nonlinear context
4765e76c431SBarry Smith .   x - current iterate
4775e76c431SBarry Smith .   f - residual evaluated at x
4785e76c431SBarry Smith .   y - search direction (contains new iterate on output)
4795e76c431SBarry Smith .   w - work vector
4805e76c431SBarry Smith .   fnorm - 2-norm of f
4815e76c431SBarry Smith 
4825e76c431SBarry Smith     Output parameters for func:
4835e76c431SBarry Smith .   g - residual evaluated at new iterate y
4845e76c431SBarry Smith .   y - new iterate (contains search direction on input)
4855e76c431SBarry Smith .   gnorm - 2-norm of g
4865e76c431SBarry Smith .   ynorm - 2-norm of search length
487761aaf1bSLois Curfman McInnes .   flag - set to 0 if the line search succeeds; a nonzero integer
488761aaf1bSLois Curfman McInnes            on failure.
489f59ffdedSLois Curfman McInnes 
490f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine
491f59ffdedSLois Curfman McInnes 
492f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch()
4935e76c431SBarry Smith @*/
49477c4ece6SBarry Smith int SNESSetLineSearch(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec,
495761aaf1bSLois Curfman McInnes                              double,double*,double*,int*))
4965e76c431SBarry Smith {
497f63b844aSLois Curfman McInnes   if ((snes)->type == SNES_EQ_LS) ((SNES_LS *)(snes->data))->LineSearch = func;
4985e42470aSBarry Smith   return 0;
4995e76c431SBarry Smith }
50052392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
5015615d1e5SSatish Balay #undef __FUNC__
5025615d1e5SSatish Balay #define __FUNC__ "SNESPrintHelp_EQ_LS"
503f63b844aSLois Curfman McInnes static int SNESPrintHelp_EQ_LS(SNES snes,char *p)
5045e42470aSBarry Smith {
5055e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5066daaf66cSBarry Smith 
507f63b844aSLois Curfman McInnes   PetscPrintf(snes->comm," method SNES_EQ_LS (ls) for systems of nonlinear equations:\n");
50809d61ba7SLois Curfman McInnes   PetscPrintf(snes->comm,"   %ssnes_eq_ls [basic,quadratic,cubic]\n",p);
50909d61ba7SLois Curfman McInnes   PetscPrintf(snes->comm,"   %ssnes_eq_ls_alpha <alpha> (default %g)\n",p,ls->alpha);
51009d61ba7SLois Curfman McInnes   PetscPrintf(snes->comm,"   %ssnes_eq_ls_maxstep <max> (default %g)\n",p,ls->maxstep);
51109d61ba7SLois Curfman McInnes   PetscPrintf(snes->comm,"   %ssnes_eq_ls_steptol <tol> (default %g)\n",p,ls->steptol);
5126b5873e3SBarry Smith   return 0;
5135e42470aSBarry Smith }
51452392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
5155615d1e5SSatish Balay #undef __FUNC__
5165615d1e5SSatish Balay #define __FUNC__ "SNESView_EQ_LS"
517f63b844aSLois Curfman McInnes static int SNESView_EQ_LS(PetscObject obj,Viewer viewer)
518a935fc98SLois Curfman McInnes {
519a935fc98SLois Curfman McInnes   SNES       snes = (SNES)obj;
520a935fc98SLois Curfman McInnes   SNES_LS    *ls = (SNES_LS *)snes->data;
521d636dbe3SBarry Smith   FILE       *fd;
52219bcc07fSBarry Smith   char       *cstr;
52351695f54SLois Curfman McInnes   int        ierr;
52419bcc07fSBarry Smith   ViewerType vtype;
525a935fc98SLois Curfman McInnes 
52619bcc07fSBarry Smith   ierr = ViewerGetType(viewer,&vtype); CHKERRQ(ierr);
52719bcc07fSBarry Smith   if (vtype  == ASCII_FILE_VIEWER || vtype == ASCII_FILES_VIEWER) {
52890ace30eSBarry Smith     ierr = ViewerASCIIGetPointer(viewer,&fd); CHKERRQ(ierr);
52919bcc07fSBarry Smith     if (ls->LineSearch == SNESNoLineSearch) cstr = "SNESNoLineSearch";
53019bcc07fSBarry Smith     else if (ls->LineSearch == SNESQuadraticLineSearch) cstr = "SNESQuadraticLineSearch";
53119bcc07fSBarry Smith     else if (ls->LineSearch == SNESCubicLineSearch) cstr = "SNESCubicLineSearch";
53219bcc07fSBarry Smith     else cstr = "unknown";
53377c4ece6SBarry Smith     PetscFPrintf(snes->comm,fd,"    line search variant: %s\n",cstr);
53477c4ece6SBarry Smith     PetscFPrintf(snes->comm,fd,"    alpha=%g, maxstep=%g, steptol=%g\n",
535a935fc98SLois Curfman McInnes                  ls->alpha,ls->maxstep,ls->steptol);
53619bcc07fSBarry Smith   }
537a935fc98SLois Curfman McInnes   return 0;
538a935fc98SLois Curfman McInnes }
53952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */
5405615d1e5SSatish Balay #undef __FUNC__
5415615d1e5SSatish Balay #define __FUNC__ "SNESSetFromOptions_EQ_LS"
542f63b844aSLois Curfman McInnes static int SNESSetFromOptions_EQ_LS(SNES snes)
5435e42470aSBarry Smith {
5445e42470aSBarry Smith   SNES_LS *ls = (SNES_LS *)snes->data;
5455e42470aSBarry Smith   char    ver[16];
5465e42470aSBarry Smith   double  tmp;
54725cdf11fSBarry Smith   int     ierr,flg;
5485e42470aSBarry Smith 
54909d61ba7SLois Curfman McInnes   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_alpha",&tmp, &flg);CHKERRQ(ierr);
55025cdf11fSBarry Smith   if (flg) {
5515e42470aSBarry Smith     ls->alpha = tmp;
5525e42470aSBarry Smith   }
55309d61ba7SLois Curfman McInnes   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_maxstep",&tmp, &flg);CHKERRQ(ierr);
55425cdf11fSBarry Smith   if (flg) {
5555e42470aSBarry Smith     ls->maxstep = tmp;
5565e42470aSBarry Smith   }
55709d61ba7SLois Curfman McInnes   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_steptol",&tmp, &flg);CHKERRQ(ierr);
55825cdf11fSBarry Smith   if (flg) {
5595e42470aSBarry Smith     ls->steptol = tmp;
5605e42470aSBarry Smith   }
56109d61ba7SLois Curfman McInnes   ierr = OptionsGetString(snes->prefix,"-snes_eq_ls",ver,16, &flg); CHKERRQ(ierr);
56225cdf11fSBarry Smith   if (flg) {
56348d91487SBarry Smith     if (!PetscStrcmp(ver,"basic")) {
56477c4ece6SBarry Smith       SNESSetLineSearch(snes,SNESNoLineSearch);
5655e42470aSBarry Smith     }
56648d91487SBarry Smith     else if (!PetscStrcmp(ver,"quadratic")) {
56777c4ece6SBarry Smith       SNESSetLineSearch(snes,SNESQuadraticLineSearch);
5685e42470aSBarry Smith     }
56948d91487SBarry Smith     else if (!PetscStrcmp(ver,"cubic")) {
57077c4ece6SBarry Smith       SNESSetLineSearch(snes,SNESCubicLineSearch);
5715e42470aSBarry Smith     }
572e3372554SBarry Smith     else {SETERRQ(1,0,"Unknown line search");}
5735e42470aSBarry Smith   }
5745e42470aSBarry Smith   return 0;
5755e42470aSBarry Smith }
5765e42470aSBarry Smith /* ------------------------------------------------------------ */
5775615d1e5SSatish Balay #undef __FUNC__
5785615d1e5SSatish Balay #define __FUNC__ "SNESCreate_EQ_LS"
579f63b844aSLois Curfman McInnes int SNESCreate_EQ_LS(SNES  snes )
5805e42470aSBarry Smith {
5815e42470aSBarry Smith   SNES_LS *neP;
5825e42470aSBarry Smith 
583ddd12767SBarry Smith   if (snes->method_class != SNES_NONLINEAR_EQUATIONS)
584e3372554SBarry Smith     SETERRQ(1,0,"For SNES_NONLINEAR_EQUATIONS only");
585f63b844aSLois Curfman McInnes   snes->type		= SNES_EQ_LS;
586f63b844aSLois Curfman McInnes   snes->setup		= SNESSetUp_EQ_LS;
587f63b844aSLois Curfman McInnes   snes->solve		= SNESSolve_EQ_LS;
588f63b844aSLois Curfman McInnes   snes->destroy		= SNESDestroy_EQ_LS;
589f63b844aSLois Curfman McInnes   snes->converged	= SNESConverged_EQ_LS;
590f63b844aSLois Curfman McInnes   snes->printhelp       = SNESPrintHelp_EQ_LS;
591f63b844aSLois Curfman McInnes   snes->setfromoptions  = SNESSetFromOptions_EQ_LS;
592f63b844aSLois Curfman McInnes   snes->view            = SNESView_EQ_LS;
5935baf8537SBarry Smith   snes->nwork           = 0;
5945e42470aSBarry Smith 
5950452661fSBarry Smith   neP			= PetscNew(SNES_LS);   CHKPTRQ(neP);
596ff782a9fSLois Curfman McInnes   PLogObjectMemory(snes,sizeof(SNES_LS));
5975e42470aSBarry Smith   snes->data    	= (void *) neP;
5985e42470aSBarry Smith   neP->alpha		= 1.e-4;
5995e42470aSBarry Smith   neP->maxstep		= 1.e8;
6005e42470aSBarry Smith   neP->steptol		= 1.e-12;
6015e42470aSBarry Smith   neP->LineSearch       = SNESCubicLineSearch;
6025e42470aSBarry Smith   return 0;
6035e42470aSBarry Smith }
6045e42470aSBarry Smith 
605