15e76c431SBarry Smith #ifndef lint 2*406556e6SLois Curfman McInnes static char vcid[] = "$Id: ls.c,v 1.89 1997/04/21 20:09:04 curfman 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 */ 3851979daaSLois 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; 5151979daaSLois Curfman McInnes if (history) history[0] = fnorm; 5294a424c1SBarry Smith SNESMonitor(snes,0,fnorm); 535e76c431SBarry Smith 5494a9d846SBarry Smith if (fnorm == 0.0) {outits = 0; return 0;} 5594a9d846SBarry Smith 56d034289bSBarry Smith /* set parameter for default relative tolerance convergence test */ 57d034289bSBarry Smith snes->ttol = fnorm*snes->rtol; 58d034289bSBarry Smith 595e76c431SBarry Smith for ( i=0; i<maxits; i++ ) { 605e42470aSBarry Smith snes->iter = i+1; 615e76c431SBarry Smith 62ea4d3ed3SLois Curfman McInnes /* Solve J Y = F, where J is Jacobian matrix */ 635334005bSBarry Smith ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg); CHKERRQ(ierr); 645334005bSBarry Smith ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg); CHKERRQ(ierr); 6578b31e54SBarry Smith ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr); 667a00f4a9SLois Curfman McInnes snes->linear_its += PetscAbsInt(lits); 67af1ccdebSLois Curfman McInnes PLogInfo(snes,"SNESSolve_EQ_LS: iter=%d, linear solve iterations=%d\n",snes->iter,lits); 68ea4d3ed3SLois Curfman McInnes 69ea4d3ed3SLois Curfman McInnes /* Compute a (scaled) negative update in the line search routine: 70ea4d3ed3SLois Curfman McInnes Y <- X - lambda*Y 71ea4d3ed3SLois Curfman McInnes and evaluate G(Y) = function(Y)) 72ea4d3ed3SLois Curfman McInnes */ 7381b6cf68SLois Curfman McInnes ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr); 74ddd12767SBarry Smith ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail); CHKERRQ(ierr); 75af1ccdebSLois Curfman McInnes PLogInfo(snes,"SNESSolve_EQ_LS: fnorm=%g, gnorm=%g, ynorm=%g, lsfail=%d\n",fnorm,gnorm,ynorm,lsfail); 76761aaf1bSLois Curfman McInnes if (lsfail) snes->nfailures++; 775e76c431SBarry Smith 7839e2f89bSBarry Smith TMP = F; F = G; snes->vec_func_always = F; G = TMP; 7939e2f89bSBarry Smith TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP; 805e76c431SBarry Smith fnorm = gnorm; 815e76c431SBarry Smith 825e42470aSBarry Smith snes->norm = fnorm; 835e76c431SBarry Smith if (history && history_len > i+1) history[i+1] = fnorm; 84cddf8d76SBarry Smith ierr = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr); /* xnorm = || X || */ 8594a424c1SBarry Smith SNESMonitor(snes,i+1,fnorm); 865e76c431SBarry Smith 875e76c431SBarry Smith /* Test for convergence */ 88bbb6d6a8SBarry Smith if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) { 8916c95cacSBarry Smith break; 9016c95cacSBarry Smith } 9116c95cacSBarry Smith } 9239e2f89bSBarry Smith if (X != snes->vec_sol) { 93393d2d9aSLois Curfman McInnes ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr); 9439e2f89bSBarry Smith snes->vec_sol_always = snes->vec_sol; 9539e2f89bSBarry Smith snes->vec_func_always = snes->vec_func; 9639e2f89bSBarry Smith } 9752392280SLois Curfman McInnes if (i == maxits) { 9894a424c1SBarry Smith PLogInfo(snes, 99f63b844aSLois Curfman McInnes "SNESSolve_EQ_LS: Maximum number of iterations has been reached: %d\n",maxits); 10052392280SLois Curfman McInnes i--; 10152392280SLois Curfman McInnes } 10251979daaSLois Curfman McInnes if (history) snes->conv_act_size = (history_len < i+1) ? history_len : i+1; 1035e42470aSBarry Smith *outits = i+1; 1045e42470aSBarry Smith return 0; 1055e76c431SBarry Smith } 1065e76c431SBarry Smith /* ------------------------------------------------------------ */ 1075615d1e5SSatish Balay #undef __FUNC__ 1085615d1e5SSatish Balay #define __FUNC__ "SNESSetUp_EQ_LS" 109f63b844aSLois Curfman McInnes int SNESSetUp_EQ_LS(SNES snes ) 1105e76c431SBarry Smith { 1115e42470aSBarry Smith int ierr; 11281b6cf68SLois Curfman McInnes snes->nwork = 4; 113d7e8b826SBarry Smith ierr = VecDuplicateVecs(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 /* ------------------------------------------------------------ */ 1195615d1e5SSatish Balay #undef __FUNC__ 1205eea60f9SBarry Smith #define __FUNC__ "SNESDestroy_EQ_LS" /* ADIC Ignore */ 121f63b844aSLois Curfman McInnes int SNESDestroy_EQ_LS(PetscObject obj) 1225e76c431SBarry Smith { 1235e42470aSBarry Smith SNES snes = (SNES) obj; 124393d2d9aSLois Curfman McInnes int ierr; 1255baf8537SBarry Smith if (snes->nwork) { 1264b0e389bSBarry Smith ierr = VecDestroyVecs(snes->work,snes->nwork); CHKERRQ(ierr); 12721c89e3eSBarry Smith } 1280452661fSBarry Smith PetscFree(snes->data); 1295e42470aSBarry Smith return 0; 1305e76c431SBarry Smith } 1315e76c431SBarry Smith /* ------------------------------------------------------------ */ 1325615d1e5SSatish Balay #undef __FUNC__ 1335615d1e5SSatish Balay #define __FUNC__ "SNESNoLineSearch" 1345e76c431SBarry Smith /*ARGSUSED*/ 1354b828684SBarry Smith /*@C 1365e42470aSBarry Smith SNESNoLineSearch - This routine is not a line search at all; 1375e76c431SBarry Smith it simply uses the full Newton step. Thus, this routine is intended 1385e76c431SBarry Smith to serve as a template and is not recommended for general use. 1395e76c431SBarry Smith 1405e76c431SBarry Smith Input Parameters: 1415e42470aSBarry Smith . snes - nonlinear context 1425e76c431SBarry Smith . x - current iterate 1435e76c431SBarry Smith . f - residual evaluated at x 1445e76c431SBarry Smith . y - search direction (contains new iterate on output) 1455e76c431SBarry Smith . w - work vector 1465e76c431SBarry Smith . fnorm - 2-norm of f 1475e76c431SBarry Smith 148c4a48953SLois Curfman McInnes Output Parameters: 1495e76c431SBarry Smith . g - residual evaluated at new iterate y 1505e76c431SBarry Smith . y - new iterate (contains search direction on input) 1515e76c431SBarry Smith . gnorm - 2-norm of g 1525e76c431SBarry Smith . ynorm - 2-norm of search length 153761aaf1bSLois Curfman McInnes . flag - set to 0, indicating a successful line search 1545e76c431SBarry Smith 155c4a48953SLois Curfman McInnes Options Database Key: 15609d61ba7SLois Curfman McInnes $ -snes_eq_ls basic 157c4a48953SLois Curfman McInnes 15828ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 15928ae5a21SLois Curfman McInnes 160f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 16177c4ece6SBarry Smith SNESSetLineSearch() 1625e76c431SBarry Smith @*/ 1635e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 164761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag ) 1655e76c431SBarry Smith { 1665e42470aSBarry Smith int ierr; 1675334005bSBarry Smith Scalar mone = -1.0; 1685334005bSBarry Smith 169761aaf1bSLois Curfman McInnes *flag = 0; 1707857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 171cddf8d76SBarry Smith ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr); /* ynorm = || y || */ 172ea4d3ed3SLois Curfman McInnes ierr = VecAYPX(&mone,x,y); CHKERRQ(ierr); /* y <- y - x */ 173ea4d3ed3SLois Curfman McInnes ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); /* Compute F(y) */ 174cddf8d76SBarry Smith ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); /* gnorm = || g || */ 1757857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 176761aaf1bSLois Curfman McInnes return 0; 1775e76c431SBarry Smith } 1785e76c431SBarry Smith /* ------------------------------------------------------------------ */ 1795615d1e5SSatish Balay #undef __FUNC__ 1805615d1e5SSatish Balay #define __FUNC__ "SNESCubicLineSearch" 1814b828684SBarry Smith /*@C 182f525115eSLois Curfman McInnes SNESCubicLineSearch - Performs a cubic line search (default line search method). 1835e76c431SBarry Smith 1845e76c431SBarry Smith Input Parameters: 1855e42470aSBarry Smith . snes - nonlinear context 1865e76c431SBarry Smith . x - current iterate 1875e76c431SBarry Smith . f - residual evaluated at x 1885e76c431SBarry Smith . y - search direction (contains new iterate on output) 1895e76c431SBarry Smith . w - work vector 1905e76c431SBarry Smith . fnorm - 2-norm of f 1915e76c431SBarry Smith 192393d2d9aSLois Curfman McInnes Output Parameters: 1935e76c431SBarry Smith . g - residual evaluated at new iterate y 1945e76c431SBarry Smith . y - new iterate (contains search direction on input) 1955e76c431SBarry Smith . gnorm - 2-norm of g 1965e76c431SBarry Smith . ynorm - 2-norm of search length 197761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 1985e76c431SBarry Smith 199c4a48953SLois Curfman McInnes Options Database Key: 20009d61ba7SLois Curfman McInnes $ -snes_eq_ls cubic 201c4a48953SLois Curfman McInnes 2025e76c431SBarry Smith Notes: 2035e76c431SBarry Smith This line search is taken from "Numerical Methods for Unconstrained 2045e76c431SBarry Smith Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 2055e76c431SBarry Smith 20628ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 20728ae5a21SLois Curfman McInnes 20877c4ece6SBarry Smith .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearch() 2095e76c431SBarry Smith @*/ 2105e42470aSBarry Smith int SNESCubicLineSearch(SNES snes,Vec x,Vec f,Vec g,Vec y,Vec w, 211761aaf1bSLois Curfman McInnes double fnorm,double *ynorm,double *gnorm,int *flag) 2125e76c431SBarry Smith { 213*406556e6SLois Curfman McInnes /* 214*406556e6SLois Curfman McInnes Note that for line search purposes we work with with the related 215*406556e6SLois Curfman McInnes minimization problem: 216*406556e6SLois Curfman McInnes min z(x): R^n -> R, 217*406556e6SLois Curfman McInnes where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2. 218*406556e6SLois Curfman McInnes */ 219*406556e6SLois Curfman McInnes 220ddd12767SBarry Smith double steptol, initslope, lambdaprev, gnormprev, a, b, d, t1, t2; 221ea4d3ed3SLois Curfman McInnes double maxstep, minlambda, alpha, lambda, lambdatemp, lambdaneg; 2226b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2235e42470aSBarry Smith Scalar cinitslope, clambda; 2246b5873e3SBarry Smith #endif 2255e42470aSBarry Smith int ierr, count; 2265e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 2275334005bSBarry Smith Scalar mone = -1.0,scale; 2285e76c431SBarry Smith 2297857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 230761aaf1bSLois Curfman McInnes *flag = 0; 2315e76c431SBarry Smith alpha = neP->alpha; 2325e76c431SBarry Smith maxstep = neP->maxstep; 2335e76c431SBarry Smith steptol = neP->steptol; 2345e76c431SBarry Smith 235cddf8d76SBarry Smith ierr = VecNorm(y,NORM_2,ynorm); CHKERRQ(ierr); 23694a9d846SBarry Smith if (*ynorm == 0.0) { 23794a9d846SBarry Smith PLogInfo(snes,"SNESCubicLineSearch: Search direction and size is 0\n"); 238ad922ac9SBarry Smith goto theend1; 23994a9d846SBarry Smith } 2405e76c431SBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 2415e42470aSBarry Smith scale = maxstep/(*ynorm); 2426b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 24394a424c1SBarry Smith PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",real(scale)); 2446b5873e3SBarry Smith #else 24594a424c1SBarry Smith PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",scale); 2466b5873e3SBarry Smith #endif 247393d2d9aSLois Curfman McInnes ierr = VecScale(&scale,y); CHKERRQ(ierr); 2485e76c431SBarry Smith *ynorm = maxstep; 2495e76c431SBarry Smith } 2505e76c431SBarry Smith minlambda = steptol/(*ynorm); 251a703fe33SLois Curfman McInnes ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr); 2525e42470aSBarry Smith #if defined(PETSC_COMPLEX) 253a703fe33SLois Curfman McInnes ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr); 2545e42470aSBarry Smith initslope = real(cinitslope); 2555e42470aSBarry Smith #else 256a703fe33SLois Curfman McInnes ierr = VecDot(f,w,&initslope); CHKERRQ(ierr); 2575e42470aSBarry Smith #endif 2585e76c431SBarry Smith if (initslope > 0.0) initslope = -initslope; 2595e76c431SBarry Smith if (initslope == 0.0) initslope = -1.0; 2605e76c431SBarry Smith 261393d2d9aSLois Curfman McInnes ierr = VecCopy(y,w); CHKERRQ(ierr); 2625334005bSBarry Smith ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr); 26378b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 264cddf8d76SBarry Smith ierr = VecNorm(g,NORM_2,gnorm); 265*406556e6SLois Curfman McInnes if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* Sufficient reduction */ 266393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 26794a424c1SBarry Smith PLogInfo(snes,"SNESCubicLineSearch: Using full step\n"); 268ad922ac9SBarry Smith goto theend1; 2695e76c431SBarry Smith } 2705e76c431SBarry Smith 2715e76c431SBarry Smith /* Fit points with quadratic */ 2725e76c431SBarry Smith lambda = 1.0; count = 0; 273a703fe33SLois Curfman McInnes lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 2745e76c431SBarry Smith lambdaprev = lambda; 2755e76c431SBarry Smith gnormprev = *gnorm; 276ddd12767SBarry Smith if (lambdatemp <= .1*lambda) lambda = .1*lambda; 277ddd12767SBarry Smith else lambda = lambdatemp; 278393d2d9aSLois Curfman McInnes ierr = VecCopy(x,w); CHKERRQ(ierr); 279ea4d3ed3SLois Curfman McInnes lambdaneg = -lambda; 2805e42470aSBarry Smith #if defined(PETSC_COMPLEX) 281ea4d3ed3SLois Curfman McInnes clambda = lambdaneg; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 2825e42470aSBarry Smith #else 283ea4d3ed3SLois Curfman McInnes ierr = VecAXPY(&lambdaneg,y,w); CHKERRQ(ierr); 2845e42470aSBarry Smith #endif 28578b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 286cddf8d76SBarry Smith ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 287*406556e6SLois Curfman McInnes if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* sufficient reduction */ 288393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 289ea4d3ed3SLois Curfman McInnes PLogInfo(snes,"SNESCubicLineSearch: Quadratically determined step, lambda=%g\n",lambda); 290ad922ac9SBarry Smith goto theend1; 2915e76c431SBarry Smith } 2925e76c431SBarry Smith 2935e76c431SBarry Smith /* Fit points with cubic */ 2945e76c431SBarry Smith count = 1; 2955e76c431SBarry Smith while (1) { 2965e76c431SBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 2972b022350SLois Curfman McInnes PLogInfo(snes,"SNESCubicLineSearch:Unable to find good step length! %d \n",count); 2982b022350SLois Curfman McInnes PLogInfo(snes,"SNESCubicLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n", 299ea4d3ed3SLois Curfman McInnes fnorm,*gnorm,*ynorm,lambda,initslope); 300393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 301761aaf1bSLois Curfman McInnes *flag = -1; break; 3025e76c431SBarry Smith } 303*406556e6SLois Curfman McInnes t1 = ((*gnorm)*(*gnorm) - fnorm*fnorm)*0.5 - lambda*initslope; 304*406556e6SLois Curfman McInnes t2 = (gnormprev*gnormprev - fnorm*fnorm)*0.5 - lambdaprev*initslope; 305ddd12767SBarry Smith a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3062b022350SLois Curfman McInnes b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3075e76c431SBarry Smith d = b*b - 3*a*initslope; 3085e76c431SBarry Smith if (d < 0.0) d = 0.0; 3095e76c431SBarry Smith if (a == 0.0) { 3105e76c431SBarry Smith lambdatemp = -initslope/(2.0*b); 3115e76c431SBarry Smith } else { 3125e76c431SBarry Smith lambdatemp = (-b + sqrt(d))/(3.0*a); 3135e76c431SBarry Smith } 3145e76c431SBarry Smith if (lambdatemp > .5*lambda) { 3155e76c431SBarry Smith lambdatemp = .5*lambda; 3165e76c431SBarry Smith } 3175e76c431SBarry Smith lambdaprev = lambda; 3185e76c431SBarry Smith gnormprev = *gnorm; 3195e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3205e76c431SBarry Smith lambda = .1*lambda; 3215e76c431SBarry Smith } 3225e76c431SBarry Smith else lambda = lambdatemp; 323393d2d9aSLois Curfman McInnes ierr = VecCopy( x, w ); CHKERRQ(ierr); 324ea4d3ed3SLois Curfman McInnes lambdaneg = -lambda; 3255e42470aSBarry Smith #if defined(PETSC_COMPLEX) 326ea4d3ed3SLois Curfman McInnes clambda = lambdaneg; 327393d2d9aSLois Curfman McInnes ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 3285e42470aSBarry Smith #else 329ea4d3ed3SLois Curfman McInnes ierr = VecAXPY(&lambdaneg,y,w); CHKERRQ(ierr); 3305e42470aSBarry Smith #endif 33178b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 332cddf8d76SBarry Smith ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 333*406556e6SLois Curfman McInnes if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* is reduction enough? */ 334393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 335ea4d3ed3SLois Curfman McInnes PLogInfo(snes,"SNESCubicLineSearch: Cubically determined step, lambda=%g\n",lambda); 3362715565aSLois Curfman McInnes goto theend1; 3372b022350SLois Curfman McInnes } else { 3382b022350SLois Curfman McInnes PLogInfo(snes,"SNESCubicLineSearch: Cubic step no good, shrinking lambda, lambda=%g\n",lambda); 3395e76c431SBarry Smith } 3405e76c431SBarry Smith count++; 3415e76c431SBarry Smith } 342ad922ac9SBarry Smith theend1: 3437857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3445e42470aSBarry Smith return 0; 3455e76c431SBarry Smith } 34652392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 3475615d1e5SSatish Balay #undef __FUNC__ 3485615d1e5SSatish Balay #define __FUNC__ "SNESQuadraticLineSearch" 3494b828684SBarry Smith /*@C 350f525115eSLois Curfman McInnes SNESQuadraticLineSearch - Performs a quadratic line search. 3515e76c431SBarry Smith 3525e42470aSBarry Smith Input Parameters: 353f59ffdedSLois Curfman McInnes . snes - the SNES context 3545e42470aSBarry Smith . x - current iterate 3555e42470aSBarry Smith . f - residual evaluated at x 3565e42470aSBarry Smith . y - search direction (contains new iterate on output) 3575e42470aSBarry Smith . w - work vector 3585e42470aSBarry Smith . fnorm - 2-norm of f 3595e42470aSBarry Smith 360c4a48953SLois Curfman McInnes Output Parameters: 3615e42470aSBarry Smith . g - residual evaluated at new iterate y 3625e42470aSBarry Smith . y - new iterate (contains search direction on input) 3635e42470aSBarry Smith . gnorm - 2-norm of g 3645e42470aSBarry Smith . ynorm - 2-norm of search length 365761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 3665e42470aSBarry Smith 367c4a48953SLois Curfman McInnes Options Database Key: 36809d61ba7SLois Curfman McInnes $ -snes_eq_ls quadratic 369c4a48953SLois Curfman McInnes 3705e42470aSBarry Smith Notes: 37177c4ece6SBarry Smith Use SNESSetLineSearch() 372f63b844aSLois Curfman McInnes to set this routine within the SNES_EQ_LS method. 3735e42470aSBarry Smith 374f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search 375f59ffdedSLois Curfman McInnes 37677c4ece6SBarry Smith .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearch() 3775e42470aSBarry Smith @*/ 3785e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 379761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag) 3805e76c431SBarry Smith { 381*406556e6SLois Curfman McInnes /* 382*406556e6SLois Curfman McInnes Note that for line search purposes we work with with the related 383*406556e6SLois Curfman McInnes minimization problem: 384*406556e6SLois Curfman McInnes min z(x): R^n -> R, 385*406556e6SLois Curfman McInnes where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2. 386*406556e6SLois Curfman McInnes */ 387ddd12767SBarry Smith double steptol,initslope,lambdaprev,gnormprev,maxstep,minlambda,alpha,lambda,lambdatemp; 3886b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 3895e42470aSBarry Smith Scalar cinitslope,clambda; 3906b5873e3SBarry Smith #endif 3915e42470aSBarry Smith int ierr,count; 3925e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 3935334005bSBarry Smith Scalar mone = -1.0,scale; 3945e76c431SBarry Smith 3957857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 396761aaf1bSLois Curfman McInnes *flag = 0; 3975e42470aSBarry Smith alpha = neP->alpha; 3985e42470aSBarry Smith maxstep = neP->maxstep; 3995e42470aSBarry Smith steptol = neP->steptol; 4005e76c431SBarry Smith 401cddf8d76SBarry Smith VecNorm(y, NORM_2,ynorm ); 40294a9d846SBarry Smith if (*ynorm == 0.0) { 40394a9d846SBarry Smith PLogInfo(snes,"SNESQuadraticLineSearch: Search direction and size is 0\n"); 404ad922ac9SBarry Smith goto theend2; 40594a9d846SBarry Smith } 4065e42470aSBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 4075e42470aSBarry Smith scale = maxstep/(*ynorm); 408393d2d9aSLois Curfman McInnes ierr = VecScale(&scale,y); CHKERRQ(ierr); 4095e42470aSBarry Smith *ynorm = maxstep; 4105e76c431SBarry Smith } 4115e42470aSBarry Smith minlambda = steptol/(*ynorm); 412a703fe33SLois Curfman McInnes ierr = MatMult(snes->jacobian,y,w); CHKERRQ(ierr); 4135e42470aSBarry Smith #if defined(PETSC_COMPLEX) 414a703fe33SLois Curfman McInnes ierr = VecDot(f,w,&cinitslope); CHKERRQ(ierr); 4155e42470aSBarry Smith initslope = real(cinitslope); 4165e42470aSBarry Smith #else 417a703fe33SLois Curfman McInnes ierr = VecDot(f,w,&initslope); CHKERRQ(ierr); 4185e42470aSBarry Smith #endif 4195e42470aSBarry Smith if (initslope > 0.0) initslope = -initslope; 4205e42470aSBarry Smith if (initslope == 0.0) initslope = -1.0; 4215e42470aSBarry Smith 422393d2d9aSLois Curfman McInnes ierr = VecCopy(y,w); CHKERRQ(ierr); 4235334005bSBarry Smith ierr = VecAYPX(&mone,x,w); CHKERRQ(ierr); 42478b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 425cddf8d76SBarry Smith ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 426*406556e6SLois Curfman McInnes if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* Sufficient reduction */ 427393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 42894a424c1SBarry Smith PLogInfo(snes,"SNESQuadraticLineSearch: Using full step\n"); 429ad922ac9SBarry Smith goto theend2; 4305e42470aSBarry Smith } 4315e42470aSBarry Smith 4325e42470aSBarry Smith /* Fit points with quadratic */ 4335e42470aSBarry Smith lambda = 1.0; count = 0; 4345e42470aSBarry Smith count = 1; 4355e42470aSBarry Smith while (1) { 4365e42470aSBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 43794a424c1SBarry Smith PLogInfo(snes, 4384b828684SBarry Smith "SNESQuadraticLineSearch:Unable to find good step length! %d \n",count); 43994a424c1SBarry Smith PLogInfo(snes, 440ea4d3ed3SLois Curfman McInnes "SNESQuadraticLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n", 441ea4d3ed3SLois Curfman McInnes fnorm,*gnorm,*ynorm,lambda,initslope); 442393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 443761aaf1bSLois Curfman McInnes *flag = -1; break; 4445e42470aSBarry Smith } 445a703fe33SLois Curfman McInnes lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 4465e42470aSBarry Smith lambdaprev = lambda; 4475e42470aSBarry Smith gnormprev = *gnorm; 4485e42470aSBarry Smith if (lambdatemp <= .1*lambda) { 4495e42470aSBarry Smith lambda = .1*lambda; 4505e42470aSBarry Smith } else lambda = lambdatemp; 451393d2d9aSLois Curfman McInnes ierr = VecCopy(x,w); CHKERRQ(ierr); 4525334005bSBarry Smith lambda = -lambda; 4535e42470aSBarry Smith #if defined(PETSC_COMPLEX) 454393d2d9aSLois Curfman McInnes clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 4555e42470aSBarry Smith #else 456393d2d9aSLois Curfman McInnes ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr); 4575e42470aSBarry Smith #endif 45878b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 459cddf8d76SBarry Smith ierr = VecNorm(g,NORM_2,gnorm); CHKERRQ(ierr); 460*406556e6SLois Curfman McInnes if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* sufficient reduction */ 461393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 46294a424c1SBarry Smith PLogInfo(snes, 463ea4d3ed3SLois Curfman McInnes "SNESQuadraticLineSearch:Quadratically determined step, lambda=%g\n",lambda); 46406259719SBarry Smith break; 4655e42470aSBarry Smith } 4665e42470aSBarry Smith count++; 4675e42470aSBarry Smith } 468ad922ac9SBarry Smith theend2: 4697857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4705e42470aSBarry Smith return 0; 4715e76c431SBarry Smith } 4725e76c431SBarry Smith /* ------------------------------------------------------------ */ 4735615d1e5SSatish Balay #undef __FUNC__ 4745eea60f9SBarry Smith #define __FUNC__ "SNESSetLineSearch" /* ADIC Ignore */ 475c9e6a524SLois Curfman McInnes /*@C 47677c4ece6SBarry Smith SNESSetLineSearch - Sets the line search routine to be used 477f63b844aSLois Curfman McInnes by the method SNES_EQ_LS. 4785e76c431SBarry Smith 4795e76c431SBarry Smith Input Parameters: 480eafb4bcbSBarry Smith . snes - nonlinear context obtained from SNESCreate() 4815e76c431SBarry Smith . func - pointer to int function 4825e76c431SBarry Smith 483c4a48953SLois Curfman McInnes Available Routines: 484f59ffdedSLois Curfman McInnes . SNESCubicLineSearch() - default line search 485f59ffdedSLois Curfman McInnes . SNESQuadraticLineSearch() - quadratic line search 486f59ffdedSLois Curfman McInnes . SNESNoLineSearch() - the full Newton step (actually not a line search) 4875e76c431SBarry Smith 488c4a48953SLois Curfman McInnes Options Database Keys: 48909d61ba7SLois Curfman McInnes $ -snes_eq_ls [basic,quadratic,cubic] 490912ebf9aSLois Curfman McInnes $ -snes_eq_ls_alpha <alpha> 491912ebf9aSLois Curfman McInnes $ -snes_eq_ls_maxstep <max> 492912ebf9aSLois Curfman McInnes $ -snes_eq_ls_steptol <steptol> 493c4a48953SLois Curfman McInnes 4945e76c431SBarry Smith Calling sequence of func: 495f59ffdedSLois Curfman McInnes func (SNES snes, Vec x, Vec f, Vec g, Vec y, 496761aaf1bSLois Curfman McInnes Vec w, double fnorm, double *ynorm, 497761aaf1bSLois Curfman McInnes double *gnorm, *flag) 4985e76c431SBarry Smith 4995e76c431SBarry Smith Input parameters for func: 5005e42470aSBarry Smith . snes - nonlinear context 5015e76c431SBarry Smith . x - current iterate 5025e76c431SBarry Smith . f - residual evaluated at x 5035e76c431SBarry Smith . y - search direction (contains new iterate on output) 5045e76c431SBarry Smith . w - work vector 5055e76c431SBarry Smith . fnorm - 2-norm of f 5065e76c431SBarry Smith 5075e76c431SBarry Smith Output parameters for func: 5085e76c431SBarry Smith . g - residual evaluated at new iterate y 5095e76c431SBarry Smith . y - new iterate (contains search direction on input) 5105e76c431SBarry Smith . gnorm - 2-norm of g 5115e76c431SBarry Smith . ynorm - 2-norm of search length 512761aaf1bSLois Curfman McInnes . flag - set to 0 if the line search succeeds; a nonzero integer 513761aaf1bSLois Curfman McInnes on failure. 514f59ffdedSLois Curfman McInnes 515f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine 516f59ffdedSLois Curfman McInnes 517f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch() 5185e76c431SBarry Smith @*/ 51977c4ece6SBarry Smith int SNESSetLineSearch(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 520761aaf1bSLois Curfman McInnes double,double*,double*,int*)) 5215e76c431SBarry Smith { 522f63b844aSLois Curfman McInnes if ((snes)->type == SNES_EQ_LS) ((SNES_LS *)(snes->data))->LineSearch = func; 5235e42470aSBarry Smith return 0; 5245e76c431SBarry Smith } 52552392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 5265615d1e5SSatish Balay #undef __FUNC__ 5275eea60f9SBarry Smith #define __FUNC__ "SNESPrintHelp_EQ_LS" /* ADIC Ignore */ 528f63b844aSLois Curfman McInnes static int SNESPrintHelp_EQ_LS(SNES snes,char *p) 5295e42470aSBarry Smith { 5305e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5316daaf66cSBarry Smith 532f63b844aSLois Curfman McInnes PetscPrintf(snes->comm," method SNES_EQ_LS (ls) for systems of nonlinear equations:\n"); 53309d61ba7SLois Curfman McInnes PetscPrintf(snes->comm," %ssnes_eq_ls [basic,quadratic,cubic]\n",p); 53409d61ba7SLois Curfman McInnes PetscPrintf(snes->comm," %ssnes_eq_ls_alpha <alpha> (default %g)\n",p,ls->alpha); 53509d61ba7SLois Curfman McInnes PetscPrintf(snes->comm," %ssnes_eq_ls_maxstep <max> (default %g)\n",p,ls->maxstep); 53609d61ba7SLois Curfman McInnes PetscPrintf(snes->comm," %ssnes_eq_ls_steptol <tol> (default %g)\n",p,ls->steptol); 5376b5873e3SBarry Smith return 0; 5385e42470aSBarry Smith } 53952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 5405615d1e5SSatish Balay #undef __FUNC__ 5415eea60f9SBarry Smith #define __FUNC__ "SNESView_EQ_LS" /* ADIC Ignore */ 542f63b844aSLois Curfman McInnes static int SNESView_EQ_LS(PetscObject obj,Viewer viewer) 543a935fc98SLois Curfman McInnes { 544a935fc98SLois Curfman McInnes SNES snes = (SNES)obj; 545a935fc98SLois Curfman McInnes SNES_LS *ls = (SNES_LS *)snes->data; 546d636dbe3SBarry Smith FILE *fd; 54719bcc07fSBarry Smith char *cstr; 54851695f54SLois Curfman McInnes int ierr; 54919bcc07fSBarry Smith ViewerType vtype; 550a935fc98SLois Curfman McInnes 55119bcc07fSBarry Smith ierr = ViewerGetType(viewer,&vtype); CHKERRQ(ierr); 55219bcc07fSBarry Smith if (vtype == ASCII_FILE_VIEWER || vtype == ASCII_FILES_VIEWER) { 55390ace30eSBarry Smith ierr = ViewerASCIIGetPointer(viewer,&fd); CHKERRQ(ierr); 55419bcc07fSBarry Smith if (ls->LineSearch == SNESNoLineSearch) cstr = "SNESNoLineSearch"; 55519bcc07fSBarry Smith else if (ls->LineSearch == SNESQuadraticLineSearch) cstr = "SNESQuadraticLineSearch"; 55619bcc07fSBarry Smith else if (ls->LineSearch == SNESCubicLineSearch) cstr = "SNESCubicLineSearch"; 55719bcc07fSBarry Smith else cstr = "unknown"; 55877c4ece6SBarry Smith PetscFPrintf(snes->comm,fd," line search variant: %s\n",cstr); 55977c4ece6SBarry Smith PetscFPrintf(snes->comm,fd," alpha=%g, maxstep=%g, steptol=%g\n", 560a935fc98SLois Curfman McInnes ls->alpha,ls->maxstep,ls->steptol); 56119bcc07fSBarry Smith } 562a935fc98SLois Curfman McInnes return 0; 563a935fc98SLois Curfman McInnes } 56452392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 5655615d1e5SSatish Balay #undef __FUNC__ 5665615d1e5SSatish Balay #define __FUNC__ "SNESSetFromOptions_EQ_LS" 567f63b844aSLois Curfman McInnes static int SNESSetFromOptions_EQ_LS(SNES snes) 5685e42470aSBarry Smith { 5695e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5705e42470aSBarry Smith char ver[16]; 5715e42470aSBarry Smith double tmp; 57225cdf11fSBarry Smith int ierr,flg; 5735e42470aSBarry Smith 57409d61ba7SLois Curfman McInnes ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_alpha",&tmp, &flg);CHKERRQ(ierr); 57525cdf11fSBarry Smith if (flg) { 5765e42470aSBarry Smith ls->alpha = tmp; 5775e42470aSBarry Smith } 57809d61ba7SLois Curfman McInnes ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_maxstep",&tmp, &flg);CHKERRQ(ierr); 57925cdf11fSBarry Smith if (flg) { 5805e42470aSBarry Smith ls->maxstep = tmp; 5815e42470aSBarry Smith } 58209d61ba7SLois Curfman McInnes ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_steptol",&tmp, &flg);CHKERRQ(ierr); 58325cdf11fSBarry Smith if (flg) { 5845e42470aSBarry Smith ls->steptol = tmp; 5855e42470aSBarry Smith } 58609d61ba7SLois Curfman McInnes ierr = OptionsGetString(snes->prefix,"-snes_eq_ls",ver,16, &flg); CHKERRQ(ierr); 58725cdf11fSBarry Smith if (flg) { 58848d91487SBarry Smith if (!PetscStrcmp(ver,"basic")) { 58977c4ece6SBarry Smith SNESSetLineSearch(snes,SNESNoLineSearch); 5905e42470aSBarry Smith } 59148d91487SBarry Smith else if (!PetscStrcmp(ver,"quadratic")) { 59277c4ece6SBarry Smith SNESSetLineSearch(snes,SNESQuadraticLineSearch); 5935e42470aSBarry Smith } 59448d91487SBarry Smith else if (!PetscStrcmp(ver,"cubic")) { 59577c4ece6SBarry Smith SNESSetLineSearch(snes,SNESCubicLineSearch); 5965e42470aSBarry Smith } 597e3372554SBarry Smith else {SETERRQ(1,0,"Unknown line search");} 5985e42470aSBarry Smith } 5995e42470aSBarry Smith return 0; 6005e42470aSBarry Smith } 6015e42470aSBarry Smith /* ------------------------------------------------------------ */ 6025615d1e5SSatish Balay #undef __FUNC__ 6035615d1e5SSatish Balay #define __FUNC__ "SNESCreate_EQ_LS" 604f63b844aSLois Curfman McInnes int SNESCreate_EQ_LS(SNES snes ) 6055e42470aSBarry Smith { 6065e42470aSBarry Smith SNES_LS *neP; 6075e42470aSBarry Smith 608ddd12767SBarry Smith if (snes->method_class != SNES_NONLINEAR_EQUATIONS) 609e3372554SBarry Smith SETERRQ(1,0,"For SNES_NONLINEAR_EQUATIONS only"); 610f63b844aSLois Curfman McInnes snes->type = SNES_EQ_LS; 611f63b844aSLois Curfman McInnes snes->setup = SNESSetUp_EQ_LS; 612f63b844aSLois Curfman McInnes snes->solve = SNESSolve_EQ_LS; 613f63b844aSLois Curfman McInnes snes->destroy = SNESDestroy_EQ_LS; 614f63b844aSLois Curfman McInnes snes->converged = SNESConverged_EQ_LS; 615f63b844aSLois Curfman McInnes snes->printhelp = SNESPrintHelp_EQ_LS; 616f63b844aSLois Curfman McInnes snes->setfromoptions = SNESSetFromOptions_EQ_LS; 617f63b844aSLois Curfman McInnes snes->view = SNESView_EQ_LS; 6185baf8537SBarry Smith snes->nwork = 0; 6195e42470aSBarry Smith 6200452661fSBarry Smith neP = PetscNew(SNES_LS); CHKPTRQ(neP); 621ff782a9fSLois Curfman McInnes PLogObjectMemory(snes,sizeof(SNES_LS)); 6225e42470aSBarry Smith snes->data = (void *) neP; 6235e42470aSBarry Smith neP->alpha = 1.e-4; 6245e42470aSBarry Smith neP->maxstep = 1.e8; 6255e42470aSBarry Smith neP->steptol = 1.e-12; 6265e42470aSBarry Smith neP->LineSearch = SNESCubicLineSearch; 6275e42470aSBarry Smith return 0; 6285e42470aSBarry Smith } 6295e42470aSBarry Smith 630