15e76c431SBarry Smith #ifndef lint 2*7857610eSBarry Smith static char vcid[] = "$Id: ls.c,v 1.10 1995/04/19 03:01:24 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; 326b5873e3SBarry Smith int maxits, i, history_len,ierr,lits; 336b5873e3SBarry Smith double fnorm, gnorm, xnorm, ynorm, *history; 345e42470aSBarry Smith Vec Y, X, F, G, W, TMP; 355e76c431SBarry Smith 365e42470aSBarry Smith history = snes->conv_hist; /* convergence history */ 375e42470aSBarry Smith history_len = snes->conv_hist_len; /* convergence history length */ 385e42470aSBarry Smith maxits = snes->max_its; /* maximum number of iterations */ 395e42470aSBarry Smith X = snes->vec_sol; /* solution vector */ 405e42470aSBarry Smith F = snes->vec_res; /* residual vector */ 415e42470aSBarry Smith Y = snes->work[0]; /* work vectors */ 425e42470aSBarry Smith G = snes->work[1]; 435e42470aSBarry Smith W = snes->work[2]; 445e76c431SBarry Smith 455e42470aSBarry Smith ierr = SNESComputeInitialGuess(snes,X); CHKERR(ierr); /* X <- X_0 */ 465e42470aSBarry Smith VecNorm( X, &xnorm ); /* xnorm = || X || */ 47a9581db2SBarry Smith ierr = SNESComputeFunction(snes,X,F); CHKERR(ierr); /* (+/-) F(X) */ 485e42470aSBarry Smith VecNorm(F, &fnorm ); /* fnorm <- || F || */ 495e42470aSBarry Smith snes->norm = fnorm; 505e76c431SBarry Smith if (history && history_len > 0) history[0] = fnorm; 51eafb4bcbSBarry Smith if (snes->Monitor) (*snes->Monitor)(snes,0,fnorm,snes->monP); 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 */ 575e42470aSBarry Smith (*snes->ComputeJacobian)(X,&snes->jacobian,snes->jacP); 585e42470aSBarry Smith ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian,0); 595e42470aSBarry Smith ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERR(ierr); 605e42470aSBarry Smith ierr = (*neP->LineSearch)(snes, X, F, G, Y, W, fnorm, &ynorm, &gnorm ); 615e76c431SBarry Smith 625e76c431SBarry Smith TMP = F; F = G; G = TMP; 635e76c431SBarry Smith TMP = X; X = Y; Y = TMP; 645e76c431SBarry Smith fnorm = gnorm; 655e76c431SBarry Smith 665e42470aSBarry Smith snes->norm = fnorm; 675e76c431SBarry Smith if (history && history_len > i+1) history[i+1] = fnorm; 685e42470aSBarry Smith VecNorm( X, &xnorm ); /* xnorm = || X || */ 69eafb4bcbSBarry Smith if (snes->Monitor) (*snes->Monitor)(snes,i+1,fnorm,snes->monP); 705e76c431SBarry Smith 715e76c431SBarry Smith /* Test for convergence */ 725e42470aSBarry Smith if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) { 735e42470aSBarry Smith if (X != snes->vec_sol) VecCopy( X, snes->vec_sol ); 745e76c431SBarry Smith break; 755e76c431SBarry Smith } 765e76c431SBarry Smith } 775e76c431SBarry Smith if (i == maxits) i--; 785e42470aSBarry Smith *outits = i+1; 795e42470aSBarry Smith return 0; 805e76c431SBarry Smith } 815e76c431SBarry Smith /* ------------------------------------------------------------ */ 825e76c431SBarry Smith /*ARGSUSED*/ 835e42470aSBarry Smith int SNESSetUp_LS(SNES snes ) 845e76c431SBarry Smith { 855e42470aSBarry Smith int ierr; 865e42470aSBarry Smith snes->nwork = 3; 875e42470aSBarry Smith ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr); 885e42470aSBarry Smith PLogObjectParents(snes,snes->nwork,snes->work ); 895e42470aSBarry Smith return 0; 905e76c431SBarry Smith } 915e76c431SBarry Smith /* ------------------------------------------------------------ */ 925e76c431SBarry Smith /*ARGSUSED*/ 935e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj) 945e76c431SBarry Smith { 955e42470aSBarry Smith SNES snes = (SNES) obj; 965e42470aSBarry Smith SLESDestroy(snes->sles); 975e42470aSBarry Smith VecFreeVecs(snes->work, snes->nwork ); 985e42470aSBarry Smith PLogObjectDestroy(obj); 995e42470aSBarry Smith PETSCHEADERDESTROY(obj); 1005e42470aSBarry Smith return 0; 1015e76c431SBarry Smith } 1025e42470aSBarry Smith /*@ 1030de89847SLois Curfman McInnes SNESDefaultMonitor - Default SNES monitoring routine. Prints the 1045e42470aSBarry Smith residual norm at each iteration. 1055e42470aSBarry Smith 1065e42470aSBarry Smith Input Parameters: 1070de89847SLois Curfman McInnes . snes - the SNES context 1080de89847SLois Curfman McInnes . its - iteration number 1090de89847SLois Curfman McInnes . fnorm - 2-norm residual value (may be estimated) 1100de89847SLois Curfman McInnes . dummy - unused context 1115e42470aSBarry Smith 1125e42470aSBarry Smith Notes: 1135e42470aSBarry Smith f is either the residual or its negative, depending on the user's 114a9581db2SBarry Smith preference, as set with SNESSetFunction(). 115a67c8522SLois Curfman McInnes 116a67c8522SLois Curfman McInnes .keywords: SNES, nonlinear, default, monitor, residual, norm 117a67c8522SLois Curfman McInnes 118a67c8522SLois Curfman McInnes .seealso: SNESSetMonitor() 1195e42470aSBarry Smith @*/ 120eafb4bcbSBarry Smith int SNESDefaultMonitor(SNES snes,int its, double fnorm,void *dummy) 1215e42470aSBarry Smith { 1225e42470aSBarry Smith fprintf( stdout, "iter = %d, residual norm %g \n",its,fnorm); 1235e42470aSBarry Smith return 0; 1245e42470aSBarry Smith } 1255e42470aSBarry Smith 1265e42470aSBarry Smith /*@ 1275e42470aSBarry Smith SNESDefaultConverged - Default test for monitoring the convergence 1280de89847SLois Curfman McInnes of the SNES solvers. 1295e42470aSBarry Smith 1305e42470aSBarry Smith Input Parameters: 1310de89847SLois Curfman McInnes . snes - the SNES context 1325e42470aSBarry Smith . xnorm - 2-norm of current iterate 1335e42470aSBarry Smith . pnorm - 2-norm of current step 1345e42470aSBarry Smith . fnorm - 2-norm of residual 1350de89847SLois Curfman McInnes . dummy - unused context 1365e42470aSBarry Smith 1375e42470aSBarry Smith Returns: 1385e42470aSBarry Smith $ 2 if ( fnorm < atol ), 1395e42470aSBarry Smith $ 3 if ( pnorm < xtol*xnorm ), 1405e42470aSBarry Smith $ -2 if ( nres > max_res ), 1415e42470aSBarry Smith $ 0 otherwise, 1425e42470aSBarry Smith 1435e42470aSBarry Smith where 1445e42470aSBarry Smith $ atol - absolute residual norm tolerance, 1450de89847SLois Curfman McInnes $ set with SNESSetAbsoluteTolerance() 1465e42470aSBarry Smith $ max_res - maximum number of residual evaluations, 1470de89847SLois Curfman McInnes $ set with SNESSetMaxResidualEvaluations() 1485e42470aSBarry Smith $ nres - number of residual evaluations 1495e42470aSBarry Smith $ xtol - relative residual norm tolerance, 1500de89847SLois Curfman McInnes $ set with SNESSetRelativeTolerance() 1515e42470aSBarry Smith 1520de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence 1535e42470aSBarry Smith 1540de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest() 1555e42470aSBarry Smith @*/ 1565e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm, 1575e42470aSBarry Smith void *dummy) 1585e42470aSBarry Smith { 1595e42470aSBarry Smith if (fnorm < snes->atol) { 160a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 161a67c8522SLois Curfman McInnes "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol); 1625e42470aSBarry Smith return 2; 1635e42470aSBarry Smith } 1645e42470aSBarry Smith if (pnorm < snes->xtol*(xnorm)) { 165a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 166a67c8522SLois Curfman McInnes "Converged due to small update length %g < %g*%g\n", 1675e42470aSBarry Smith pnorm,snes->xtol,xnorm); 1685e42470aSBarry Smith return 3; 1695e42470aSBarry Smith } 170a67c8522SLois Curfman McInnes if (snes->nresids > snes->max_resids) { 171a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 172a67c8522SLois Curfman McInnes "Exceeded maximum number of residual evaluations: %d > %d\n", 173a67c8522SLois Curfman McInnes snes->nresids, snes->max_resids ); 174a67c8522SLois Curfman McInnes return -2; 175a67c8522SLois Curfman McInnes } 1765e42470aSBarry Smith return 0; 1775e42470aSBarry Smith } 1785e42470aSBarry Smith 1795e76c431SBarry Smith /* ------------------------------------------------------------ */ 1805e76c431SBarry Smith /*ARGSUSED*/ 1815e76c431SBarry Smith /*@ 1825e42470aSBarry Smith SNESNoLineSearch - This routine is not a line search at all; 1835e76c431SBarry Smith it simply uses the full Newton step. Thus, this routine is intended 1845e76c431SBarry Smith to serve as a template and is not recommended for general use. 1855e76c431SBarry Smith 1865e76c431SBarry Smith Input Parameters: 1875e42470aSBarry Smith . snes - nonlinear context 1885e76c431SBarry Smith . x - current iterate 1895e76c431SBarry Smith . f - residual evaluated at x 1905e76c431SBarry Smith . y - search direction (contains new iterate on output) 1915e76c431SBarry Smith . w - work vector 1925e76c431SBarry Smith . fnorm - 2-norm of f 1935e76c431SBarry Smith 1945e76c431SBarry Smith Output parameters: 1955e76c431SBarry Smith . g - residual evaluated at new iterate y 1965e76c431SBarry Smith . y - new iterate (contains search direction on input) 1975e76c431SBarry Smith . gnorm - 2-norm of g 1985e76c431SBarry Smith . ynorm - 2-norm of search length 1995e76c431SBarry Smith 2005e76c431SBarry Smith Returns: 2015e76c431SBarry Smith 1, indicating success of the step. 2025e76c431SBarry Smith 20328ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 20428ae5a21SLois Curfman McInnes 205f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 206f59ffdedSLois Curfman McInnes .seealso: SNESSetLineSearchRoutine() 2075e76c431SBarry Smith @*/ 2085e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 2095e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 2105e76c431SBarry Smith { 2115e42470aSBarry Smith int ierr; 2125e42470aSBarry Smith Scalar one = 1.0; 213*7857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 2145e42470aSBarry Smith VecNorm(y, ynorm ); /* ynorm = || y || */ 2155e42470aSBarry Smith VecAXPY(&one, x, y ); /* y <- x + y */ 216a9581db2SBarry Smith ierr = SNESComputeFunction(snes,y,g); CHKERR(ierr); 2175e42470aSBarry Smith VecNorm( g, gnorm ); /* gnorm = || g || */ 218*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 2195e76c431SBarry Smith return 1; 2205e76c431SBarry Smith } 2215e76c431SBarry Smith /* ------------------------------------------------------------------ */ 2225e76c431SBarry Smith /*@ 223f59ffdedSLois Curfman McInnes SNESCubicLineSearch - This routine performs a cubic line search and 224f59ffdedSLois Curfman McInnes is the default line search method. 2255e76c431SBarry Smith 2265e76c431SBarry Smith Input Parameters: 2275e42470aSBarry Smith . snes - nonlinear context 2285e76c431SBarry Smith . x - current iterate 2295e76c431SBarry Smith . f - residual evaluated at x 2305e76c431SBarry Smith . y - search direction (contains new iterate on output) 2315e76c431SBarry Smith . w - work vector 2325e76c431SBarry Smith . fnorm - 2-norm of f 2335e76c431SBarry Smith 2345e76c431SBarry Smith Output parameters: 2355e76c431SBarry Smith . g - residual evaluated at new iterate y 2365e76c431SBarry Smith . y - new iterate (contains search direction on input) 2375e76c431SBarry Smith . gnorm - 2-norm of g 2385e76c431SBarry Smith . ynorm - 2-norm of search length 2395e76c431SBarry Smith 2405e76c431SBarry Smith Returns: 2415e76c431SBarry Smith 1 if the line search succeeds; 0 if the line search fails. 2425e76c431SBarry Smith 2435e76c431SBarry Smith Notes: 2445e76c431SBarry Smith This line search is taken from "Numerical Methods for Unconstrained 2455e76c431SBarry Smith Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 2465e76c431SBarry Smith 24728ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 24828ae5a21SLois Curfman McInnes 249f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 2505e76c431SBarry Smith @*/ 2515e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 2525e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 2535e76c431SBarry Smith { 2545e42470aSBarry Smith double steptol, initslope; 2555e42470aSBarry Smith double lambdaprev, gnormprev; 2565e76c431SBarry Smith double a, b, d, t1, t2; 2576b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2585e42470aSBarry Smith Scalar cinitslope,clambda; 2596b5873e3SBarry Smith #endif 2605e42470aSBarry Smith int ierr,count; 2615e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 2625e42470aSBarry Smith Scalar one = 1.0,scale; 2635e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 2645e76c431SBarry Smith 265*7857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 2665e76c431SBarry Smith alpha = neP->alpha; 2675e76c431SBarry Smith maxstep = neP->maxstep; 2685e76c431SBarry Smith steptol = neP->steptol; 2695e76c431SBarry Smith 2705e42470aSBarry Smith VecNorm(y, ynorm ); 2715e76c431SBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 2725e42470aSBarry Smith scale = maxstep/(*ynorm); 2736b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2746b5873e3SBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale)); 2756b5873e3SBarry Smith #else 2765e42470aSBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale); 2776b5873e3SBarry Smith #endif 2785e42470aSBarry Smith VecScale(&scale, y ); 2795e76c431SBarry Smith *ynorm = maxstep; 2805e76c431SBarry Smith } 2815e76c431SBarry Smith minlambda = steptol/(*ynorm); 2825e42470aSBarry Smith #if defined(PETSC_COMPLEX) 2835e42470aSBarry Smith VecDot(f, y, &cinitslope ); 2845e42470aSBarry Smith initslope = real(cinitslope); 2855e42470aSBarry Smith #else 2865e42470aSBarry Smith VecDot(f, y, &initslope ); 2875e42470aSBarry Smith #endif 2885e76c431SBarry Smith if (initslope > 0.0) initslope = -initslope; 2895e76c431SBarry Smith if (initslope == 0.0) initslope = -1.0; 2905e76c431SBarry Smith 2915e42470aSBarry Smith VecCopy(y, w ); 2925e42470aSBarry Smith VecAXPY(&one, x, w ); 293a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 2945e42470aSBarry Smith VecNorm(g, gnorm ); 2955e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 2965e42470aSBarry Smith VecCopy(w, y ); 2975e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 298*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 2995e42470aSBarry Smith return 0; 3005e76c431SBarry Smith } 3015e76c431SBarry Smith 3025e76c431SBarry Smith /* Fit points with quadratic */ 3035e76c431SBarry Smith lambda = 1.0; count = 0; 3045e76c431SBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 3055e76c431SBarry Smith lambdaprev = lambda; 3065e76c431SBarry Smith gnormprev = *gnorm; 3075e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3085e76c431SBarry Smith lambda = .1*lambda; 3095e76c431SBarry Smith } else lambda = lambdatemp; 3105e42470aSBarry Smith VecCopy(x, w ); 3115e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3125e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 3135e42470aSBarry Smith #else 3145e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3155e42470aSBarry Smith #endif 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,"Quadratically determined step, lambda %g\n",lambda); 321*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3225e42470aSBarry Smith return 0; 3235e76c431SBarry Smith } 3245e76c431SBarry Smith 3255e76c431SBarry Smith /* Fit points with cubic */ 3265e76c431SBarry Smith count = 1; 3275e76c431SBarry Smith while (1) { 3285e76c431SBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 3295e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 3305e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 3315e76c431SBarry Smith fnorm,*gnorm, *ynorm,lambda); 3325e42470aSBarry Smith VecCopy(w, y ); 333*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3345e76c431SBarry Smith return 0; 3355e76c431SBarry Smith } 3365e76c431SBarry Smith t1 = *gnorm - fnorm - lambda*initslope; 3375e76c431SBarry Smith t2 = gnormprev - fnorm - lambdaprev*initslope; 3385e76c431SBarry Smith a = (t1/(lambda*lambda) - 3395e76c431SBarry Smith t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3405e76c431SBarry Smith b = (-lambdaprev*t1/(lambda*lambda) + 3415e76c431SBarry Smith lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3425e76c431SBarry Smith d = b*b - 3*a*initslope; 3435e76c431SBarry Smith if (d < 0.0) d = 0.0; 3445e76c431SBarry Smith if (a == 0.0) { 3455e76c431SBarry Smith lambdatemp = -initslope/(2.0*b); 3465e76c431SBarry Smith } else { 3475e76c431SBarry Smith lambdatemp = (-b + sqrt(d))/(3.0*a); 3485e76c431SBarry Smith } 3495e76c431SBarry Smith if (lambdatemp > .5*lambda) { 3505e76c431SBarry Smith lambdatemp = .5*lambda; 3515e76c431SBarry Smith } 3525e76c431SBarry Smith lambdaprev = lambda; 3535e76c431SBarry Smith gnormprev = *gnorm; 3545e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3555e76c431SBarry Smith lambda = .1*lambda; 3565e76c431SBarry Smith } 3575e76c431SBarry Smith else lambda = lambdatemp; 3585e42470aSBarry Smith VecCopy( x, w ); 3595e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3605e42470aSBarry Smith VecAXPY(&clambda, y, w ); 3615e42470aSBarry Smith #else 3625e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3635e42470aSBarry Smith #endif 364a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 3655e42470aSBarry Smith VecNorm(g, gnorm ); 3665e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* is reduction enough */ 3675e42470aSBarry Smith VecCopy(w, y ); 3685e42470aSBarry Smith PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda); 369*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3705e42470aSBarry Smith return 0; 3715e76c431SBarry Smith } 3725e76c431SBarry Smith count++; 3735e76c431SBarry Smith } 374*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3755e42470aSBarry Smith return 0; 3765e76c431SBarry Smith } 3775e42470aSBarry Smith /*@ 3785e42470aSBarry Smith SNESQuadraticLineSearch - This routine performs a cubic line search. 3795e76c431SBarry Smith 3805e42470aSBarry Smith Input Parameters: 381f59ffdedSLois Curfman McInnes . snes - the SNES context 3825e42470aSBarry Smith . x - current iterate 3835e42470aSBarry Smith . f - residual evaluated at x 3845e42470aSBarry Smith . y - search direction (contains new iterate on output) 3855e42470aSBarry Smith . w - work vector 3865e42470aSBarry Smith . fnorm - 2-norm of f 3875e42470aSBarry Smith 3885e42470aSBarry Smith Output parameters: 3895e42470aSBarry Smith . g - residual evaluated at new iterate y 3905e42470aSBarry Smith . y - new iterate (contains search direction on input) 3915e42470aSBarry Smith . gnorm - 2-norm of g 3925e42470aSBarry Smith . ynorm - 2-norm of search length 3935e42470aSBarry Smith 3945e42470aSBarry Smith Returns: 3955e42470aSBarry Smith 1 if the line search succeeds; 0 if the line search fails. 3965e42470aSBarry Smith 3975e42470aSBarry Smith Notes: 398f59ffdedSLois Curfman McInnes Use SNESSetLineSearchRoutine() 399f59ffdedSLois Curfman McInnes to set this routine within the SNES_NLS method. 4005e42470aSBarry Smith 401f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search 402f59ffdedSLois Curfman McInnes 403f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 4045e42470aSBarry Smith @*/ 4055e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 4065e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 4075e76c431SBarry Smith { 4085e42470aSBarry Smith double steptol, initslope; 4095e42470aSBarry Smith double lambdaprev, gnormprev; 4106b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 4115e42470aSBarry Smith Scalar cinitslope,clambda; 4126b5873e3SBarry Smith #endif 4135e42470aSBarry Smith int ierr,count; 4145e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 4155e42470aSBarry Smith Scalar one = 1.0,scale; 4165e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 4175e76c431SBarry Smith 418*7857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 4195e42470aSBarry Smith alpha = neP->alpha; 4205e42470aSBarry Smith maxstep = neP->maxstep; 4215e42470aSBarry Smith steptol = neP->steptol; 4225e76c431SBarry Smith 4235e42470aSBarry Smith VecNorm(y, ynorm ); 4245e42470aSBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 4255e42470aSBarry Smith scale = maxstep/(*ynorm); 4265e42470aSBarry Smith VecScale(&scale, y ); 4275e42470aSBarry Smith *ynorm = maxstep; 4285e76c431SBarry Smith } 4295e42470aSBarry Smith minlambda = steptol/(*ynorm); 4305e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4315e42470aSBarry Smith VecDot(f, y, &cinitslope ); 4325e42470aSBarry Smith initslope = real(cinitslope); 4335e42470aSBarry Smith #else 4345e42470aSBarry Smith VecDot(f, y, &initslope ); 4355e42470aSBarry Smith #endif 4365e42470aSBarry Smith if (initslope > 0.0) initslope = -initslope; 4375e42470aSBarry Smith if (initslope == 0.0) initslope = -1.0; 4385e42470aSBarry Smith 4395e42470aSBarry Smith VecCopy(y, w ); 4405e42470aSBarry Smith VecAXPY(&one, x, w ); 441a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 4425e42470aSBarry Smith VecNorm(g, gnorm ); 4435e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 4445e42470aSBarry Smith VecCopy(w, y ); 4455e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 446*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4475e42470aSBarry Smith return 0; 4485e42470aSBarry Smith } 4495e42470aSBarry Smith 4505e42470aSBarry Smith /* Fit points with quadratic */ 4515e42470aSBarry Smith lambda = 1.0; count = 0; 4525e42470aSBarry Smith count = 1; 4535e42470aSBarry Smith while (1) { 4545e42470aSBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 4555e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 4565e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 4575e42470aSBarry Smith fnorm,*gnorm, *ynorm,lambda); 4585e42470aSBarry Smith VecCopy(w, y ); 459*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4605e42470aSBarry Smith return 0; 4615e42470aSBarry Smith } 4625e42470aSBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 4635e42470aSBarry Smith lambdaprev = lambda; 4645e42470aSBarry Smith gnormprev = *gnorm; 4655e42470aSBarry Smith if (lambdatemp <= .1*lambda) { 4665e42470aSBarry Smith lambda = .1*lambda; 4675e42470aSBarry Smith } else lambda = lambdatemp; 4685e42470aSBarry Smith VecCopy(x, w ); 4695e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4705e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 4715e42470aSBarry Smith #else 4725e42470aSBarry Smith VecAXPY(&lambda, y, w ); 4735e42470aSBarry Smith #endif 474a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 4755e42470aSBarry Smith VecNorm(g, gnorm ); 4765e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 4775e42470aSBarry Smith VecCopy(w, y ); 4785e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 479*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4805e42470aSBarry Smith return 0; 4815e42470aSBarry Smith } 4825e42470aSBarry Smith count++; 4835e42470aSBarry Smith } 4845e42470aSBarry Smith 485*7857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4865e42470aSBarry Smith return 0; 4875e76c431SBarry Smith } 4885e76c431SBarry Smith /* ------------------------------------------------------------ */ 4895e76c431SBarry Smith /*@C 4905e42470aSBarry Smith SNESSetLineSearchRoutine - Sets the line search routine to be used 491fbe28522SBarry Smith by the method SNES_LS. 4925e76c431SBarry Smith 4935e76c431SBarry Smith Input Parameters: 494eafb4bcbSBarry Smith . snes - nonlinear context obtained from SNESCreate() 4955e76c431SBarry Smith . func - pointer to int function 4965e76c431SBarry Smith 4975e76c431SBarry Smith Possible routines: 498f59ffdedSLois Curfman McInnes . SNESCubicLineSearch() - default line search 499f59ffdedSLois Curfman McInnes . SNESQuadraticLineSearch() - quadratic line search 500f59ffdedSLois Curfman McInnes . SNESNoLineSearch() - the full Newton step (actually not a line search) 5015e76c431SBarry Smith 5025e76c431SBarry Smith Calling sequence of func: 503f59ffdedSLois Curfman McInnes func (SNES snes, Vec x, Vec f, Vec g, Vec y, 5045e42470aSBarry Smith Vec w, double fnorm, double *ynorm, double *gnorm) 5055e76c431SBarry Smith 5065e76c431SBarry Smith Input parameters for func: 5075e42470aSBarry Smith . snes - nonlinear context 5085e76c431SBarry Smith . x - current iterate 5095e76c431SBarry Smith . f - residual evaluated at x 5105e76c431SBarry Smith . y - search direction (contains new iterate on output) 5115e76c431SBarry Smith . w - work vector 5125e76c431SBarry Smith . fnorm - 2-norm of f 5135e76c431SBarry Smith 5145e76c431SBarry Smith Output parameters for func: 5155e76c431SBarry Smith . g - residual evaluated at new iterate y 5165e76c431SBarry Smith . y - new iterate (contains search direction on input) 5175e76c431SBarry Smith . gnorm - 2-norm of g 5185e76c431SBarry Smith . ynorm - 2-norm of search length 5195e76c431SBarry Smith 5205e76c431SBarry Smith Returned by func: 5215e76c431SBarry Smith 1 if the line search succeeds; 0 if the line search fails. 522f59ffdedSLois Curfman McInnes 523f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine 524f59ffdedSLois Curfman McInnes 525f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch() 5265e76c431SBarry Smith @*/ 5275e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 5285e42470aSBarry Smith double,double *,double*) ) 5295e76c431SBarry Smith { 530fbe28522SBarry Smith if ((snes)->type == SNES_NLS) 5315e42470aSBarry Smith ((SNES_LS *)(snes->data))->LineSearch = func; 5325e42470aSBarry Smith return 0; 5335e76c431SBarry Smith } 5345e42470aSBarry Smith 5355e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes) 5365e42470aSBarry Smith { 5375e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5385e42470aSBarry Smith fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n"); 5395e42470aSBarry Smith fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha); 5405e42470aSBarry Smith fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep); 5415e42470aSBarry Smith fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol); 5426b5873e3SBarry Smith return 0; 5435e42470aSBarry Smith } 5445e42470aSBarry Smith 5456b5873e3SBarry Smith #include "options.h" 5465e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes) 5475e42470aSBarry Smith { 5485e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5495e42470aSBarry Smith char ver[16]; 5505e42470aSBarry Smith double tmp; 5515e42470aSBarry Smith 5525e42470aSBarry Smith if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_alpa",&tmp)) { 5535e42470aSBarry Smith ls->alpha = tmp; 5545e42470aSBarry Smith } 5555e42470aSBarry Smith if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_maxstep",&tmp)) { 5565e42470aSBarry Smith ls->maxstep = tmp; 5575e42470aSBarry Smith } 5585e42470aSBarry Smith if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_steptol",&tmp)) { 5595e42470aSBarry Smith ls->steptol = tmp; 5605e42470aSBarry Smith } 5615e42470aSBarry Smith if (OptionsGetString(0,snes->prefix,"-snes_line_search",ver,16)) { 5625e42470aSBarry Smith if (!strcmp(ver,"basic")) { 5635e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESNoLineSearch); 5645e42470aSBarry Smith } 5655e42470aSBarry Smith else if (!strcmp(ver,"quadratic")) { 5665e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch); 5675e42470aSBarry Smith } 5685e42470aSBarry Smith else if (!strcmp(ver,"cubic")) { 5695e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESCubicLineSearch); 5705e42470aSBarry Smith } 5715e42470aSBarry Smith else {SETERR(1,"Unknown line search?");} 5725e42470aSBarry Smith } 5735e42470aSBarry Smith return 0; 5745e42470aSBarry Smith } 5755e42470aSBarry Smith 5765e42470aSBarry Smith /* ------------------------------------------------------------ */ 5775e42470aSBarry Smith int SNESCreate_LS(SNES snes ) 5785e42470aSBarry Smith { 5795e42470aSBarry Smith SNES_LS *neP; 5805e42470aSBarry Smith 581fbe28522SBarry Smith snes->type = SNES_NLS; 5825e42470aSBarry Smith snes->Setup = SNESSetUp_LS; 5835e42470aSBarry Smith snes->Solver = SNESSolve_LS; 5845e42470aSBarry Smith snes->destroy = SNESDestroy_LS; 5855e42470aSBarry Smith snes->Converged = SNESDefaultConverged; 5865e42470aSBarry Smith snes->PrintHelp = SNESPrintHelp_LS; 5875e42470aSBarry Smith snes->SetFromOptions = SNESSetFromOptions_LS; 5885e42470aSBarry Smith 5895e42470aSBarry Smith neP = NEW(SNES_LS); CHKPTR(neP); 5905e42470aSBarry Smith snes->data = (void *) neP; 5915e42470aSBarry Smith neP->alpha = 1.e-4; 5925e42470aSBarry Smith neP->maxstep = 1.e8; 5935e42470aSBarry Smith neP->steptol = 1.e-12; 5945e42470aSBarry Smith neP->LineSearch = SNESCubicLineSearch; 5955e42470aSBarry Smith return 0; 5965e42470aSBarry Smith } 5975e42470aSBarry Smith 5985e42470aSBarry Smith 5995e42470aSBarry Smith 6005e42470aSBarry Smith 601