15e76c431SBarry Smith #ifndef lint 2*df60cc22SBarry Smith static char vcid[] = "$Id: ls.c,v 1.17 1995/05/16 00:40:57 curfman 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; 33*df60cc22SBarry 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 */ 58*df60cc22SBarry Smith ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre, 59*df60cc22SBarry 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 ); 102*df60cc22SBarry 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 { 125edd2f0e1SBarry Smith MPE_printf(snes->comm, "iter = %d, Function norm %g \n",its,fnorm); 1265e42470aSBarry Smith return 0; 1275e42470aSBarry Smith } 1285e42470aSBarry Smith 1295e42470aSBarry Smith /*@ 1305e42470aSBarry Smith SNESDefaultConverged - Default test for monitoring the convergence 1310de89847SLois Curfman McInnes of the SNES solvers. 1325e42470aSBarry Smith 1335e42470aSBarry Smith Input Parameters: 1340de89847SLois Curfman McInnes . snes - the SNES context 1355e42470aSBarry Smith . xnorm - 2-norm of current iterate 1365e42470aSBarry Smith . pnorm - 2-norm of current step 1375e42470aSBarry Smith . fnorm - 2-norm of residual 1380de89847SLois Curfman McInnes . dummy - unused context 1395e42470aSBarry Smith 1405e42470aSBarry Smith Returns: 1415e42470aSBarry Smith $ 2 if ( fnorm < atol ), 1425e42470aSBarry Smith $ 3 if ( pnorm < xtol*xnorm ), 1435e42470aSBarry Smith $ -2 if ( nres > max_res ), 1445e42470aSBarry Smith $ 0 otherwise, 1455e42470aSBarry Smith 1465e42470aSBarry Smith where 1475e42470aSBarry Smith $ atol - absolute residual norm tolerance, 1480de89847SLois Curfman McInnes $ set with SNESSetAbsoluteTolerance() 1495e42470aSBarry Smith $ max_res - maximum number of residual evaluations, 1500de89847SLois Curfman McInnes $ set with SNESSetMaxResidualEvaluations() 1515e42470aSBarry Smith $ nres - number of residual evaluations 1525e42470aSBarry Smith $ xtol - relative residual norm tolerance, 1530de89847SLois Curfman McInnes $ set with SNESSetRelativeTolerance() 1545e42470aSBarry Smith 1550de89847SLois Curfman McInnes .keywords: SNES, nonlinear, default, converged, convergence 1565e42470aSBarry Smith 1570de89847SLois Curfman McInnes .seealso: SNESSetConvergenceTest() 1585e42470aSBarry Smith @*/ 1595e42470aSBarry Smith int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm, 1605e42470aSBarry Smith void *dummy) 1615e42470aSBarry Smith { 1625e42470aSBarry Smith if (fnorm < snes->atol) { 163a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 164a67c8522SLois Curfman McInnes "Converged due to absolute residual norm %g < %g\n",fnorm,snes->atol); 1655e42470aSBarry Smith return 2; 1665e42470aSBarry Smith } 1675e42470aSBarry Smith if (pnorm < snes->xtol*(xnorm)) { 168a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 169a67c8522SLois Curfman McInnes "Converged due to small update length %g < %g*%g\n", 1705e42470aSBarry Smith pnorm,snes->xtol,xnorm); 1715e42470aSBarry Smith return 3; 1725e42470aSBarry Smith } 17349e3953dSBarry Smith if (snes->nfuncs > snes->max_funcs) { 174a67c8522SLois Curfman McInnes PLogInfo((PetscObject)snes, 175a67c8522SLois Curfman McInnes "Exceeded maximum number of residual evaluations: %d > %d\n", 17649e3953dSBarry Smith snes->nfuncs, snes->max_funcs ); 177a67c8522SLois Curfman McInnes return -2; 178a67c8522SLois Curfman McInnes } 1795e42470aSBarry Smith return 0; 1805e42470aSBarry Smith } 1815e42470aSBarry Smith 1825e76c431SBarry Smith /* ------------------------------------------------------------ */ 1835e76c431SBarry Smith /*ARGSUSED*/ 1845e76c431SBarry Smith /*@ 1855e42470aSBarry Smith SNESNoLineSearch - This routine is not a line search at all; 1865e76c431SBarry Smith it simply uses the full Newton step. Thus, this routine is intended 1875e76c431SBarry Smith to serve as a template and is not recommended for general use. 1885e76c431SBarry Smith 1895e76c431SBarry Smith Input Parameters: 1905e42470aSBarry Smith . snes - nonlinear context 1915e76c431SBarry Smith . x - current iterate 1925e76c431SBarry Smith . f - residual evaluated at x 1935e76c431SBarry Smith . y - search direction (contains new iterate on output) 1945e76c431SBarry Smith . w - work vector 1955e76c431SBarry Smith . fnorm - 2-norm of f 1965e76c431SBarry Smith 197c4a48953SLois Curfman McInnes Output Parameters: 1985e76c431SBarry Smith . g - residual evaluated at new iterate y 1995e76c431SBarry Smith . y - new iterate (contains search direction on input) 2005e76c431SBarry Smith . gnorm - 2-norm of g 2015e76c431SBarry Smith . ynorm - 2-norm of search length 2025e76c431SBarry Smith 203c4a48953SLois Curfman McInnes Options Database Key: 204c4a48953SLois Curfman McInnes $ -snes_line_search basic 205c4a48953SLois Curfman McInnes 2065e76c431SBarry Smith Returns: 2075e76c431SBarry Smith 1, indicating success of the step. 2085e76c431SBarry Smith 20928ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 21028ae5a21SLois Curfman McInnes 211f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 212f59ffdedSLois Curfman McInnes .seealso: SNESSetLineSearchRoutine() 2135e76c431SBarry Smith @*/ 2145e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 2155e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 2165e76c431SBarry Smith { 2175e42470aSBarry Smith int ierr; 2185e42470aSBarry Smith Scalar one = 1.0; 2197857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 2205e42470aSBarry Smith VecNorm(y, ynorm ); /* ynorm = || y || */ 2215e42470aSBarry Smith VecAXPY(&one, x, y ); /* y <- x + y */ 222a9581db2SBarry Smith ierr = SNESComputeFunction(snes,y,g); CHKERR(ierr); 2235e42470aSBarry Smith VecNorm( g, gnorm ); /* gnorm = || g || */ 2247857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 2255e76c431SBarry Smith return 1; 2265e76c431SBarry Smith } 2275e76c431SBarry Smith /* ------------------------------------------------------------------ */ 2285e76c431SBarry Smith /*@ 229f59ffdedSLois Curfman McInnes SNESCubicLineSearch - This routine performs a cubic line search and 230f59ffdedSLois Curfman McInnes is the default line search method. 2315e76c431SBarry Smith 2325e76c431SBarry Smith Input Parameters: 2335e42470aSBarry Smith . snes - nonlinear context 2345e76c431SBarry Smith . x - current iterate 2355e76c431SBarry Smith . f - residual evaluated at x 2365e76c431SBarry Smith . y - search direction (contains new iterate on output) 2375e76c431SBarry Smith . w - work vector 2385e76c431SBarry Smith . fnorm - 2-norm of f 2395e76c431SBarry Smith 2405e76c431SBarry Smith Output parameters: 2415e76c431SBarry Smith . g - residual evaluated at new iterate y 2425e76c431SBarry Smith . y - new iterate (contains search direction on input) 2435e76c431SBarry Smith . gnorm - 2-norm of g 2445e76c431SBarry Smith . ynorm - 2-norm of search length 2455e76c431SBarry Smith 2465e76c431SBarry Smith Returns: 2475e76c431SBarry Smith 1 if the line search succeeds; 0 if the line search fails. 2485e76c431SBarry Smith 249c4a48953SLois Curfman McInnes Options Database Key: 250c4a48953SLois Curfman McInnes $ -snes_line_search cubic 251c4a48953SLois Curfman McInnes 2525e76c431SBarry Smith Notes: 2535e76c431SBarry Smith This line search is taken from "Numerical Methods for Unconstrained 2545e76c431SBarry Smith Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 2555e76c431SBarry Smith 25628ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 25728ae5a21SLois Curfman McInnes 258f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 2595e76c431SBarry Smith @*/ 2605e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 2615e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 2625e76c431SBarry Smith { 2635e42470aSBarry Smith double steptol, initslope; 2645e42470aSBarry Smith double lambdaprev, gnormprev; 2655e76c431SBarry Smith double a, b, d, t1, t2; 2666b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2675e42470aSBarry Smith Scalar cinitslope,clambda; 2686b5873e3SBarry Smith #endif 2695e42470aSBarry Smith int ierr,count; 2705e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 2715e42470aSBarry Smith Scalar one = 1.0,scale; 2725e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 2735e76c431SBarry Smith 2747857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 2755e76c431SBarry Smith alpha = neP->alpha; 2765e76c431SBarry Smith maxstep = neP->maxstep; 2775e76c431SBarry Smith steptol = neP->steptol; 2785e76c431SBarry Smith 2795e42470aSBarry Smith VecNorm(y, ynorm ); 2805e76c431SBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 2815e42470aSBarry Smith scale = maxstep/(*ynorm); 2826b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2836b5873e3SBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale)); 2846b5873e3SBarry Smith #else 2855e42470aSBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale); 2866b5873e3SBarry Smith #endif 2875e42470aSBarry Smith VecScale(&scale, y ); 2885e76c431SBarry Smith *ynorm = maxstep; 2895e76c431SBarry Smith } 2905e76c431SBarry Smith minlambda = steptol/(*ynorm); 2915e42470aSBarry Smith #if defined(PETSC_COMPLEX) 2925e42470aSBarry Smith VecDot(f, y, &cinitslope ); 2935e42470aSBarry Smith initslope = real(cinitslope); 2945e42470aSBarry Smith #else 2955e42470aSBarry Smith VecDot(f, y, &initslope ); 2965e42470aSBarry Smith #endif 2975e76c431SBarry Smith if (initslope > 0.0) initslope = -initslope; 2985e76c431SBarry Smith if (initslope == 0.0) initslope = -1.0; 2995e76c431SBarry Smith 3005e42470aSBarry Smith VecCopy(y, w ); 3015e42470aSBarry Smith VecAXPY(&one, x, w ); 302a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 3035e42470aSBarry Smith VecNorm(g, gnorm ); 3045e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 3055e42470aSBarry Smith VecCopy(w, y ); 3065e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 3077857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3085e42470aSBarry Smith return 0; 3095e76c431SBarry Smith } 3105e76c431SBarry Smith 3115e76c431SBarry Smith /* Fit points with quadratic */ 3125e76c431SBarry Smith lambda = 1.0; count = 0; 3135e76c431SBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 3145e76c431SBarry Smith lambdaprev = lambda; 3155e76c431SBarry Smith gnormprev = *gnorm; 3165e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3175e76c431SBarry Smith lambda = .1*lambda; 3185e76c431SBarry Smith } else lambda = lambdatemp; 3195e42470aSBarry Smith VecCopy(x, w ); 3205e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3215e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 3225e42470aSBarry Smith #else 3235e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3245e42470aSBarry Smith #endif 325a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 3265e42470aSBarry Smith VecNorm(g, gnorm ); 3275e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 3285e42470aSBarry Smith VecCopy(w, y ); 3295e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 3307857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3315e42470aSBarry Smith return 0; 3325e76c431SBarry Smith } 3335e76c431SBarry Smith 3345e76c431SBarry Smith /* Fit points with cubic */ 3355e76c431SBarry Smith count = 1; 3365e76c431SBarry Smith while (1) { 3375e76c431SBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 3385e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 3395e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 3405e76c431SBarry Smith fnorm,*gnorm, *ynorm,lambda); 3415e42470aSBarry Smith VecCopy(w, y ); 3427857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 34323242f5aSBarry Smith return -1; 3445e76c431SBarry Smith } 3455e76c431SBarry Smith t1 = *gnorm - fnorm - lambda*initslope; 3465e76c431SBarry Smith t2 = gnormprev - fnorm - lambdaprev*initslope; 3475e76c431SBarry Smith a = (t1/(lambda*lambda) - 3485e76c431SBarry Smith t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3495e76c431SBarry Smith b = (-lambdaprev*t1/(lambda*lambda) + 3505e76c431SBarry Smith lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 3515e76c431SBarry Smith d = b*b - 3*a*initslope; 3525e76c431SBarry Smith if (d < 0.0) d = 0.0; 3535e76c431SBarry Smith if (a == 0.0) { 3545e76c431SBarry Smith lambdatemp = -initslope/(2.0*b); 3555e76c431SBarry Smith } else { 3565e76c431SBarry Smith lambdatemp = (-b + sqrt(d))/(3.0*a); 3575e76c431SBarry Smith } 3585e76c431SBarry Smith if (lambdatemp > .5*lambda) { 3595e76c431SBarry Smith lambdatemp = .5*lambda; 3605e76c431SBarry Smith } 3615e76c431SBarry Smith lambdaprev = lambda; 3625e76c431SBarry Smith gnormprev = *gnorm; 3635e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3645e76c431SBarry Smith lambda = .1*lambda; 3655e76c431SBarry Smith } 3665e76c431SBarry Smith else lambda = lambdatemp; 3675e42470aSBarry Smith VecCopy( x, w ); 3685e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3695e42470aSBarry Smith VecAXPY(&clambda, y, w ); 3705e42470aSBarry Smith #else 3715e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3725e42470aSBarry Smith #endif 373a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 3745e42470aSBarry Smith VecNorm(g, gnorm ); 3755e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* is reduction enough */ 3765e42470aSBarry Smith VecCopy(w, y ); 3775e42470aSBarry Smith PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda); 3787857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3795e42470aSBarry Smith return 0; 3805e76c431SBarry Smith } 3815e76c431SBarry Smith count++; 3825e76c431SBarry Smith } 3837857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3845e42470aSBarry Smith return 0; 3855e76c431SBarry Smith } 3865e42470aSBarry Smith /*@ 3875e42470aSBarry Smith SNESQuadraticLineSearch - This routine performs a cubic line search. 3885e76c431SBarry Smith 3895e42470aSBarry Smith Input Parameters: 390f59ffdedSLois Curfman McInnes . snes - the SNES context 3915e42470aSBarry Smith . x - current iterate 3925e42470aSBarry Smith . f - residual evaluated at x 3935e42470aSBarry Smith . y - search direction (contains new iterate on output) 3945e42470aSBarry Smith . w - work vector 3955e42470aSBarry Smith . fnorm - 2-norm of f 3965e42470aSBarry Smith 397c4a48953SLois Curfman McInnes Output Parameters: 3985e42470aSBarry Smith . g - residual evaluated at new iterate y 3995e42470aSBarry Smith . y - new iterate (contains search direction on input) 4005e42470aSBarry Smith . gnorm - 2-norm of g 4015e42470aSBarry Smith . ynorm - 2-norm of search length 4025e42470aSBarry Smith 4035e42470aSBarry Smith Returns: 4045e42470aSBarry Smith 1 if the line search succeeds; 0 if the line search fails. 4055e42470aSBarry Smith 406c4a48953SLois Curfman McInnes Options Database Key: 407c4a48953SLois Curfman McInnes $ -snes_line_search quadratic 408c4a48953SLois Curfman McInnes 4095e42470aSBarry Smith Notes: 410f59ffdedSLois Curfman McInnes Use SNESSetLineSearchRoutine() 411f59ffdedSLois Curfman McInnes to set this routine within the SNES_NLS method. 4125e42470aSBarry Smith 413f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search 414f59ffdedSLois Curfman McInnes 415f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 4165e42470aSBarry Smith @*/ 4175e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 4185e42470aSBarry Smith double fnorm, double *ynorm, double *gnorm ) 4195e76c431SBarry Smith { 4205e42470aSBarry Smith double steptol, initslope; 4215e42470aSBarry Smith double lambdaprev, gnormprev; 4226b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 4235e42470aSBarry Smith Scalar cinitslope,clambda; 4246b5873e3SBarry Smith #endif 4255e42470aSBarry Smith int ierr,count; 4265e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 4275e42470aSBarry Smith Scalar one = 1.0,scale; 4285e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 4295e76c431SBarry Smith 4307857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 4315e42470aSBarry Smith alpha = neP->alpha; 4325e42470aSBarry Smith maxstep = neP->maxstep; 4335e42470aSBarry Smith steptol = neP->steptol; 4345e76c431SBarry Smith 4355e42470aSBarry Smith VecNorm(y, ynorm ); 4365e42470aSBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 4375e42470aSBarry Smith scale = maxstep/(*ynorm); 4385e42470aSBarry Smith VecScale(&scale, y ); 4395e42470aSBarry Smith *ynorm = maxstep; 4405e76c431SBarry Smith } 4415e42470aSBarry Smith minlambda = steptol/(*ynorm); 4425e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4435e42470aSBarry Smith VecDot(f, y, &cinitslope ); 4445e42470aSBarry Smith initslope = real(cinitslope); 4455e42470aSBarry Smith #else 4465e42470aSBarry Smith VecDot(f, y, &initslope ); 4475e42470aSBarry Smith #endif 4485e42470aSBarry Smith if (initslope > 0.0) initslope = -initslope; 4495e42470aSBarry Smith if (initslope == 0.0) initslope = -1.0; 4505e42470aSBarry Smith 4515e42470aSBarry Smith VecCopy(y, w ); 4525e42470aSBarry Smith VecAXPY(&one, x, w ); 453a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 4545e42470aSBarry Smith VecNorm(g, gnorm ); 4555e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 4565e42470aSBarry Smith VecCopy(w, y ); 4575e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 4587857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4595e42470aSBarry Smith return 0; 4605e42470aSBarry Smith } 4615e42470aSBarry Smith 4625e42470aSBarry Smith /* Fit points with quadratic */ 4635e42470aSBarry Smith lambda = 1.0; count = 0; 4645e42470aSBarry Smith count = 1; 4655e42470aSBarry Smith while (1) { 4665e42470aSBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 4675e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 4685e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 4695e42470aSBarry Smith fnorm,*gnorm, *ynorm,lambda); 4705e42470aSBarry Smith VecCopy(w, y ); 4717857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4725e42470aSBarry Smith return 0; 4735e42470aSBarry Smith } 4745e42470aSBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 4755e42470aSBarry Smith lambdaprev = lambda; 4765e42470aSBarry Smith gnormprev = *gnorm; 4775e42470aSBarry Smith if (lambdatemp <= .1*lambda) { 4785e42470aSBarry Smith lambda = .1*lambda; 4795e42470aSBarry Smith } else lambda = lambdatemp; 4805e42470aSBarry Smith VecCopy(x, w ); 4815e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4825e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 4835e42470aSBarry Smith #else 4845e42470aSBarry Smith VecAXPY(&lambda, y, w ); 4855e42470aSBarry Smith #endif 486a9581db2SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERR(ierr); 4875e42470aSBarry Smith VecNorm(g, gnorm ); 4885e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 4895e42470aSBarry Smith VecCopy(w, y ); 4905e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 4917857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4925e42470aSBarry Smith return 0; 4935e42470aSBarry Smith } 4945e42470aSBarry Smith count++; 4955e42470aSBarry Smith } 4965e42470aSBarry Smith 4977857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4985e42470aSBarry Smith return 0; 4995e76c431SBarry Smith } 5005e76c431SBarry Smith /* ------------------------------------------------------------ */ 5015e76c431SBarry Smith /*@C 5025e42470aSBarry Smith SNESSetLineSearchRoutine - Sets the line search routine to be used 503fbe28522SBarry Smith by the method SNES_LS. 5045e76c431SBarry Smith 5055e76c431SBarry Smith Input Parameters: 506eafb4bcbSBarry Smith . snes - nonlinear context obtained from SNESCreate() 5075e76c431SBarry Smith . func - pointer to int function 5085e76c431SBarry Smith 509c4a48953SLois Curfman McInnes Available Routines: 510f59ffdedSLois Curfman McInnes . SNESCubicLineSearch() - default line search 511f59ffdedSLois Curfman McInnes . SNESQuadraticLineSearch() - quadratic line search 512f59ffdedSLois Curfman McInnes . SNESNoLineSearch() - the full Newton step (actually not a line search) 5135e76c431SBarry Smith 514c4a48953SLois Curfman McInnes Options Database Keys: 515c4a48953SLois Curfman McInnes $ -snes_line_search [basic,quadratic,cubic] 516c4a48953SLois Curfman McInnes 5175e76c431SBarry Smith Calling sequence of func: 518f59ffdedSLois Curfman McInnes func (SNES snes, Vec x, Vec f, Vec g, Vec y, 5195e42470aSBarry Smith Vec w, double fnorm, double *ynorm, double *gnorm) 5205e76c431SBarry Smith 5215e76c431SBarry Smith Input parameters for func: 5225e42470aSBarry Smith . snes - nonlinear context 5235e76c431SBarry Smith . x - current iterate 5245e76c431SBarry Smith . f - residual evaluated at x 5255e76c431SBarry Smith . y - search direction (contains new iterate on output) 5265e76c431SBarry Smith . w - work vector 5275e76c431SBarry Smith . fnorm - 2-norm of f 5285e76c431SBarry Smith 5295e76c431SBarry Smith Output parameters for func: 5305e76c431SBarry Smith . g - residual evaluated at new iterate y 5315e76c431SBarry Smith . y - new iterate (contains search direction on input) 5325e76c431SBarry Smith . gnorm - 2-norm of g 5335e76c431SBarry Smith . ynorm - 2-norm of search length 5345e76c431SBarry Smith 5355e76c431SBarry Smith Returned by func: 5365e76c431SBarry Smith 1 if the line search succeeds; 0 if the line search fails. 537f59ffdedSLois Curfman McInnes 538f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine 539f59ffdedSLois Curfman McInnes 540f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch() 5415e76c431SBarry Smith @*/ 5425e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 5435e42470aSBarry Smith double,double *,double*) ) 5445e76c431SBarry Smith { 545fbe28522SBarry Smith if ((snes)->type == SNES_NLS) 5465e42470aSBarry Smith ((SNES_LS *)(snes->data))->LineSearch = func; 5475e42470aSBarry Smith return 0; 5485e76c431SBarry Smith } 5495e42470aSBarry Smith 5505e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes) 5515e42470aSBarry Smith { 5525e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5535e42470aSBarry Smith fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n"); 5545e42470aSBarry Smith fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha); 5555e42470aSBarry Smith fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep); 5565e42470aSBarry Smith fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol); 5576b5873e3SBarry Smith return 0; 5585e42470aSBarry Smith } 5595e42470aSBarry Smith 5605e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes) 5615e42470aSBarry Smith { 5625e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5635e42470aSBarry Smith char ver[16]; 5645e42470aSBarry Smith double tmp; 5655e42470aSBarry Smith 566*df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) { 5675e42470aSBarry Smith ls->alpha = tmp; 5685e42470aSBarry Smith } 569*df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) { 5705e42470aSBarry Smith ls->maxstep = tmp; 5715e42470aSBarry Smith } 572*df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) { 5735e42470aSBarry Smith ls->steptol = tmp; 5745e42470aSBarry Smith } 575*df60cc22SBarry Smith if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) { 5765e42470aSBarry Smith if (!strcmp(ver,"basic")) { 5775e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESNoLineSearch); 5785e42470aSBarry Smith } 5795e42470aSBarry Smith else if (!strcmp(ver,"quadratic")) { 5805e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch); 5815e42470aSBarry Smith } 5825e42470aSBarry Smith else if (!strcmp(ver,"cubic")) { 5835e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESCubicLineSearch); 5845e42470aSBarry Smith } 5855e42470aSBarry Smith else {SETERR(1,"Unknown line search?");} 5865e42470aSBarry Smith } 5875e42470aSBarry Smith return 0; 5885e42470aSBarry Smith } 5895e42470aSBarry Smith 5905e42470aSBarry Smith /* ------------------------------------------------------------ */ 5915e42470aSBarry Smith int SNESCreate_LS(SNES snes ) 5925e42470aSBarry Smith { 5935e42470aSBarry Smith SNES_LS *neP; 5945e42470aSBarry Smith 595fbe28522SBarry Smith snes->type = SNES_NLS; 59649e3953dSBarry Smith snes->setup = SNESSetUp_LS; 59749e3953dSBarry Smith snes->solve = SNESSolve_LS; 5985e42470aSBarry Smith snes->destroy = SNESDestroy_LS; 5995e42470aSBarry Smith snes->Converged = SNESDefaultConverged; 60049e3953dSBarry Smith snes->printhelp = SNESPrintHelp_LS; 60149e3953dSBarry Smith snes->setfromoptions = SNESSetFromOptions_LS; 6025e42470aSBarry Smith 6035e42470aSBarry Smith neP = NEW(SNES_LS); CHKPTR(neP); 6045e42470aSBarry Smith snes->data = (void *) neP; 6055e42470aSBarry Smith neP->alpha = 1.e-4; 6065e42470aSBarry Smith neP->maxstep = 1.e8; 6075e42470aSBarry Smith neP->steptol = 1.e-12; 6085e42470aSBarry Smith neP->LineSearch = SNESCubicLineSearch; 6095e42470aSBarry Smith return 0; 6105e42470aSBarry Smith } 6115e42470aSBarry Smith 6125e42470aSBarry Smith 6135e42470aSBarry Smith 6145e42470aSBarry Smith 615