15e76c431SBarry Smith #ifndef lint 2*ddbbdb52SLois Curfman McInnes static char vcid[] = "$Id: ls.c,v 1.30 1995/07/14 18:36:07 curfman Exp curfman $"; 35e76c431SBarry Smith #endif 45e76c431SBarry Smith 55e76c431SBarry Smith #include <math.h> 6fbe28522SBarry Smith #include "ls.h" 7a935fc98SLois Curfman McInnes #include "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 245e42470aSBarry Smith and Schnabel. See the examples in src/snes/examples. 255e76c431SBarry Smith */ 265e76c431SBarry Smith 275e42470aSBarry Smith int SNESSolve_LS(SNES snes,int *outits) 285e76c431SBarry Smith { 295e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 30761aaf1bSLois Curfman McInnes int maxits, i, history_len, ierr, lits, lsfail; 31df60cc22SBarry Smith MatStructure flg = ALLMAT_DIFFERENT_NONZERO_PATTERN; 326b5873e3SBarry Smith double fnorm, gnorm, xnorm, ynorm, *history; 335e42470aSBarry Smith Vec Y, X, F, G, W, TMP; 345e76c431SBarry Smith 355e42470aSBarry Smith history = snes->conv_hist; /* convergence history */ 365e42470aSBarry Smith history_len = snes->conv_hist_len; /* convergence history length */ 375e42470aSBarry Smith maxits = snes->max_its; /* maximum number of iterations */ 385e42470aSBarry Smith X = snes->vec_sol; /* solution vector */ 3939e2f89bSBarry Smith F = snes->vec_func; /* residual vector */ 405e42470aSBarry Smith Y = snes->work[0]; /* work vectors */ 415e42470aSBarry Smith G = snes->work[1]; 425e42470aSBarry Smith W = snes->work[2]; 435e76c431SBarry Smith 4478b31e54SBarry Smith ierr = SNESComputeInitialGuess(snes,X); CHKERRQ(ierr); /* X <- X_0 */ 455e42470aSBarry Smith VecNorm(X,&xnorm); /* xnorm = || X || */ 4678b31e54SBarry Smith ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr); /* (+/-) F(X) */ 475e42470aSBarry Smith VecNorm(F,&fnorm); /* fnorm <- ||F|| */ 485e42470aSBarry Smith snes->norm = fnorm; 495e76c431SBarry Smith if (history && history_len > 0) history[0] = fnorm; 50a935fc98SLois Curfman McInnes if (snes->Monitor) 51a935fc98SLois Curfman McInnes {ierr = (*snes->Monitor)(snes,0,fnorm,snes->monP); CHKERRQ(ierr);} 525e76c431SBarry Smith 535e76c431SBarry Smith for ( i=0; i<maxits; i++ ) { 545e42470aSBarry Smith snes->iter = i+1; 555e76c431SBarry Smith 565e76c431SBarry Smith /* Solve J Y = -F, where J is Jacobian matrix */ 57df60cc22SBarry Smith ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre, 5878b31e54SBarry Smith &flg,snes->jacP); CHKERRQ(ierr); 59a935fc98SLois Curfman McInnes ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre, 60a935fc98SLois Curfman McInnes flg); CHKERRQ(ierr); 6178b31e54SBarry Smith ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr); 6281b6cf68SLois Curfman McInnes ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr); 63761aaf1bSLois Curfman McInnes ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail); 64761aaf1bSLois Curfman McInnes if (lsfail) snes->nfailures++; 6578b31e54SBarry Smith CHKERRQ(ierr); 665e76c431SBarry Smith 6739e2f89bSBarry Smith TMP = F; F = G; snes->vec_func_always = F; G = TMP; 6839e2f89bSBarry Smith TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP; 695e76c431SBarry Smith fnorm = gnorm; 705e76c431SBarry Smith 715e42470aSBarry Smith snes->norm = fnorm; 725e76c431SBarry Smith if (history && history_len > i+1) history[i+1] = fnorm; 735e42470aSBarry Smith VecNorm(X,&xnorm); /* xnorm = || X || */ 74a935fc98SLois Curfman McInnes if (snes->Monitor) 75a935fc98SLois Curfman McInnes {(*snes->Monitor)(snes,i+1,fnorm,snes->monP); CHKERRQ(ierr);} 765e76c431SBarry Smith 775e76c431SBarry Smith /* Test for convergence */ 785e42470aSBarry Smith if ((*snes->Converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) { 7939e2f89bSBarry Smith if (X != snes->vec_sol) { 8039e2f89bSBarry Smith VecCopy(X,snes->vec_sol); 8139e2f89bSBarry Smith snes->vec_sol_always = snes->vec_sol; 8239e2f89bSBarry Smith snes->vec_func_always = snes->vec_func; 8339e2f89bSBarry Smith } 845e76c431SBarry Smith break; 855e76c431SBarry Smith } 865e76c431SBarry Smith } 875e76c431SBarry Smith if (i == maxits) i--; 885e42470aSBarry Smith *outits = i+1; 895e42470aSBarry Smith return 0; 905e76c431SBarry Smith } 915e76c431SBarry Smith /* ------------------------------------------------------------ */ 925e42470aSBarry Smith int SNESSetUp_LS(SNES snes ) 935e76c431SBarry Smith { 945e42470aSBarry Smith int ierr; 9581b6cf68SLois Curfman McInnes snes->nwork = 4; 9678b31e54SBarry Smith ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work); CHKERRQ(ierr); 975e42470aSBarry Smith PLogObjectParents(snes,snes->nwork,snes->work ); 9881b6cf68SLois Curfman McInnes snes->vec_sol_update_always = snes->work[3]; 995e42470aSBarry Smith return 0; 1005e76c431SBarry Smith } 1015e76c431SBarry Smith /* ------------------------------------------------------------ */ 1025e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj) 1035e76c431SBarry Smith { 1045e42470aSBarry Smith SNES snes = (SNES) obj; 1055e42470aSBarry Smith VecFreeVecs(snes->work, snes->nwork ); 10678b31e54SBarry Smith PETSCFREE(snes->data); 1075e42470aSBarry Smith return 0; 1085e76c431SBarry Smith } 1095e42470aSBarry Smith /*@ 1100de89847SLois Curfman McInnes SNESDefaultMonitor - Default SNES monitoring routine. Prints the 1115e42470aSBarry Smith residual norm at each iteration. 1125e42470aSBarry Smith 1135e42470aSBarry Smith Input Parameters: 1140de89847SLois Curfman McInnes . snes - the SNES context 1150de89847SLois Curfman McInnes . its - iteration number 1160de89847SLois Curfman McInnes . fnorm - 2-norm residual value (may be estimated) 1170de89847SLois Curfman McInnes . dummy - unused context 1185e42470aSBarry Smith 1195e42470aSBarry Smith Notes: 1205e42470aSBarry Smith f is either the residual or its negative, depending on the user's 121a9581db2SBarry Smith preference, as set with SNESSetFunction(). 122a67c8522SLois Curfman McInnes 123a67c8522SLois Curfman McInnes .keywords: SNES, nonlinear, default, monitor, residual, norm 124a67c8522SLois Curfman McInnes 125a67c8522SLois Curfman McInnes .seealso: SNESSetMonitor() 1265e42470aSBarry Smith @*/ 127eafb4bcbSBarry Smith int SNESDefaultMonitor(SNES snes,int its,double fnorm,void *dummy) 1285e42470aSBarry Smith { 129614e12f8SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm); 130614e12f8SBarry Smith return 0; 131614e12f8SBarry Smith } 132614e12f8SBarry Smith 133614e12f8SBarry Smith int SNESDefaultSMonitor(SNES snes,int its, double fnorm,void *dummy) 134614e12f8SBarry Smith { 135614e12f8SBarry Smith if (fnorm > 1.e-9 || fnorm == 0.0) { 1367f813858SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm); 13769e4de80SBarry Smith } 138614e12f8SBarry Smith else if (fnorm > 1.e-11){ 13969e4de80SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm %5.3e \n",its,fnorm); 14069e4de80SBarry Smith } 141614e12f8SBarry Smith else { 142614e12f8SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm < 1.e-11\n",its); 143614e12f8SBarry Smith } 1445e42470aSBarry Smith return 0; 1455e42470aSBarry Smith } 1465e42470aSBarry Smith 1475e42470aSBarry Smith /*@ 1485e42470aSBarry Smith SNESDefaultConverged - Default test for monitoring the convergence 1490de89847SLois Curfman McInnes of the SNES solvers. 1505e42470aSBarry Smith 1515e42470aSBarry Smith Input Parameters: 1520de89847SLois Curfman McInnes . snes - the SNES context 1535e42470aSBarry Smith . xnorm - 2-norm of current iterate 1545e42470aSBarry Smith . pnorm - 2-norm of current step 1555e42470aSBarry Smith . fnorm - 2-norm of residual 1560de89847SLois Curfman McInnes . dummy - unused context 1575e42470aSBarry Smith 1585e42470aSBarry Smith Returns: 1595e42470aSBarry Smith $ 2 if ( fnorm < atol ), 1605e42470aSBarry Smith $ 3 if ( pnorm < xtol*xnorm ), 1615e42470aSBarry Smith $ -2 if ( nres > max_res ), 1625e42470aSBarry Smith $ 0 otherwise, 1635e42470aSBarry Smith 1645e42470aSBarry Smith where 1655e42470aSBarry Smith $ atol - absolute residual norm tolerance, 1660de89847SLois Curfman McInnes $ set with SNESSetAbsoluteTolerance() 1675e42470aSBarry Smith $ max_res - maximum number of residual evaluations, 1680de89847SLois Curfman McInnes $ set with SNESSetMaxResidualEvaluations() 1695e42470aSBarry Smith $ nres - number of residual evaluations 1705e42470aSBarry Smith $ xtol - relative residual norm tolerance, 1710de89847SLois Curfman McInnes $ set with SNESSetRelativeTolerance() 1725e42470aSBarry Smith 1730de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence 1745e42470aSBarry Smith 1750de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest() 1765e42470aSBarry Smith @*/ 1775e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm, 1785e42470aSBarry Smith void *dummy) 1795e42470aSBarry Smith { 1805e42470aSBarry Smith if (fnorm < snes->atol) { 181a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 182a67c8522SLois Curfman McInnes "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol); 1835e42470aSBarry Smith return 2; 1845e42470aSBarry Smith } 1855e42470aSBarry Smith if (pnorm < snes->xtol*(xnorm)) { 186a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 187a67c8522SLois Curfman McInnes "Converged due to small update length %g < %g*%g\n", 1885e42470aSBarry Smith pnorm,snes->xtol,xnorm); 1895e42470aSBarry Smith return 3; 1905e42470aSBarry Smith } 19149e3953dSBarry Smith if (snes->nfuncs > snes->max_funcs) { 192a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 193*ddbbdb52SLois Curfman McInnes "Exceeded maximum number of function evaluations: %d > %d\n", 19449e3953dSBarry Smith snes->nfuncs, snes->max_funcs ); 195a67c8522SLois Curfman McInnes return -2; 196a67c8522SLois Curfman McInnes } 1975e42470aSBarry Smith return 0; 1985e42470aSBarry Smith } 1995e42470aSBarry Smith 2005e76c431SBarry Smith /* ------------------------------------------------------------ */ 2015e76c431SBarry Smith /*ARGSUSED*/ 2025e76c431SBarry Smith /*@ 2035e42470aSBarry Smith SNESNoLineSearch - This routine is not a line search at all; 2045e76c431SBarry Smith it simply uses the full Newton step. Thus, this routine is intended 2055e76c431SBarry Smith to serve as a template and is not recommended for general use. 2065e76c431SBarry Smith 2075e76c431SBarry Smith Input Parameters: 2085e42470aSBarry Smith . snes - nonlinear context 2095e76c431SBarry Smith . x - current iterate 2105e76c431SBarry Smith . f - residual evaluated at x 2115e76c431SBarry Smith . y - search direction (contains new iterate on output) 2125e76c431SBarry Smith . w - work vector 2135e76c431SBarry Smith . fnorm - 2-norm of f 2145e76c431SBarry Smith 215c4a48953SLois Curfman McInnes Output Parameters: 2165e76c431SBarry Smith . g - residual evaluated at new iterate y 2175e76c431SBarry Smith . y - new iterate (contains search direction on input) 2185e76c431SBarry Smith . gnorm - 2-norm of g 2195e76c431SBarry Smith . ynorm - 2-norm of search length 220761aaf1bSLois Curfman McInnes . flag - set to 0, indicating a successful line search 2215e76c431SBarry Smith 222c4a48953SLois Curfman McInnes Options Database Key: 223c4a48953SLois Curfman McInnes $ -snes_line_search basic 224c4a48953SLois Curfman McInnes 22528ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 22628ae5a21SLois Curfman McInnes 227f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 228a935fc98SLois Curfman McInnes SNESSetLineSearchRoutine() 2295e76c431SBarry Smith @*/ 2305e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 231761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag ) 2325e76c431SBarry Smith { 2335e42470aSBarry Smith int ierr; 2345e42470aSBarry Smith Scalar one = 1.0; 235761aaf1bSLois Curfman McInnes *flag = 0; 2367857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 2375e42470aSBarry Smith VecNorm(y,ynorm); /* ynorm = || y || */ 2385e42470aSBarry Smith VecAXPY(&one,x,y); /* y <- x + y */ 23978b31e54SBarry Smith ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); 2405e42470aSBarry Smith VecNorm(g,gnorm); /* gnorm = || g || */ 2417857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 242761aaf1bSLois Curfman McInnes return 0; 2435e76c431SBarry Smith } 2445e76c431SBarry Smith /* ------------------------------------------------------------------ */ 2455e76c431SBarry Smith /*@ 246f59ffdedSLois Curfman McInnes SNESCubicLineSearch - This routine performs a cubic line search and 247f59ffdedSLois Curfman McInnes is the default line search method. 2485e76c431SBarry Smith 2495e76c431SBarry Smith Input Parameters: 2505e42470aSBarry Smith . snes - nonlinear context 2515e76c431SBarry Smith . x - current iterate 2525e76c431SBarry Smith . f - residual evaluated at x 2535e76c431SBarry Smith . y - search direction (contains new iterate on output) 2545e76c431SBarry Smith . w - work vector 2555e76c431SBarry Smith . fnorm - 2-norm of f 2565e76c431SBarry Smith 2575e76c431SBarry Smith Output parameters: 2585e76c431SBarry Smith . g - residual evaluated at new iterate y 2595e76c431SBarry Smith . y - new iterate (contains search direction on input) 2605e76c431SBarry Smith . gnorm - 2-norm of g 2615e76c431SBarry Smith . ynorm - 2-norm of search length 262761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 2635e76c431SBarry Smith 264c4a48953SLois Curfman McInnes Options Database Key: 265c4a48953SLois Curfman McInnes $ -snes_line_search cubic 266c4a48953SLois Curfman McInnes 2675e76c431SBarry Smith Notes: 2685e76c431SBarry Smith This line search is taken from "Numerical Methods for Unconstrained 2695e76c431SBarry Smith Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 2705e76c431SBarry Smith 27128ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 27228ae5a21SLois Curfman McInnes 273f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 2745e76c431SBarry Smith @*/ 2755e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 276761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag) 2775e76c431SBarry Smith { 2785e42470aSBarry Smith double steptol, initslope; 2795e42470aSBarry Smith double lambdaprev, gnormprev; 2805e76c431SBarry Smith double a, b, d, t1, t2; 2816b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2825e42470aSBarry Smith Scalar cinitslope,clambda; 2836b5873e3SBarry Smith #endif 2845e42470aSBarry Smith int ierr,count; 2855e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 2865e42470aSBarry Smith Scalar one = 1.0,scale; 2875e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 2885e76c431SBarry Smith 2897857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 290761aaf1bSLois Curfman McInnes *flag = 0; 2915e76c431SBarry Smith alpha = neP->alpha; 2925e76c431SBarry Smith maxstep = neP->maxstep; 2935e76c431SBarry Smith steptol = neP->steptol; 2945e76c431SBarry Smith 2955e42470aSBarry Smith VecNorm(y, ynorm ); 2965e76c431SBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 2975e42470aSBarry Smith scale = maxstep/(*ynorm); 2986b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2996b5873e3SBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale)); 3006b5873e3SBarry Smith #else 3015e42470aSBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale); 3026b5873e3SBarry Smith #endif 3035e42470aSBarry Smith VecScale(&scale, y ); 3045e76c431SBarry Smith *ynorm = maxstep; 3055e76c431SBarry Smith } 3065e76c431SBarry Smith minlambda = steptol/(*ynorm); 3075e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3085e42470aSBarry Smith VecDot(f, y, &cinitslope ); 3095e42470aSBarry Smith initslope = real(cinitslope); 3105e42470aSBarry Smith #else 3115e42470aSBarry Smith VecDot(f, y, &initslope ); 3125e42470aSBarry Smith #endif 3135e76c431SBarry Smith if (initslope > 0.0) initslope = -initslope; 3145e76c431SBarry Smith if (initslope == 0.0) initslope = -1.0; 3155e76c431SBarry Smith 3165e42470aSBarry Smith VecCopy(y, w ); 3175e42470aSBarry Smith VecAXPY(&one, x, w ); 31878b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 3195e42470aSBarry Smith VecNorm(g, gnorm ); 3205e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 3215e42470aSBarry Smith VecCopy(w, y ); 3225e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 323d93a2b8dSBarry Smith goto theend; 3245e76c431SBarry Smith } 3255e76c431SBarry Smith 3265e76c431SBarry Smith /* Fit points with quadratic */ 3275e76c431SBarry Smith lambda = 1.0; count = 0; 3285e76c431SBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 3295e76c431SBarry Smith lambdaprev = lambda; 3305e76c431SBarry Smith gnormprev = *gnorm; 3315e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3325e76c431SBarry Smith lambda = .1*lambda; 3335e76c431SBarry Smith } else lambda = lambdatemp; 3345e42470aSBarry Smith VecCopy(x, w ); 3355e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3365e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 3375e42470aSBarry Smith #else 3385e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3395e42470aSBarry Smith #endif 34078b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 3415e42470aSBarry Smith VecNorm(g, gnorm ); 3425e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 3435e42470aSBarry Smith VecCopy(w, y ); 3445e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 345d93a2b8dSBarry Smith goto theend; 3465e76c431SBarry Smith } 3475e76c431SBarry Smith 3485e76c431SBarry Smith /* Fit points with cubic */ 3495e76c431SBarry Smith count = 1; 3505e76c431SBarry Smith while (1) { 3515e76c431SBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 3525e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 3535e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 3545e76c431SBarry Smith fnorm,*gnorm, *ynorm,lambda); 3555e42470aSBarry Smith VecCopy(w, y ); 356761aaf1bSLois Curfman McInnes *flag = -1; break; 3575e76c431SBarry Smith } 3585e76c431SBarry Smith t1 = *gnorm - fnorm - lambda*initslope; 3595e76c431SBarry Smith t2 = gnormprev - fnorm - lambdaprev*initslope; 3605e76c431SBarry Smith a = (t1/(lambda*lambda) - 3615e76c431SBarry Smith t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3625e76c431SBarry Smith b = (-lambdaprev*t1/(lambda*lambda) + 3635e76c431SBarry Smith lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3645e76c431SBarry Smith d = b*b - 3*a*initslope; 3655e76c431SBarry Smith if (d < 0.0) d = 0.0; 3665e76c431SBarry Smith if (a == 0.0) { 3675e76c431SBarry Smith lambdatemp = -initslope/(2.0*b); 3685e76c431SBarry Smith } else { 3695e76c431SBarry Smith lambdatemp = (-b + sqrt(d))/(3.0*a); 3705e76c431SBarry Smith } 3715e76c431SBarry Smith if (lambdatemp > .5*lambda) { 3725e76c431SBarry Smith lambdatemp = .5*lambda; 3735e76c431SBarry Smith } 3745e76c431SBarry Smith lambdaprev = lambda; 3755e76c431SBarry Smith gnormprev = *gnorm; 3765e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3775e76c431SBarry Smith lambda = .1*lambda; 3785e76c431SBarry Smith } 3795e76c431SBarry Smith else lambda = lambdatemp; 3805e42470aSBarry Smith VecCopy( x, w ); 3815e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3825e42470aSBarry Smith VecAXPY(&clambda, y, w ); 3835e42470aSBarry Smith #else 3845e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3855e42470aSBarry Smith #endif 38678b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 3875e42470aSBarry Smith VecNorm(g, gnorm ); 3885e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* is reduction enough */ 3895e42470aSBarry Smith VecCopy(w, y ); 3905e42470aSBarry Smith PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda); 391761aaf1bSLois Curfman McInnes *flag = -1; break; 3925e76c431SBarry Smith } 3935e76c431SBarry Smith count++; 3945e76c431SBarry Smith } 395d93a2b8dSBarry Smith theend: 3967857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3975e42470aSBarry Smith return 0; 3985e76c431SBarry Smith } 3995e42470aSBarry Smith /*@ 4005e42470aSBarry Smith SNESQuadraticLineSearch - This routine performs a cubic line search. 4015e76c431SBarry Smith 4025e42470aSBarry Smith Input Parameters: 403f59ffdedSLois Curfman McInnes . snes - the SNES context 4045e42470aSBarry Smith . x - current iterate 4055e42470aSBarry Smith . f - residual evaluated at x 4065e42470aSBarry Smith . y - search direction (contains new iterate on output) 4075e42470aSBarry Smith . w - work vector 4085e42470aSBarry Smith . fnorm - 2-norm of f 4095e42470aSBarry Smith 410c4a48953SLois Curfman McInnes Output Parameters: 4115e42470aSBarry Smith . g - residual evaluated at new iterate y 4125e42470aSBarry Smith . y - new iterate (contains search direction on input) 4135e42470aSBarry Smith . gnorm - 2-norm of g 4145e42470aSBarry Smith . ynorm - 2-norm of search length 415761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 4165e42470aSBarry Smith 417c4a48953SLois Curfman McInnes Options Database Key: 418c4a48953SLois Curfman McInnes $ -snes_line_search quadratic 419c4a48953SLois Curfman McInnes 4205e42470aSBarry Smith Notes: 421f59ffdedSLois Curfman McInnes Use SNESSetLineSearchRoutine() 422f59ffdedSLois Curfman McInnes to set this routine within the SNES_NLS method. 4235e42470aSBarry Smith 424f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search 425f59ffdedSLois Curfman McInnes 426f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 4275e42470aSBarry Smith @*/ 4285e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 429761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag) 4305e76c431SBarry Smith { 4315e42470aSBarry Smith double steptol, initslope; 4325e42470aSBarry Smith double lambdaprev, gnormprev; 4336b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 4345e42470aSBarry Smith Scalar cinitslope,clambda; 4356b5873e3SBarry Smith #endif 4365e42470aSBarry Smith int ierr,count; 4375e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 4385e42470aSBarry Smith Scalar one = 1.0,scale; 4395e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 4405e76c431SBarry Smith 4417857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 442761aaf1bSLois Curfman McInnes *flag = 0; 4435e42470aSBarry Smith alpha = neP->alpha; 4445e42470aSBarry Smith maxstep = neP->maxstep; 4455e42470aSBarry Smith steptol = neP->steptol; 4465e76c431SBarry Smith 4475e42470aSBarry Smith VecNorm(y, ynorm ); 4485e42470aSBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 4495e42470aSBarry Smith scale = maxstep/(*ynorm); 4505e42470aSBarry Smith VecScale(&scale, y ); 4515e42470aSBarry Smith *ynorm = maxstep; 4525e76c431SBarry Smith } 4535e42470aSBarry Smith minlambda = steptol/(*ynorm); 4545e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4555e42470aSBarry Smith VecDot(f, y, &cinitslope ); 4565e42470aSBarry Smith initslope = real(cinitslope); 4575e42470aSBarry Smith #else 4585e42470aSBarry Smith VecDot(f, y, &initslope ); 4595e42470aSBarry Smith #endif 4605e42470aSBarry Smith if (initslope > 0.0) initslope = -initslope; 4615e42470aSBarry Smith if (initslope == 0.0) initslope = -1.0; 4625e42470aSBarry Smith 4635e42470aSBarry Smith VecCopy(y, w ); 4645e42470aSBarry Smith VecAXPY(&one, x, w ); 46578b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 4665e42470aSBarry Smith VecNorm(g, gnorm ); 4675e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 4685e42470aSBarry Smith VecCopy(w, y ); 4695e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 470d93a2b8dSBarry Smith goto theend; 4715e42470aSBarry Smith } 4725e42470aSBarry Smith 4735e42470aSBarry Smith /* Fit points with quadratic */ 4745e42470aSBarry Smith lambda = 1.0; count = 0; 4755e42470aSBarry Smith count = 1; 4765e42470aSBarry Smith while (1) { 4775e42470aSBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 4785e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 4795e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 4805e42470aSBarry Smith fnorm,*gnorm, *ynorm,lambda); 4815e42470aSBarry Smith VecCopy(w, y ); 482761aaf1bSLois Curfman McInnes *flag = -1; break; 4835e42470aSBarry Smith } 4845e42470aSBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 4855e42470aSBarry Smith lambdaprev = lambda; 4865e42470aSBarry Smith gnormprev = *gnorm; 4875e42470aSBarry Smith if (lambdatemp <= .1*lambda) { 4885e42470aSBarry Smith lambda = .1*lambda; 4895e42470aSBarry Smith } else lambda = lambdatemp; 4905e42470aSBarry Smith VecCopy(x, w ); 4915e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4925e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 4935e42470aSBarry Smith #else 4945e42470aSBarry Smith VecAXPY(&lambda, y, w ); 4955e42470aSBarry Smith #endif 49678b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 4975e42470aSBarry Smith VecNorm(g, gnorm ); 4985e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 4995e42470aSBarry Smith VecCopy(w, y ); 5005e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 50106259719SBarry Smith break; 5025e42470aSBarry Smith } 5035e42470aSBarry Smith count++; 5045e42470aSBarry Smith } 505d93a2b8dSBarry Smith theend: 5067857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 5075e42470aSBarry Smith return 0; 5085e76c431SBarry Smith } 5095e76c431SBarry Smith /* ------------------------------------------------------------ */ 510b1f0a012SBarry Smith /*@ 5115e42470aSBarry Smith SNESSetLineSearchRoutine - Sets the line search routine to be used 512fbe28522SBarry Smith by the method SNES_LS. 5135e76c431SBarry Smith 5145e76c431SBarry Smith Input Parameters: 515eafb4bcbSBarry Smith . snes - nonlinear context obtained from SNESCreate() 5165e76c431SBarry Smith . func - pointer to int function 5175e76c431SBarry Smith 518c4a48953SLois Curfman McInnes Available Routines: 519f59ffdedSLois Curfman McInnes . SNESCubicLineSearch() - default line search 520f59ffdedSLois Curfman McInnes . SNESQuadraticLineSearch() - quadratic line search 521f59ffdedSLois Curfman McInnes . SNESNoLineSearch() - the full Newton step (actually not a line search) 5225e76c431SBarry Smith 523c4a48953SLois Curfman McInnes Options Database Keys: 524c4a48953SLois Curfman McInnes $ -snes_line_search [basic,quadratic,cubic] 525c4a48953SLois Curfman McInnes 5265e76c431SBarry Smith Calling sequence of func: 527f59ffdedSLois Curfman McInnes func (SNES snes, Vec x, Vec f, Vec g, Vec y, 528761aaf1bSLois Curfman McInnes Vec w, double fnorm, double *ynorm, 529761aaf1bSLois Curfman McInnes double *gnorm, *flag) 5305e76c431SBarry Smith 5315e76c431SBarry Smith Input parameters for func: 5325e42470aSBarry Smith . snes - nonlinear context 5335e76c431SBarry Smith . x - current iterate 5345e76c431SBarry Smith . f - residual evaluated at x 5355e76c431SBarry Smith . y - search direction (contains new iterate on output) 5365e76c431SBarry Smith . w - work vector 5375e76c431SBarry Smith . fnorm - 2-norm of f 5385e76c431SBarry Smith 5395e76c431SBarry Smith Output parameters for func: 5405e76c431SBarry Smith . g - residual evaluated at new iterate y 5415e76c431SBarry Smith . y - new iterate (contains search direction on input) 5425e76c431SBarry Smith . gnorm - 2-norm of g 5435e76c431SBarry Smith . ynorm - 2-norm of search length 544761aaf1bSLois Curfman McInnes . flag - set to 0 if the line search succeeds; a nonzero integer 545761aaf1bSLois Curfman McInnes on failure. 546f59ffdedSLois Curfman McInnes 547f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine 548f59ffdedSLois Curfman McInnes 549f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch() 5505e76c431SBarry Smith @*/ 5515e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 552761aaf1bSLois Curfman McInnes double,double *,double*,int*) ) 5535e76c431SBarry Smith { 554fbe28522SBarry Smith if ((snes)->type == SNES_NLS) 5555e42470aSBarry Smith ((SNES_LS *)(snes->data))->LineSearch = func; 5565e42470aSBarry Smith return 0; 5575e76c431SBarry Smith } 5585e42470aSBarry Smith 5595e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes) 5605e42470aSBarry Smith { 5615e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5625e42470aSBarry Smith fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n"); 5635e42470aSBarry Smith fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha); 5645e42470aSBarry Smith fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep); 5655e42470aSBarry Smith fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol); 5666b5873e3SBarry Smith return 0; 5675e42470aSBarry Smith } 5685e42470aSBarry Smith 569a935fc98SLois Curfman McInnes static int SNESView_LS(PetscObject obj,Viewer viewer) 570a935fc98SLois Curfman McInnes { 571a935fc98SLois Curfman McInnes SNES snes = (SNES)obj; 572a935fc98SLois Curfman McInnes SNES_LS *ls = (SNES_LS *)snes->data; 573a935fc98SLois Curfman McInnes FILE *fd = ViewerFileGetPointer_Private(viewer); 574a935fc98SLois Curfman McInnes char *cstring; 575a935fc98SLois Curfman McInnes 576a935fc98SLois Curfman McInnes if (ls->LineSearch == SNESNoLineSearch) 577a935fc98SLois Curfman McInnes cstring = "SNESNoLineSearch"; 578a935fc98SLois Curfman McInnes else if (ls->LineSearch == SNESQuadraticLineSearch) 579a935fc98SLois Curfman McInnes cstring = "SNESQuadraticLineSearch"; 580a935fc98SLois Curfman McInnes else if (ls->LineSearch == SNESCubicLineSearch) 581a935fc98SLois Curfman McInnes cstring = "SNESCubicLineSearch"; 582a935fc98SLois Curfman McInnes else 583a935fc98SLois Curfman McInnes cstring = "unknown"; 584a935fc98SLois Curfman McInnes MPIU_fprintf(snes->comm,fd," line search variant: %s\n",cstring); 585a935fc98SLois Curfman McInnes MPIU_fprintf(snes->comm,fd," alpha=%g, maxstep=%g, steptol=%g\n", 586a935fc98SLois Curfman McInnes ls->alpha,ls->maxstep,ls->steptol); 587a935fc98SLois Curfman McInnes return 0; 588a935fc98SLois Curfman McInnes } 589a935fc98SLois Curfman McInnes 5905e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes) 5915e42470aSBarry Smith { 5925e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5935e42470aSBarry Smith char ver[16]; 5945e42470aSBarry Smith double tmp; 5955e42470aSBarry Smith 596df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) { 5975e42470aSBarry Smith ls->alpha = tmp; 5985e42470aSBarry Smith } 599df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) { 6005e42470aSBarry Smith ls->maxstep = tmp; 6015e42470aSBarry Smith } 602df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) { 6035e42470aSBarry Smith ls->steptol = tmp; 6045e42470aSBarry Smith } 605df60cc22SBarry Smith if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) { 6065e42470aSBarry Smith if (!strcmp(ver,"basic")) { 6075e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESNoLineSearch); 6085e42470aSBarry Smith } 6095e42470aSBarry Smith else if (!strcmp(ver,"quadratic")) { 6105e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch); 6115e42470aSBarry Smith } 6125e42470aSBarry Smith else if (!strcmp(ver,"cubic")) { 6135e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESCubicLineSearch); 6145e42470aSBarry Smith } 6158c48e012SBarry Smith else {SETERRQ(1,"SNESSetFromOptions_LS: Unknown line search");} 6165e42470aSBarry Smith } 6175e42470aSBarry Smith return 0; 6185e42470aSBarry Smith } 6195e42470aSBarry Smith 6205e42470aSBarry Smith /* ------------------------------------------------------------ */ 6215e42470aSBarry Smith int SNESCreate_LS(SNES snes ) 6225e42470aSBarry Smith { 6235e42470aSBarry Smith SNES_LS *neP; 6245e42470aSBarry Smith 625fbe28522SBarry Smith snes->type = SNES_NLS; 626a935fc98SLois Curfman McInnes snes->method_class = SNES_T; 62749e3953dSBarry Smith snes->setup = SNESSetUp_LS; 62849e3953dSBarry Smith snes->solve = SNESSolve_LS; 6295e42470aSBarry Smith snes->destroy = SNESDestroy_LS; 6305e42470aSBarry Smith snes->Converged = SNESDefaultConverged; 63149e3953dSBarry Smith snes->printhelp = SNESPrintHelp_LS; 63249e3953dSBarry Smith snes->setfromoptions = SNESSetFromOptions_LS; 633a935fc98SLois Curfman McInnes snes->view = SNESView_LS; 6345e42470aSBarry Smith 63578b31e54SBarry Smith neP = PETSCNEW(SNES_LS); CHKPTRQ(neP); 6365e42470aSBarry Smith snes->data = (void *) neP; 6375e42470aSBarry Smith neP->alpha = 1.e-4; 6385e42470aSBarry Smith neP->maxstep = 1.e8; 6395e42470aSBarry Smith neP->steptol = 1.e-12; 6405e42470aSBarry Smith neP->LineSearch = SNESCubicLineSearch; 6415e42470aSBarry Smith return 0; 6425e42470aSBarry Smith } 6435e42470aSBarry Smith 6445e42470aSBarry Smith 6455e42470aSBarry Smith 6465e42470aSBarry Smith 647