15e76c431SBarry Smith #ifndef lint 2*614e12f8SBarry Smith static char vcid[] = "$Id: ls.c,v 1.20 1995/05/30 22:52:06 bsmith Exp bsmith $"; 35e76c431SBarry Smith #endif 45e76c431SBarry Smith 55e76c431SBarry Smith #include <math.h> 6fbe28522SBarry Smith #include "ls.h" 75e42470aSBarry Smith 85e42470aSBarry Smith /* 95e42470aSBarry Smith Implements a line search variant of Newton's Method 105e76c431SBarry Smith for solving systems of nonlinear equations. 115e76c431SBarry Smith 125e76c431SBarry Smith Input parameters: 135e42470aSBarry Smith . snes - nonlinear context obtained from SNESCreate() 145e76c431SBarry Smith 155e42470aSBarry Smith Output Parameters: 165e42470aSBarry Smith . its - Number of global iterations until termination. 175e76c431SBarry Smith 185e76c431SBarry Smith Notes: 195e42470aSBarry Smith See SNESCreate() and SNESSetUp() for information on the definition and 205e76c431SBarry Smith initialization of the nonlinear solver context. 215e76c431SBarry Smith 225e76c431SBarry Smith This implements essentially a truncated Newton method with a 235e76c431SBarry Smith line search. By default a cubic backtracking line search 245e76c431SBarry Smith is employed, as described in the text "Numerical Methods for 255e76c431SBarry Smith Unconstrained Optimization and Nonlinear Equations" by Dennis 265e42470aSBarry Smith and Schnabel. See the examples in src/snes/examples. 275e76c431SBarry Smith */ 285e76c431SBarry Smith 295e42470aSBarry Smith int SNESSolve_LS( SNES snes, int *outits ) 305e76c431SBarry Smith { 315e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 32edd2f0e1SBarry Smith int maxits, i, history_len,ierr,lits; 33df60cc22SBarry Smith MatStructure flg = ALLMAT_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 */ 385e42470aSBarry Smith history_len = snes->conv_hist_len; /* 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 465e42470aSBarry Smith ierr = SNESComputeInitialGuess(snes,X); CHKERR(ierr); /* X <- X_0 */ 475e42470aSBarry Smith VecNorm( X, &xnorm ); /* xnorm = || X || */ 48a9581db2SBarry Smith ierr = SNESComputeFunction(snes,X,F); CHKERR(ierr); /* (+/-) F(X) */ 495e42470aSBarry Smith VecNorm(F, &fnorm ); /* fnorm <- || F || */ 505e42470aSBarry Smith snes->norm = fnorm; 515e76c431SBarry Smith if (history && history_len > 0) history[0] = fnorm; 52eafb4bcbSBarry Smith if (snes->Monitor) (*snes->Monitor)(snes,0,fnorm,snes->monP); 535e76c431SBarry Smith 545e76c431SBarry Smith for ( i=0; i<maxits; i++ ) { 555e42470aSBarry Smith snes->iter = i+1; 565e76c431SBarry Smith 575e76c431SBarry Smith /* Solve J Y = -F, where J is Jacobian matrix */ 58df60cc22SBarry Smith ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre, 59df60cc22SBarry Smith &flg,snes->jacP); CHKERR(ierr); 6023242f5aSBarry Smith ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg); 615e42470aSBarry Smith ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERR(ierr); 625e42470aSBarry Smith ierr = (*neP->LineSearch)(snes, X, F, G, Y, W, fnorm, &ynorm, &gnorm ); 6323242f5aSBarry Smith CHKERR(ierr); 645e76c431SBarry Smith 6539e2f89bSBarry Smith TMP = F; F = G; snes->vec_func_always = F; G = TMP; 6639e2f89bSBarry Smith TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP; 675e76c431SBarry Smith fnorm = gnorm; 685e76c431SBarry Smith 695e42470aSBarry Smith snes->norm = fnorm; 705e76c431SBarry Smith if (history && history_len > i+1) history[i+1] = fnorm; 715e42470aSBarry Smith VecNorm( X, &xnorm ); /* xnorm = || X || */ 72eafb4bcbSBarry Smith if (snes->Monitor) (*snes->Monitor)(snes,i+1,fnorm,snes->monP); 735e76c431SBarry Smith 745e76c431SBarry Smith /* Test for convergence */ 755e42470aSBarry Smith if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) { 7639e2f89bSBarry Smith if (X != snes->vec_sol) { 7739e2f89bSBarry Smith VecCopy( X, snes->vec_sol ); 7839e2f89bSBarry Smith snes->vec_sol_always = snes->vec_sol; 7939e2f89bSBarry Smith snes->vec_func_always = snes->vec_func; 8039e2f89bSBarry Smith } 815e76c431SBarry Smith break; 825e76c431SBarry Smith } 835e76c431SBarry Smith } 845e76c431SBarry Smith if (i == maxits) i--; 855e42470aSBarry Smith *outits = i+1; 865e42470aSBarry Smith return 0; 875e76c431SBarry Smith } 885e76c431SBarry Smith /* ------------------------------------------------------------ */ 895e42470aSBarry Smith int SNESSetUp_LS(SNES snes ) 905e76c431SBarry Smith { 915e42470aSBarry Smith int ierr; 925e42470aSBarry Smith snes->nwork = 3; 935e42470aSBarry Smith ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr); 945e42470aSBarry Smith PLogObjectParents(snes,snes->nwork,snes->work ); 955e42470aSBarry Smith return 0; 965e76c431SBarry Smith } 975e76c431SBarry Smith /* ------------------------------------------------------------ */ 985e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj) 995e76c431SBarry Smith { 1005e42470aSBarry Smith SNES snes = (SNES) obj; 1015e42470aSBarry Smith VecFreeVecs(snes->work, snes->nwork ); 102df60cc22SBarry Smith FREE(snes->data); 1035e42470aSBarry Smith return 0; 1045e76c431SBarry Smith } 1055e42470aSBarry Smith /*@ 1060de89847SLois Curfman McInnes SNESDefaultMonitor - Default SNES monitoring routine. Prints the 1075e42470aSBarry Smith residual norm at each iteration. 1085e42470aSBarry Smith 1095e42470aSBarry Smith Input Parameters: 1100de89847SLois Curfman McInnes . snes - the SNES context 1110de89847SLois Curfman McInnes . its - iteration number 1120de89847SLois Curfman McInnes . fnorm - 2-norm residual value (may be estimated) 1130de89847SLois Curfman McInnes . dummy - unused context 1145e42470aSBarry Smith 1155e42470aSBarry Smith Notes: 1165e42470aSBarry Smith f is either the residual or its negative, depending on the user's 117a9581db2SBarry Smith preference, as set with SNESSetFunction(). 118a67c8522SLois Curfman McInnes 119a67c8522SLois Curfman McInnes .keywords: SNES, nonlinear, default, monitor, residual, norm 120a67c8522SLois Curfman McInnes 121a67c8522SLois Curfman McInnes .seealso: SNESSetMonitor() 1225e42470aSBarry Smith @*/ 123eafb4bcbSBarry Smith int SNESDefaultMonitor(SNES snes,int its, double fnorm,void *dummy) 1245e42470aSBarry Smith { 125*614e12f8SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm); 126*614e12f8SBarry Smith return 0; 127*614e12f8SBarry Smith } 128*614e12f8SBarry Smith 129*614e12f8SBarry Smith int SNESDefaultSMonitor(SNES snes,int its, double fnorm,void *dummy) 130*614e12f8SBarry Smith { 131*614e12f8SBarry Smith if (fnorm > 1.e-9 || fnorm == 0.0) { 1327f813858SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm); 13369e4de80SBarry Smith } 134*614e12f8SBarry Smith else if (fnorm > 1.e-11){ 13569e4de80SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm %5.3e \n",its,fnorm); 13669e4de80SBarry Smith } 137*614e12f8SBarry Smith else { 138*614e12f8SBarry Smith MPIU_printf(snes->comm, "iter = %d, Function norm < 1.e-11\n",its); 139*614e12f8SBarry Smith } 1405e42470aSBarry Smith return 0; 1415e42470aSBarry Smith } 1425e42470aSBarry Smith 1435e42470aSBarry Smith /*@ 1445e42470aSBarry Smith SNESDefaultConverged - Default test for monitoring the convergence 1450de89847SLois Curfman McInnes of the SNES solvers. 1465e42470aSBarry Smith 1475e42470aSBarry Smith Input Parameters: 1480de89847SLois Curfman McInnes . snes - the SNES context 1495e42470aSBarry Smith . xnorm - 2-norm of current iterate 1505e42470aSBarry Smith . pnorm - 2-norm of current step 1515e42470aSBarry Smith . fnorm - 2-norm of residual 1520de89847SLois Curfman McInnes . dummy - unused context 1535e42470aSBarry Smith 1545e42470aSBarry Smith Returns: 1555e42470aSBarry Smith $ 2 if ( fnorm < atol ), 1565e42470aSBarry Smith $ 3 if ( pnorm < xtol*xnorm ), 1575e42470aSBarry Smith $ -2 if ( nres > max_res ), 1585e42470aSBarry Smith $ 0 otherwise, 1595e42470aSBarry Smith 1605e42470aSBarry Smith where 1615e42470aSBarry Smith $ atol - absolute residual norm tolerance, 1620de89847SLois Curfman McInnes $ set with SNESSetAbsoluteTolerance() 1635e42470aSBarry Smith $ max_res - maximum number of residual evaluations, 1640de89847SLois Curfman McInnes $ set with SNESSetMaxResidualEvaluations() 1655e42470aSBarry Smith $ nres - number of residual evaluations 1665e42470aSBarry Smith $ xtol - relative residual norm tolerance, 1670de89847SLois Curfman McInnes $ set with SNESSetRelativeTolerance() 1685e42470aSBarry Smith 1690de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence 1705e42470aSBarry Smith 1710de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest() 1725e42470aSBarry Smith @*/ 1735e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm, 1745e42470aSBarry Smith void *dummy) 1755e42470aSBarry Smith { 1765e42470aSBarry Smith if (fnorm < snes->atol) { 177a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 178a67c8522SLois Curfman McInnes "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol); 1795e42470aSBarry Smith return 2; 1805e42470aSBarry Smith } 1815e42470aSBarry Smith if (pnorm < snes->xtol*(xnorm)) { 182a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 183a67c8522SLois Curfman McInnes "Converged due to small update length %g < %g*%g\n", 1845e42470aSBarry Smith pnorm,snes->xtol,xnorm); 1855e42470aSBarry Smith return 3; 1865e42470aSBarry Smith } 18749e3953dSBarry Smith if (snes->nfuncs > snes->max_funcs) { 188a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 189a67c8522SLois Curfman McInnes "Exceeded maximum number of residual evaluations: %d > %d\n", 19049e3953dSBarry Smith snes->nfuncs, snes->max_funcs ); 191a67c8522SLois Curfman McInnes return -2; 192a67c8522SLois Curfman McInnes } 1935e42470aSBarry Smith return 0; 1945e42470aSBarry Smith } 1955e42470aSBarry Smith 1965e76c431SBarry Smith /* ------------------------------------------------------------ */ 1975e76c431SBarry Smith /*ARGSUSED*/ 1985e76c431SBarry Smith /*@ 1995e42470aSBarry Smith SNESNoLineSearch - This routine is not a line search at all; 2005e76c431SBarry Smith it simply uses the full Newton step. Thus, this routine is intended 2015e76c431SBarry Smith to serve as a template and is not recommended for general use. 2025e76c431SBarry Smith 2035e76c431SBarry Smith Input Parameters: 2045e42470aSBarry Smith . snes - nonlinear context 2055e76c431SBarry Smith . x - current iterate 2065e76c431SBarry Smith . f - residual evaluated at x 2075e76c431SBarry Smith . y - search direction (contains new iterate on output) 2085e76c431SBarry Smith . w - work vector 2095e76c431SBarry Smith . fnorm - 2-norm of f 2105e76c431SBarry Smith 211c4a48953SLois Curfman McInnes Output Parameters: 2125e76c431SBarry Smith . g - residual evaluated at new iterate y 2135e76c431SBarry Smith . y - new iterate (contains search direction on input) 2145e76c431SBarry Smith . gnorm - 2-norm of g 2155e76c431SBarry Smith . ynorm - 2-norm of search length 2165e76c431SBarry Smith 217c4a48953SLois Curfman McInnes Options Database Key: 218c4a48953SLois Curfman McInnes $ -snes_line_search basic 219c4a48953SLois Curfman McInnes 2205e76c431SBarry Smith Returns: 2215e76c431SBarry Smith 1, indicating success of the step. 2225e76c431SBarry Smith 22328ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 22428ae5a21SLois Curfman McInnes 225f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 226f59ffdedSLois Curfman McInnes .seealso: SNESSetLineSearchRoutine() 2275e76c431SBarry Smith @*/ 2285e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 2295e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 2305e76c431SBarry Smith { 2315e42470aSBarry Smith int ierr; 2325e42470aSBarry Smith Scalar one = 1.0; 2337857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 2345e42470aSBarry Smith VecNorm(y, ynorm ); /* ynorm = || y || */ 2355e42470aSBarry Smith VecAXPY(&one, x, y ); /* y <- x + y */ 236a9581db2SBarry Smith ierr = SNESComputeFunction(snes,y,g); CHKERR(ierr); 2375e42470aSBarry Smith VecNorm( g, gnorm ); /* gnorm = || g || */ 2387857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 2395e76c431SBarry Smith return 1; 2405e76c431SBarry Smith } 2415e76c431SBarry Smith /* ------------------------------------------------------------------ */ 2425e76c431SBarry Smith /*@ 243f59ffdedSLois Curfman McInnes SNESCubicLineSearch - This routine performs a cubic line search and 244f59ffdedSLois Curfman McInnes is the default line search method. 2455e76c431SBarry Smith 2465e76c431SBarry Smith Input Parameters: 2475e42470aSBarry Smith . snes - nonlinear context 2485e76c431SBarry Smith . x - current iterate 2495e76c431SBarry Smith . f - residual evaluated at x 2505e76c431SBarry Smith . y - search direction (contains new iterate on output) 2515e76c431SBarry Smith . w - work vector 2525e76c431SBarry Smith . fnorm - 2-norm of f 2535e76c431SBarry Smith 2545e76c431SBarry Smith Output parameters: 2555e76c431SBarry Smith . g - residual evaluated at new iterate y 2565e76c431SBarry Smith . y - new iterate (contains search direction on input) 2575e76c431SBarry Smith . gnorm - 2-norm of g 2585e76c431SBarry Smith . ynorm - 2-norm of search length 2595e76c431SBarry Smith 2605e76c431SBarry Smith Returns: 2615e76c431SBarry Smith 1 if the line search succeeds; 0 if the line search fails. 2625e76c431SBarry Smith 263c4a48953SLois Curfman McInnes Options Database Key: 264c4a48953SLois Curfman McInnes $ -snes_line_search cubic 265c4a48953SLois Curfman McInnes 2665e76c431SBarry Smith Notes: 2675e76c431SBarry Smith This line search is taken from "Numerical Methods for Unconstrained 2685e76c431SBarry Smith Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 2695e76c431SBarry Smith 27028ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 27128ae5a21SLois Curfman McInnes 272f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 2735e76c431SBarry Smith @*/ 2745e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 2755e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 2765e76c431SBarry Smith { 2775e42470aSBarry Smith double steptol, initslope; 2785e42470aSBarry Smith double lambdaprev, gnormprev; 2795e76c431SBarry Smith double a, b, d, t1, t2; 2806b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2815e42470aSBarry Smith Scalar cinitslope,clambda; 2826b5873e3SBarry Smith #endif 2835e42470aSBarry Smith int ierr,count; 2845e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 2855e42470aSBarry Smith Scalar one = 1.0,scale; 2865e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 2875e76c431SBarry Smith 2887857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 2895e76c431SBarry Smith alpha = neP->alpha; 2905e76c431SBarry Smith maxstep = neP->maxstep; 2915e76c431SBarry Smith steptol = neP->steptol; 2925e76c431SBarry Smith 2935e42470aSBarry Smith VecNorm(y, ynorm ); 2945e76c431SBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 2955e42470aSBarry Smith scale = maxstep/(*ynorm); 2966b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2976b5873e3SBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale)); 2986b5873e3SBarry Smith #else 2995e42470aSBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale); 3006b5873e3SBarry Smith #endif 3015e42470aSBarry Smith VecScale(&scale, y ); 3025e76c431SBarry Smith *ynorm = maxstep; 3035e76c431SBarry Smith } 3045e76c431SBarry Smith minlambda = steptol/(*ynorm); 3055e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3065e42470aSBarry Smith VecDot(f, y, &cinitslope ); 3075e42470aSBarry Smith initslope = real(cinitslope); 3085e42470aSBarry Smith #else 3095e42470aSBarry Smith VecDot(f, y, &initslope ); 3105e42470aSBarry Smith #endif 3115e76c431SBarry Smith if (initslope > 0.0) initslope = -initslope; 3125e76c431SBarry Smith if (initslope == 0.0) initslope = -1.0; 3135e76c431SBarry Smith 3145e42470aSBarry Smith VecCopy(y, w ); 3155e42470aSBarry Smith VecAXPY(&one, x, w ); 316a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 3175e42470aSBarry Smith VecNorm(g, gnorm ); 3185e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 3195e42470aSBarry Smith VecCopy(w, y ); 3205e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 3217857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3225e42470aSBarry Smith return 0; 3235e76c431SBarry Smith } 3245e76c431SBarry Smith 3255e76c431SBarry Smith /* Fit points with quadratic */ 3265e76c431SBarry Smith lambda = 1.0; count = 0; 3275e76c431SBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 3285e76c431SBarry Smith lambdaprev = lambda; 3295e76c431SBarry Smith gnormprev = *gnorm; 3305e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3315e76c431SBarry Smith lambda = .1*lambda; 3325e76c431SBarry Smith } else lambda = lambdatemp; 3335e42470aSBarry Smith VecCopy(x, w ); 3345e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3355e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 3365e42470aSBarry Smith #else 3375e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3385e42470aSBarry Smith #endif 339a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 3405e42470aSBarry Smith VecNorm(g, gnorm ); 3415e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 3425e42470aSBarry Smith VecCopy(w, y ); 3435e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 3447857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3455e42470aSBarry Smith return 0; 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 ); 3567857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 35723242f5aSBarry Smith return -1; 3585e76c431SBarry Smith } 3595e76c431SBarry Smith t1 = *gnorm - fnorm - lambda*initslope; 3605e76c431SBarry Smith t2 = gnormprev - fnorm - lambdaprev*initslope; 3615e76c431SBarry Smith a = (t1/(lambda*lambda) - 3625e76c431SBarry Smith t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3635e76c431SBarry Smith b = (-lambdaprev*t1/(lambda*lambda) + 3645e76c431SBarry Smith lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3655e76c431SBarry Smith d = b*b - 3*a*initslope; 3665e76c431SBarry Smith if (d < 0.0) d = 0.0; 3675e76c431SBarry Smith if (a == 0.0) { 3685e76c431SBarry Smith lambdatemp = -initslope/(2.0*b); 3695e76c431SBarry Smith } else { 3705e76c431SBarry Smith lambdatemp = (-b + sqrt(d))/(3.0*a); 3715e76c431SBarry Smith } 3725e76c431SBarry Smith if (lambdatemp > .5*lambda) { 3735e76c431SBarry Smith lambdatemp = .5*lambda; 3745e76c431SBarry Smith } 3755e76c431SBarry Smith lambdaprev = lambda; 3765e76c431SBarry Smith gnormprev = *gnorm; 3775e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3785e76c431SBarry Smith lambda = .1*lambda; 3795e76c431SBarry Smith } 3805e76c431SBarry Smith else lambda = lambdatemp; 3815e42470aSBarry Smith VecCopy( x, w ); 3825e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3835e42470aSBarry Smith VecAXPY(&clambda, y, w ); 3845e42470aSBarry Smith #else 3855e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3865e42470aSBarry Smith #endif 387a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 3885e42470aSBarry Smith VecNorm(g, gnorm ); 3895e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* is reduction enough */ 3905e42470aSBarry Smith VecCopy(w, y ); 3915e42470aSBarry Smith PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda); 3927857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3935e42470aSBarry Smith return 0; 3945e76c431SBarry Smith } 3955e76c431SBarry Smith count++; 3965e76c431SBarry Smith } 3977857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3985e42470aSBarry Smith return 0; 3995e76c431SBarry Smith } 4005e42470aSBarry Smith /*@ 4015e42470aSBarry Smith SNESQuadraticLineSearch - This routine performs a cubic line search. 4025e76c431SBarry Smith 4035e42470aSBarry Smith Input Parameters: 404f59ffdedSLois Curfman McInnes . snes - the SNES context 4055e42470aSBarry Smith . x - current iterate 4065e42470aSBarry Smith . f - residual evaluated at x 4075e42470aSBarry Smith . y - search direction (contains new iterate on output) 4085e42470aSBarry Smith . w - work vector 4095e42470aSBarry Smith . fnorm - 2-norm of f 4105e42470aSBarry Smith 411c4a48953SLois Curfman McInnes Output Parameters: 4125e42470aSBarry Smith . g - residual evaluated at new iterate y 4135e42470aSBarry Smith . y - new iterate (contains search direction on input) 4145e42470aSBarry Smith . gnorm - 2-norm of g 4155e42470aSBarry Smith . ynorm - 2-norm of search length 4165e42470aSBarry Smith 4175e42470aSBarry Smith Returns: 4185e42470aSBarry Smith 1 if the line search succeeds; 0 if the line search fails. 4195e42470aSBarry Smith 420c4a48953SLois Curfman McInnes Options Database Key: 421c4a48953SLois Curfman McInnes $ -snes_line_search quadratic 422c4a48953SLois Curfman McInnes 4235e42470aSBarry Smith Notes: 424f59ffdedSLois Curfman McInnes Use SNESSetLineSearchRoutine() 425f59ffdedSLois Curfman McInnes to set this routine within the SNES_NLS method. 4265e42470aSBarry Smith 427f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search 428f59ffdedSLois Curfman McInnes 429f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 4305e42470aSBarry Smith @*/ 4315e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 4325e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 4335e76c431SBarry Smith { 4345e42470aSBarry Smith double steptol, initslope; 4355e42470aSBarry Smith double lambdaprev, gnormprev; 4366b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 4375e42470aSBarry Smith Scalar cinitslope,clambda; 4386b5873e3SBarry Smith #endif 4395e42470aSBarry Smith int ierr,count; 4405e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 4415e42470aSBarry Smith Scalar one = 1.0,scale; 4425e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 4435e76c431SBarry Smith 4447857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 4455e42470aSBarry Smith alpha = neP->alpha; 4465e42470aSBarry Smith maxstep = neP->maxstep; 4475e42470aSBarry Smith steptol = neP->steptol; 4485e76c431SBarry Smith 4495e42470aSBarry Smith VecNorm(y, ynorm ); 4505e42470aSBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 4515e42470aSBarry Smith scale = maxstep/(*ynorm); 4525e42470aSBarry Smith VecScale(&scale, y ); 4535e42470aSBarry Smith *ynorm = maxstep; 4545e76c431SBarry Smith } 4555e42470aSBarry Smith minlambda = steptol/(*ynorm); 4565e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4575e42470aSBarry Smith VecDot(f, y, &cinitslope ); 4585e42470aSBarry Smith initslope = real(cinitslope); 4595e42470aSBarry Smith #else 4605e42470aSBarry Smith VecDot(f, y, &initslope ); 4615e42470aSBarry Smith #endif 4625e42470aSBarry Smith if (initslope > 0.0) initslope = -initslope; 4635e42470aSBarry Smith if (initslope == 0.0) initslope = -1.0; 4645e42470aSBarry Smith 4655e42470aSBarry Smith VecCopy(y, w ); 4665e42470aSBarry Smith VecAXPY(&one, x, w ); 467a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 4685e42470aSBarry Smith VecNorm(g, gnorm ); 4695e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 4705e42470aSBarry Smith VecCopy(w, y ); 4715e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 4727857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4735e42470aSBarry Smith return 0; 4745e42470aSBarry Smith } 4755e42470aSBarry Smith 4765e42470aSBarry Smith /* Fit points with quadratic */ 4775e42470aSBarry Smith lambda = 1.0; count = 0; 4785e42470aSBarry Smith count = 1; 4795e42470aSBarry Smith while (1) { 4805e42470aSBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 4815e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 4825e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 4835e42470aSBarry Smith fnorm,*gnorm, *ynorm,lambda); 4845e42470aSBarry Smith VecCopy(w, y ); 4857857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4865e42470aSBarry Smith return 0; 4875e42470aSBarry Smith } 4885e42470aSBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 4895e42470aSBarry Smith lambdaprev = lambda; 4905e42470aSBarry Smith gnormprev = *gnorm; 4915e42470aSBarry Smith if (lambdatemp <= .1*lambda) { 4925e42470aSBarry Smith lambda = .1*lambda; 4935e42470aSBarry Smith } else lambda = lambdatemp; 4945e42470aSBarry Smith VecCopy(x, w ); 4955e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4965e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 4975e42470aSBarry Smith #else 4985e42470aSBarry Smith VecAXPY(&lambda, y, w ); 4995e42470aSBarry Smith #endif 500a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 5015e42470aSBarry Smith VecNorm(g, gnorm ); 5025e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 5035e42470aSBarry Smith VecCopy(w, y ); 5045e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 5057857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 5065e42470aSBarry Smith return 0; 5075e42470aSBarry Smith } 5085e42470aSBarry Smith count++; 5095e42470aSBarry Smith } 5105e42470aSBarry Smith 5117857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 5125e42470aSBarry Smith return 0; 5135e76c431SBarry Smith } 5145e76c431SBarry Smith /* ------------------------------------------------------------ */ 5155e76c431SBarry Smith /*@C 5165e42470aSBarry Smith SNESSetLineSearchRoutine - Sets the line search routine to be used 517fbe28522SBarry Smith by the method SNES_LS. 5185e76c431SBarry Smith 5195e76c431SBarry Smith Input Parameters: 520eafb4bcbSBarry Smith . snes - nonlinear context obtained from SNESCreate() 5215e76c431SBarry Smith . func - pointer to int function 5225e76c431SBarry Smith 523c4a48953SLois Curfman McInnes Available Routines: 524f59ffdedSLois Curfman McInnes . SNESCubicLineSearch() - default line search 525f59ffdedSLois Curfman McInnes . SNESQuadraticLineSearch() - quadratic line search 526f59ffdedSLois Curfman McInnes . SNESNoLineSearch() - the full Newton step (actually not a line search) 5275e76c431SBarry Smith 528c4a48953SLois Curfman McInnes Options Database Keys: 529c4a48953SLois Curfman McInnes $ -snes_line_search [basic,quadratic,cubic] 530c4a48953SLois Curfman McInnes 5315e76c431SBarry Smith Calling sequence of func: 532f59ffdedSLois Curfman McInnes func (SNES snes, Vec x, Vec f, Vec g, Vec y, 5335e42470aSBarry Smith Vec w, double fnorm, double *ynorm, double *gnorm) 5345e76c431SBarry Smith 5355e76c431SBarry Smith Input parameters for func: 5365e42470aSBarry Smith . snes - nonlinear context 5375e76c431SBarry Smith . x - current iterate 5385e76c431SBarry Smith . f - residual evaluated at x 5395e76c431SBarry Smith . y - search direction (contains new iterate on output) 5405e76c431SBarry Smith . w - work vector 5415e76c431SBarry Smith . fnorm - 2-norm of f 5425e76c431SBarry Smith 5435e76c431SBarry Smith Output parameters for func: 5445e76c431SBarry Smith . g - residual evaluated at new iterate y 5455e76c431SBarry Smith . y - new iterate (contains search direction on input) 5465e76c431SBarry Smith . gnorm - 2-norm of g 5475e76c431SBarry Smith . ynorm - 2-norm of search length 5485e76c431SBarry Smith 5495e76c431SBarry Smith Returned by func: 5505e76c431SBarry Smith 1 if the line search succeeds; 0 if the line search fails. 551f59ffdedSLois Curfman McInnes 552f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine 553f59ffdedSLois Curfman McInnes 554f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch() 5555e76c431SBarry Smith @*/ 5565e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 5575e42470aSBarry Smith double,double *,double*) ) 5585e76c431SBarry Smith { 559fbe28522SBarry Smith if ((snes)->type == SNES_NLS) 5605e42470aSBarry Smith ((SNES_LS *)(snes->data))->LineSearch = func; 5615e42470aSBarry Smith return 0; 5625e76c431SBarry Smith } 5635e42470aSBarry Smith 5645e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes) 5655e42470aSBarry Smith { 5665e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5675e42470aSBarry Smith fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n"); 5685e42470aSBarry Smith fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha); 5695e42470aSBarry Smith fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep); 5705e42470aSBarry Smith fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol); 5716b5873e3SBarry Smith return 0; 5725e42470aSBarry Smith } 5735e42470aSBarry Smith 5745e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes) 5755e42470aSBarry Smith { 5765e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5775e42470aSBarry Smith char ver[16]; 5785e42470aSBarry Smith double tmp; 5795e42470aSBarry Smith 580df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) { 5815e42470aSBarry Smith ls->alpha = tmp; 5825e42470aSBarry Smith } 583df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) { 5845e42470aSBarry Smith ls->maxstep = tmp; 5855e42470aSBarry Smith } 586df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) { 5875e42470aSBarry Smith ls->steptol = tmp; 5885e42470aSBarry Smith } 589df60cc22SBarry Smith if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) { 5905e42470aSBarry Smith if (!strcmp(ver,"basic")) { 5915e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESNoLineSearch); 5925e42470aSBarry Smith } 5935e42470aSBarry Smith else if (!strcmp(ver,"quadratic")) { 5945e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch); 5955e42470aSBarry Smith } 5965e42470aSBarry Smith else if (!strcmp(ver,"cubic")) { 5975e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESCubicLineSearch); 5985e42470aSBarry Smith } 5995e42470aSBarry Smith else {SETERR(1,"Unknown line search?");} 6005e42470aSBarry Smith } 6015e42470aSBarry Smith return 0; 6025e42470aSBarry Smith } 6035e42470aSBarry Smith 6045e42470aSBarry Smith /* ------------------------------------------------------------ */ 6055e42470aSBarry Smith int SNESCreate_LS(SNES snes ) 6065e42470aSBarry Smith { 6075e42470aSBarry Smith SNES_LS *neP; 6085e42470aSBarry Smith 609fbe28522SBarry Smith snes->type = SNES_NLS; 61049e3953dSBarry Smith snes->setup = SNESSetUp_LS; 61149e3953dSBarry Smith snes->solve = SNESSolve_LS; 6125e42470aSBarry Smith snes->destroy = SNESDestroy_LS; 6135e42470aSBarry Smith snes->Converged = SNESDefaultConverged; 61449e3953dSBarry Smith snes->printhelp = SNESPrintHelp_LS; 61549e3953dSBarry Smith snes->setfromoptions = SNESSetFromOptions_LS; 6165e42470aSBarry Smith 6175e42470aSBarry Smith neP = NEW(SNES_LS); CHKPTR(neP); 6185e42470aSBarry Smith snes->data = (void *) neP; 6195e42470aSBarry Smith neP->alpha = 1.e-4; 6205e42470aSBarry Smith neP->maxstep = 1.e8; 6215e42470aSBarry Smith neP->steptol = 1.e-12; 6225e42470aSBarry Smith neP->LineSearch = SNESCubicLineSearch; 6235e42470aSBarry Smith return 0; 6245e42470aSBarry Smith } 6255e42470aSBarry Smith 6265e42470aSBarry Smith 6275e42470aSBarry Smith 6285e42470aSBarry Smith 629