15e76c431SBarry Smith #ifndef lint 2*393d2d9aSLois Curfman McInnes static char vcid[] = "$Id: ls.c,v 1.36 1995/07/29 04:29:53 curfman Exp curfman $"; 35e76c431SBarry Smith #endif 45e76c431SBarry Smith 55e76c431SBarry Smith #include <math.h> 6fbe28522SBarry Smith #include "ls.h" 7a935fc98SLois Curfman McInnes #include "pviewer.h" 8bbb6d6a8SBarry Smith #if defined(HAVE_STRING_H) 9bbb6d6a8SBarry Smith #include <string.h> 10bbb6d6a8SBarry Smith #endif 115e42470aSBarry Smith 125e42470aSBarry Smith /* 135e42470aSBarry Smith Implements a line search variant of Newton's Method 145e76c431SBarry Smith for solving systems of nonlinear equations. 155e76c431SBarry Smith 165e76c431SBarry Smith Input parameters: 175e42470aSBarry Smith . snes - nonlinear context obtained from SNESCreate() 185e76c431SBarry Smith 195e42470aSBarry Smith Output Parameters: 20a935fc98SLois Curfman McInnes . outits - Number of global iterations until termination. 215e76c431SBarry Smith 225e76c431SBarry Smith Notes: 235e76c431SBarry Smith This implements essentially a truncated Newton method with a 245e76c431SBarry Smith line search. By default a cubic backtracking line search 255e76c431SBarry Smith is employed, as described in the text "Numerical Methods for 265e76c431SBarry Smith Unconstrained Optimization and Nonlinear Equations" by Dennis 27*393d2d9aSLois Curfman McInnes and Schnabel. 285e76c431SBarry Smith */ 295e76c431SBarry Smith 305e42470aSBarry Smith int SNESSolve_LS(SNES snes,int *outits) 315e76c431SBarry Smith { 325e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 33761aaf1bSLois Curfman McInnes int maxits, i, history_len, ierr, lits, lsfail; 34df60cc22SBarry Smith MatStructure flg = ALLMAT_DIFFERENT_NONZERO_PATTERN; 356b5873e3SBarry Smith double fnorm, gnorm, xnorm, ynorm, *history; 365e42470aSBarry Smith Vec Y, X, F, G, W, TMP; 37336446d4SLois Curfman McInnes SLES sles; 38336446d4SLois Curfman McInnes KSP ksp; 395e76c431SBarry Smith 405e42470aSBarry Smith history = snes->conv_hist; /* convergence history */ 415e42470aSBarry Smith history_len = snes->conv_hist_len; /* convergence history length */ 425e42470aSBarry Smith maxits = snes->max_its; /* maximum number of iterations */ 435e42470aSBarry Smith X = snes->vec_sol; /* solution vector */ 4439e2f89bSBarry Smith F = snes->vec_func; /* residual vector */ 455e42470aSBarry Smith Y = snes->work[0]; /* work vectors */ 465e42470aSBarry Smith G = snes->work[1]; 475e42470aSBarry Smith W = snes->work[2]; 485e76c431SBarry Smith 4978b31e54SBarry Smith ierr = SNESComputeInitialGuess(snes,X); CHKERRQ(ierr); /* X <- X_0 */ 50*393d2d9aSLois Curfman McInnes ierr = VecNorm(X,&xnorm); CHKERRQ(ierr); /* xnorm = || X || */ 5178b31e54SBarry Smith ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr); /* (+/-) F(X) */ 52*393d2d9aSLois Curfman McInnes ierr = VecNorm(F,&fnorm); CHKERRQ(ierr); /* fnorm <- ||F|| */ 535e42470aSBarry Smith snes->norm = fnorm; 545e76c431SBarry Smith if (history && history_len > 0) history[0] = fnorm; 55bbb6d6a8SBarry Smith if (snes->monitor) 56bbb6d6a8SBarry Smith {ierr = (*snes->monitor)(snes,0,fnorm,snes->monP); CHKERRQ(ierr);} 575e76c431SBarry Smith 58*393d2d9aSLois Curfman McInnes /* Set the KSP stopping criterion to use the Eisenstat-Walker method */ 59336446d4SLois Curfman McInnes if (snes->ksp_ewconv) { 60336446d4SLois Curfman McInnes ierr = SNESGetSLES(snes,&sles); CHKERRQ(ierr); 61336446d4SLois Curfman McInnes ierr = SLESGetKSP(sles,&ksp); CHKERRQ(ierr); 62336446d4SLois Curfman McInnes ierr = KSPSetConvergenceTest(ksp,SNES_KSP_EW_Converged_Private, 63336446d4SLois Curfman McInnes (void *)snes); CHKERRQ(ierr); 64336446d4SLois Curfman McInnes } 65336446d4SLois Curfman McInnes 665e76c431SBarry Smith for ( i=0; i<maxits; i++ ) { 675e42470aSBarry Smith snes->iter = i+1; 685e76c431SBarry Smith 695e76c431SBarry Smith /* Solve J Y = -F, where J is Jacobian matrix */ 70df60cc22SBarry Smith ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre, 71bbb6d6a8SBarry Smith &flg); CHKERRQ(ierr); 72a935fc98SLois Curfman McInnes ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre, 73a935fc98SLois Curfman McInnes flg); CHKERRQ(ierr); 7478b31e54SBarry Smith ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERRQ(ierr); 7581b6cf68SLois Curfman McInnes ierr = VecCopy(Y,snes->vec_sol_update_always); CHKERRQ(ierr); 76761aaf1bSLois Curfman McInnes ierr = (*neP->LineSearch)(snes,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail); 77761aaf1bSLois Curfman McInnes if (lsfail) snes->nfailures++; 7878b31e54SBarry Smith CHKERRQ(ierr); 795e76c431SBarry Smith 8039e2f89bSBarry Smith TMP = F; F = G; snes->vec_func_always = F; G = TMP; 8139e2f89bSBarry Smith TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP; 825e76c431SBarry Smith fnorm = gnorm; 835e76c431SBarry Smith 845e42470aSBarry Smith snes->norm = fnorm; 855e76c431SBarry Smith if (history && history_len > i+1) history[i+1] = fnorm; 86*393d2d9aSLois Curfman McInnes ierr = VecNorm(X,&xnorm); CHKERRQ(ierr); /* xnorm = || X || */ 87bbb6d6a8SBarry Smith if (snes->monitor) 88*393d2d9aSLois Curfman McInnes {ierr = (*snes->monitor)(snes,i+1,fnorm,snes->monP); CHKERRQ(ierr);} 895e76c431SBarry Smith 905e76c431SBarry Smith /* Test for convergence */ 91bbb6d6a8SBarry Smith if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) { 9239e2f89bSBarry Smith if (X != snes->vec_sol) { 93*393d2d9aSLois Curfman McInnes ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr); 9439e2f89bSBarry Smith snes->vec_sol_always = snes->vec_sol; 9539e2f89bSBarry Smith snes->vec_func_always = snes->vec_func; 9639e2f89bSBarry Smith } 975e76c431SBarry Smith break; 985e76c431SBarry Smith } 995e76c431SBarry Smith } 10052392280SLois Curfman McInnes if (i == maxits) { 10152392280SLois Curfman McInnes PLogInfo((PetscObject)snes, 10252392280SLois Curfman McInnes "Maximum number of iterations has been reached: %d\n",maxits); 10352392280SLois Curfman McInnes i--; 10452392280SLois Curfman McInnes } 1055e42470aSBarry Smith *outits = i+1; 1065e42470aSBarry Smith return 0; 1075e76c431SBarry Smith } 1085e76c431SBarry Smith /* ------------------------------------------------------------ */ 1095e42470aSBarry Smith int SNESSetUp_LS(SNES snes ) 1105e76c431SBarry Smith { 1115e42470aSBarry Smith int ierr; 11281b6cf68SLois Curfman McInnes snes->nwork = 4; 11378b31e54SBarry Smith ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work); CHKERRQ(ierr); 1145e42470aSBarry Smith PLogObjectParents(snes,snes->nwork,snes->work); 11581b6cf68SLois Curfman McInnes snes->vec_sol_update_always = snes->work[3]; 1165e42470aSBarry Smith return 0; 1175e76c431SBarry Smith } 1185e76c431SBarry Smith /* ------------------------------------------------------------ */ 1195e42470aSBarry Smith int SNESDestroy_LS(PetscObject obj) 1205e76c431SBarry Smith { 1215e42470aSBarry Smith SNES snes = (SNES) obj; 122*393d2d9aSLois Curfman McInnes int ierr; 123*393d2d9aSLois Curfman McInnes ierr = VecFreeVecs(snes->work,snes->nwork); CHKERRQ(ierr); 12478b31e54SBarry Smith PETSCFREE(snes->data); 1255e42470aSBarry Smith return 0; 1265e76c431SBarry Smith } 1275e76c431SBarry Smith /* ------------------------------------------------------------ */ 1285e76c431SBarry Smith /*ARGSUSED*/ 1295e76c431SBarry Smith /*@ 1305e42470aSBarry Smith SNESNoLineSearch - This routine is not a line search at all; 1315e76c431SBarry Smith it simply uses the full Newton step. Thus, this routine is intended 1325e76c431SBarry Smith to serve as a template and is not recommended for general use. 1335e76c431SBarry Smith 1345e76c431SBarry Smith Input Parameters: 1355e42470aSBarry Smith . snes - nonlinear context 1365e76c431SBarry Smith . x - current iterate 1375e76c431SBarry Smith . f - residual evaluated at x 1385e76c431SBarry Smith . y - search direction (contains new iterate on output) 1395e76c431SBarry Smith . w - work vector 1405e76c431SBarry Smith . fnorm - 2-norm of f 1415e76c431SBarry Smith 142c4a48953SLois Curfman McInnes Output Parameters: 1435e76c431SBarry Smith . g - residual evaluated at new iterate y 1445e76c431SBarry Smith . y - new iterate (contains search direction on input) 1455e76c431SBarry Smith . gnorm - 2-norm of g 1465e76c431SBarry Smith . ynorm - 2-norm of search length 147761aaf1bSLois Curfman McInnes . flag - set to 0, indicating a successful line search 1485e76c431SBarry Smith 149c4a48953SLois Curfman McInnes Options Database Key: 150c4a48953SLois Curfman McInnes $ -snes_line_search basic 151c4a48953SLois Curfman McInnes 15228ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 15328ae5a21SLois Curfman McInnes 154f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 155a935fc98SLois Curfman McInnes SNESSetLineSearchRoutine() 1565e76c431SBarry Smith @*/ 1575e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 158761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag ) 1595e76c431SBarry Smith { 1605e42470aSBarry Smith int ierr; 1615e42470aSBarry Smith Scalar one = 1.0; 162761aaf1bSLois Curfman McInnes *flag = 0; 1637857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 164*393d2d9aSLois Curfman McInnes ierr = VecNorm(y,ynorm); CHKERRQ(ierr); /* ynorm = || y || */ 165*393d2d9aSLois Curfman McInnes ierr = VecAXPY(&one,x,y); CHKERRQ(ierr); /* y <- x + y */ 166*393d2d9aSLois Curfman McInnes ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); /* (+/-) F(y) */ 167*393d2d9aSLois Curfman McInnes ierr = VecNorm(g,gnorm); CHKERRQ(ierr); /* gnorm = || g || */ 1687857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 169761aaf1bSLois Curfman McInnes return 0; 1705e76c431SBarry Smith } 1715e76c431SBarry Smith /* ------------------------------------------------------------------ */ 1725e76c431SBarry Smith /*@ 173f59ffdedSLois Curfman McInnes SNESCubicLineSearch - This routine performs a cubic line search and 174f59ffdedSLois Curfman McInnes is the default line search method. 1755e76c431SBarry Smith 1765e76c431SBarry Smith Input Parameters: 1775e42470aSBarry Smith . snes - nonlinear context 1785e76c431SBarry Smith . x - current iterate 1795e76c431SBarry Smith . f - residual evaluated at x 1805e76c431SBarry Smith . y - search direction (contains new iterate on output) 1815e76c431SBarry Smith . w - work vector 1825e76c431SBarry Smith . fnorm - 2-norm of f 1835e76c431SBarry Smith 184*393d2d9aSLois Curfman McInnes Output Parameters: 1855e76c431SBarry Smith . g - residual evaluated at new iterate y 1865e76c431SBarry Smith . y - new iterate (contains search direction on input) 1875e76c431SBarry Smith . gnorm - 2-norm of g 1885e76c431SBarry Smith . ynorm - 2-norm of search length 189761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 1905e76c431SBarry Smith 191c4a48953SLois Curfman McInnes Options Database Key: 192c4a48953SLois Curfman McInnes $ -snes_line_search cubic 193c4a48953SLois Curfman McInnes 1945e76c431SBarry Smith Notes: 1955e76c431SBarry Smith This line search is taken from "Numerical Methods for Unconstrained 1965e76c431SBarry Smith Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 1975e76c431SBarry Smith 19828ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 19928ae5a21SLois Curfman McInnes 200f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 2015e76c431SBarry Smith @*/ 2025e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 203761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag) 2045e76c431SBarry Smith { 2055e42470aSBarry Smith double steptol, initslope; 2065e42470aSBarry Smith double lambdaprev, gnormprev; 2075e76c431SBarry Smith double a, b, d, t1, t2; 2086b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2095e42470aSBarry Smith Scalar cinitslope,clambda; 2106b5873e3SBarry Smith #endif 2115e42470aSBarry Smith int ierr, count; 2125e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 2135e42470aSBarry Smith Scalar one = 1.0,scale; 2145e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 2155e76c431SBarry Smith 2167857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 217761aaf1bSLois Curfman McInnes *flag = 0; 2185e76c431SBarry Smith alpha = neP->alpha; 2195e76c431SBarry Smith maxstep = neP->maxstep; 2205e76c431SBarry Smith steptol = neP->steptol; 2215e76c431SBarry Smith 222*393d2d9aSLois Curfman McInnes ierr = VecNorm(y,ynorm); CHKERRQ(ierr); 2235e76c431SBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 2245e42470aSBarry Smith scale = maxstep/(*ynorm); 2256b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2266b5873e3SBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale)); 2276b5873e3SBarry Smith #else 2285e42470aSBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale); 2296b5873e3SBarry Smith #endif 230*393d2d9aSLois Curfman McInnes ierr = VecScale(&scale,y); CHKERRQ(ierr); 2315e76c431SBarry Smith *ynorm = maxstep; 2325e76c431SBarry Smith } 2335e76c431SBarry Smith minlambda = steptol/(*ynorm); 2345e42470aSBarry Smith #if defined(PETSC_COMPLEX) 235*393d2d9aSLois Curfman McInnes ierr = VecDot(f,y,&cinitslope); CHKERRQ(ierr); 2365e42470aSBarry Smith initslope = real(cinitslope); 2375e42470aSBarry Smith #else 238*393d2d9aSLois Curfman McInnes VecDot(f,y,&initslope); CHKERRQ(ierr); 2395e42470aSBarry Smith #endif 2405e76c431SBarry Smith if (initslope > 0.0) initslope = -initslope; 2415e76c431SBarry Smith if (initslope == 0.0) initslope = -1.0; 2425e76c431SBarry Smith 243*393d2d9aSLois Curfman McInnes ierr = VecCopy(y,w); CHKERRQ(ierr); 244*393d2d9aSLois Curfman McInnes ierr = VecAXPY(&one,x,w); CHKERRQ(ierr); 24578b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 246*393d2d9aSLois Curfman McInnes ierr = VecNorm(g,gnorm); 2475e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 248*393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 2495e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 250d93a2b8dSBarry Smith goto theend; 2515e76c431SBarry Smith } 2525e76c431SBarry Smith 2535e76c431SBarry Smith /* Fit points with quadratic */ 2545e76c431SBarry Smith lambda = 1.0; count = 0; 2555e76c431SBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 2565e76c431SBarry Smith lambdaprev = lambda; 2575e76c431SBarry Smith gnormprev = *gnorm; 2585e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 2595e76c431SBarry Smith lambda = .1*lambda; 2605e76c431SBarry Smith } else lambda = lambdatemp; 261*393d2d9aSLois Curfman McInnes ierr = VecCopy(x,w); CHKERRQ(ierr); 2625e42470aSBarry Smith #if defined(PETSC_COMPLEX) 263*393d2d9aSLois Curfman McInnes clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 2645e42470aSBarry Smith #else 265*393d2d9aSLois Curfman McInnes ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr); 2665e42470aSBarry Smith #endif 26778b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 268*393d2d9aSLois Curfman McInnes ierr = VecNorm(g,gnorm); CHKERRQ(ierr); 2695e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 270*393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 2715e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 272d93a2b8dSBarry Smith goto theend; 2735e76c431SBarry Smith } 2745e76c431SBarry Smith 2755e76c431SBarry Smith /* Fit points with cubic */ 2765e76c431SBarry Smith count = 1; 2775e76c431SBarry Smith while (1) { 2785e76c431SBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 2795e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 2805e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 2815e76c431SBarry Smith fnorm,*gnorm, *ynorm,lambda); 282*393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 283761aaf1bSLois Curfman McInnes *flag = -1; break; 2845e76c431SBarry Smith } 2855e76c431SBarry Smith t1 = *gnorm - fnorm - lambda*initslope; 2865e76c431SBarry Smith t2 = gnormprev - fnorm - lambdaprev*initslope; 2875e76c431SBarry Smith a = (t1/(lambda*lambda) - 2885e76c431SBarry Smith t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 2895e76c431SBarry Smith b = (-lambdaprev*t1/(lambda*lambda) + 2905e76c431SBarry Smith lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 2915e76c431SBarry Smith d = b*b - 3*a*initslope; 2925e76c431SBarry Smith if (d < 0.0) d = 0.0; 2935e76c431SBarry Smith if (a == 0.0) { 2945e76c431SBarry Smith lambdatemp = -initslope/(2.0*b); 2955e76c431SBarry Smith } else { 2965e76c431SBarry Smith lambdatemp = (-b + sqrt(d))/(3.0*a); 2975e76c431SBarry Smith } 2985e76c431SBarry Smith if (lambdatemp > .5*lambda) { 2995e76c431SBarry Smith lambdatemp = .5*lambda; 3005e76c431SBarry Smith } 3015e76c431SBarry Smith lambdaprev = lambda; 3025e76c431SBarry Smith gnormprev = *gnorm; 3035e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3045e76c431SBarry Smith lambda = .1*lambda; 3055e76c431SBarry Smith } 3065e76c431SBarry Smith else lambda = lambdatemp; 307*393d2d9aSLois Curfman McInnes ierr = VecCopy( x, w ); CHKERRQ(ierr); 3085e42470aSBarry Smith #if defined(PETSC_COMPLEX) 309*393d2d9aSLois Curfman McInnes ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 3105e42470aSBarry Smith #else 311*393d2d9aSLois Curfman McInnes ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr); 3125e42470aSBarry Smith #endif 31378b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 314*393d2d9aSLois Curfman McInnes ierr = VecNorm(g,gnorm); CHKERRQ(ierr); 3155e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* is reduction enough */ 316*393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 3175e42470aSBarry Smith PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda); 318761aaf1bSLois Curfman McInnes *flag = -1; break; 3195e76c431SBarry Smith } 3205e76c431SBarry Smith count++; 3215e76c431SBarry Smith } 322d93a2b8dSBarry Smith theend: 3237857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3245e42470aSBarry Smith return 0; 3255e76c431SBarry Smith } 32652392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 3275e42470aSBarry Smith /*@ 3285e42470aSBarry Smith SNESQuadraticLineSearch - This routine performs a cubic line search. 3295e76c431SBarry Smith 3305e42470aSBarry Smith Input Parameters: 331f59ffdedSLois Curfman McInnes . snes - the SNES context 3325e42470aSBarry Smith . x - current iterate 3335e42470aSBarry Smith . f - residual evaluated at x 3345e42470aSBarry Smith . y - search direction (contains new iterate on output) 3355e42470aSBarry Smith . w - work vector 3365e42470aSBarry Smith . fnorm - 2-norm of f 3375e42470aSBarry Smith 338c4a48953SLois Curfman McInnes Output Parameters: 3395e42470aSBarry Smith . g - residual evaluated at new iterate y 3405e42470aSBarry Smith . y - new iterate (contains search direction on input) 3415e42470aSBarry Smith . gnorm - 2-norm of g 3425e42470aSBarry Smith . ynorm - 2-norm of search length 343761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 3445e42470aSBarry Smith 345c4a48953SLois Curfman McInnes Options Database Key: 346c4a48953SLois Curfman McInnes $ -snes_line_search quadratic 347c4a48953SLois Curfman McInnes 3485e42470aSBarry Smith Notes: 349f59ffdedSLois Curfman McInnes Use SNESSetLineSearchRoutine() 350f59ffdedSLois Curfman McInnes to set this routine within the SNES_NLS method. 3515e42470aSBarry Smith 352f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search 353f59ffdedSLois Curfman McInnes 354f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 3555e42470aSBarry Smith @*/ 3565e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 357761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag) 3585e76c431SBarry Smith { 3595e42470aSBarry Smith double steptol, initslope; 3605e42470aSBarry Smith double lambdaprev, gnormprev; 3616b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 3625e42470aSBarry Smith Scalar cinitslope,clambda; 3636b5873e3SBarry Smith #endif 3645e42470aSBarry Smith int ierr,count; 3655e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 3665e42470aSBarry Smith Scalar one = 1.0,scale; 3675e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 3685e76c431SBarry Smith 3697857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 370761aaf1bSLois Curfman McInnes *flag = 0; 3715e42470aSBarry Smith alpha = neP->alpha; 3725e42470aSBarry Smith maxstep = neP->maxstep; 3735e42470aSBarry Smith steptol = neP->steptol; 3745e76c431SBarry Smith 3755e42470aSBarry Smith VecNorm(y, ynorm ); 3765e42470aSBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 3775e42470aSBarry Smith scale = maxstep/(*ynorm); 378*393d2d9aSLois Curfman McInnes ierr = VecScale(&scale,y); CHKERRQ(ierr); 3795e42470aSBarry Smith *ynorm = maxstep; 3805e76c431SBarry Smith } 3815e42470aSBarry Smith minlambda = steptol/(*ynorm); 3825e42470aSBarry Smith #if defined(PETSC_COMPLEX) 383*393d2d9aSLois Curfman McInnes ierr = VecDot(f,y,&cinitslope); CHKERRQ(ierr); 3845e42470aSBarry Smith initslope = real(cinitslope); 3855e42470aSBarry Smith #else 386*393d2d9aSLois Curfman McInnes ierr = VecDot(f,y,&initslope); CHKERRQ(ierr); 3875e42470aSBarry Smith #endif 3885e42470aSBarry Smith if (initslope > 0.0) initslope = -initslope; 3895e42470aSBarry Smith if (initslope == 0.0) initslope = -1.0; 3905e42470aSBarry Smith 391*393d2d9aSLois Curfman McInnes ierr = VecCopy(y,w); CHKERRQ(ierr); 392*393d2d9aSLois Curfman McInnes ierr = VecAXPY(&one,x,w); CHKERRQ(ierr); 39378b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 394*393d2d9aSLois Curfman McInnes ierr = VecNorm(g,gnorm); CHKERRQ(ierr); 3955e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 396*393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 3975e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 398d93a2b8dSBarry Smith goto theend; 3995e42470aSBarry Smith } 4005e42470aSBarry Smith 4015e42470aSBarry Smith /* Fit points with quadratic */ 4025e42470aSBarry Smith lambda = 1.0; count = 0; 4035e42470aSBarry Smith count = 1; 4045e42470aSBarry Smith while (1) { 4055e42470aSBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 4065e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 4075e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 4085e42470aSBarry Smith fnorm,*gnorm, *ynorm,lambda); 409*393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 410761aaf1bSLois Curfman McInnes *flag = -1; break; 4115e42470aSBarry Smith } 4125e42470aSBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 4135e42470aSBarry Smith lambdaprev = lambda; 4145e42470aSBarry Smith gnormprev = *gnorm; 4155e42470aSBarry Smith if (lambdatemp <= .1*lambda) { 4165e42470aSBarry Smith lambda = .1*lambda; 4175e42470aSBarry Smith } else lambda = lambdatemp; 418*393d2d9aSLois Curfman McInnes ierr = VecCopy(x,w); CHKERRQ(ierr); 4195e42470aSBarry Smith #if defined(PETSC_COMPLEX) 420*393d2d9aSLois Curfman McInnes clambda = lambda; ierr = VecAXPY(&clambda,y,w); CHKERRQ(ierr); 4215e42470aSBarry Smith #else 422*393d2d9aSLois Curfman McInnes ierr = VecAXPY(&lambda,y,w); CHKERRQ(ierr); 4235e42470aSBarry Smith #endif 42478b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 425*393d2d9aSLois Curfman McInnes ierr = VecNorm(g,gnorm); CHKERRQ(ierr); 4265e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 427*393d2d9aSLois Curfman McInnes ierr = VecCopy(w,y); CHKERRQ(ierr); 4285e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 42906259719SBarry Smith break; 4305e42470aSBarry Smith } 4315e42470aSBarry Smith count++; 4325e42470aSBarry Smith } 433d93a2b8dSBarry Smith theend: 4347857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4355e42470aSBarry Smith return 0; 4365e76c431SBarry Smith } 4375e76c431SBarry Smith /* ------------------------------------------------------------ */ 438b1f0a012SBarry Smith /*@ 4395e42470aSBarry Smith SNESSetLineSearchRoutine - Sets the line search routine to be used 440fbe28522SBarry Smith by the method SNES_LS. 4415e76c431SBarry Smith 4425e76c431SBarry Smith Input Parameters: 443eafb4bcbSBarry Smith . snes - nonlinear context obtained from SNESCreate() 4445e76c431SBarry Smith . func - pointer to int function 4455e76c431SBarry Smith 446c4a48953SLois Curfman McInnes Available Routines: 447f59ffdedSLois Curfman McInnes . SNESCubicLineSearch() - default line search 448f59ffdedSLois Curfman McInnes . SNESQuadraticLineSearch() - quadratic line search 449f59ffdedSLois Curfman McInnes . SNESNoLineSearch() - the full Newton step (actually not a line search) 4505e76c431SBarry Smith 451c4a48953SLois Curfman McInnes Options Database Keys: 452c4a48953SLois Curfman McInnes $ -snes_line_search [basic,quadratic,cubic] 453c4a48953SLois Curfman McInnes 4545e76c431SBarry Smith Calling sequence of func: 455f59ffdedSLois Curfman McInnes func (SNES snes, Vec x, Vec f, Vec g, Vec y, 456761aaf1bSLois Curfman McInnes Vec w, double fnorm, double *ynorm, 457761aaf1bSLois Curfman McInnes double *gnorm, *flag) 4585e76c431SBarry Smith 4595e76c431SBarry Smith Input parameters for func: 4605e42470aSBarry Smith . snes - nonlinear context 4615e76c431SBarry Smith . x - current iterate 4625e76c431SBarry Smith . f - residual evaluated at x 4635e76c431SBarry Smith . y - search direction (contains new iterate on output) 4645e76c431SBarry Smith . w - work vector 4655e76c431SBarry Smith . fnorm - 2-norm of f 4665e76c431SBarry Smith 4675e76c431SBarry Smith Output parameters for func: 4685e76c431SBarry Smith . g - residual evaluated at new iterate y 4695e76c431SBarry Smith . y - new iterate (contains search direction on input) 4705e76c431SBarry Smith . gnorm - 2-norm of g 4715e76c431SBarry Smith . ynorm - 2-norm of search length 472761aaf1bSLois Curfman McInnes . flag - set to 0 if the line search succeeds; a nonzero integer 473761aaf1bSLois Curfman McInnes on failure. 474f59ffdedSLois Curfman McInnes 475f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine 476f59ffdedSLois Curfman McInnes 477f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch() 4785e76c431SBarry Smith @*/ 4795e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 480761aaf1bSLois Curfman McInnes double,double *,double*,int*) ) 4815e76c431SBarry Smith { 482fbe28522SBarry Smith if ((snes)->type == SNES_NLS) 4835e42470aSBarry Smith ((SNES_LS *)(snes->data))->LineSearch = func; 4845e42470aSBarry Smith return 0; 4855e76c431SBarry Smith } 48652392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 4875e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes) 4885e42470aSBarry Smith { 4895e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 49052392280SLois Curfman McInnes char *p; 49152392280SLois Curfman McInnes if (snes->prefix) p = snes->prefix; else p = "-"; 49252392280SLois Curfman McInnes MPIU_printf(snes->comm," method ls (system of nonlinear equations):\n"); 49352392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search [basic,quadratic,cubic]\n",p); 49452392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search_alpha alpha (default %g)\n",p,ls->alpha); 49552392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search_maxstep max (default %g)\n",p,ls->maxstep); 49652392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search_steptol tol (default %g)\n",p,ls->steptol); 4976b5873e3SBarry Smith return 0; 4985e42470aSBarry Smith } 49952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 500a935fc98SLois Curfman McInnes static int SNESView_LS(PetscObject obj,Viewer viewer) 501a935fc98SLois Curfman McInnes { 502a935fc98SLois Curfman McInnes SNES snes = (SNES)obj; 503a935fc98SLois Curfman McInnes SNES_LS *ls = (SNES_LS *)snes->data; 504a935fc98SLois Curfman McInnes FILE *fd = ViewerFileGetPointer_Private(viewer); 505a935fc98SLois Curfman McInnes char *cstring; 506a935fc98SLois Curfman McInnes 507a935fc98SLois Curfman McInnes if (ls->LineSearch == SNESNoLineSearch) 508a935fc98SLois Curfman McInnes cstring = "SNESNoLineSearch"; 509a935fc98SLois Curfman McInnes else if (ls->LineSearch == SNESQuadraticLineSearch) 510a935fc98SLois Curfman McInnes cstring = "SNESQuadraticLineSearch"; 511a935fc98SLois Curfman McInnes else if (ls->LineSearch == SNESCubicLineSearch) 512a935fc98SLois Curfman McInnes cstring = "SNESCubicLineSearch"; 513a935fc98SLois Curfman McInnes else 514a935fc98SLois Curfman McInnes cstring = "unknown"; 515a935fc98SLois Curfman McInnes MPIU_fprintf(snes->comm,fd," line search variant: %s\n",cstring); 516a935fc98SLois Curfman McInnes MPIU_fprintf(snes->comm,fd," alpha=%g, maxstep=%g, steptol=%g\n", 517a935fc98SLois Curfman McInnes ls->alpha,ls->maxstep,ls->steptol); 518a935fc98SLois Curfman McInnes return 0; 519a935fc98SLois Curfman McInnes } 52052392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 5215e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes) 5225e42470aSBarry Smith { 5235e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5245e42470aSBarry Smith char ver[16]; 5255e42470aSBarry Smith double tmp; 5265e42470aSBarry Smith 527df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) { 5285e42470aSBarry Smith ls->alpha = tmp; 5295e42470aSBarry Smith } 530df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) { 5315e42470aSBarry Smith ls->maxstep = tmp; 5325e42470aSBarry Smith } 533df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) { 5345e42470aSBarry Smith ls->steptol = tmp; 5355e42470aSBarry Smith } 536df60cc22SBarry Smith if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) { 5375e42470aSBarry Smith if (!strcmp(ver,"basic")) { 5385e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESNoLineSearch); 5395e42470aSBarry Smith } 5405e42470aSBarry Smith else if (!strcmp(ver,"quadratic")) { 5415e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch); 5425e42470aSBarry Smith } 5435e42470aSBarry Smith else if (!strcmp(ver,"cubic")) { 5445e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESCubicLineSearch); 5455e42470aSBarry Smith } 5468c48e012SBarry Smith else {SETERRQ(1,"SNESSetFromOptions_LS: Unknown line search");} 5475e42470aSBarry Smith } 5485e42470aSBarry Smith return 0; 5495e42470aSBarry Smith } 5505e42470aSBarry Smith /* ------------------------------------------------------------ */ 5515e42470aSBarry Smith int SNESCreate_LS(SNES snes ) 5525e42470aSBarry Smith { 5535e42470aSBarry Smith SNES_LS *neP; 5545e42470aSBarry Smith 5551a3481a4SLois Curfman McInnes if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1, 5561a3481a4SLois Curfman McInnes "SNESCreate_LS: Valid for SNES_NONLINEAR_EQUATIONS problems only"); 55725c7317fSLois Curfman McInnes snes->type = SNES_EQ_NLS; 55849e3953dSBarry Smith snes->setup = SNESSetUp_LS; 55949e3953dSBarry Smith snes->solve = SNESSolve_LS; 5605e42470aSBarry Smith snes->destroy = SNESDestroy_LS; 561bbb6d6a8SBarry Smith snes->converged = SNESDefaultConverged; 56249e3953dSBarry Smith snes->printhelp = SNESPrintHelp_LS; 56349e3953dSBarry Smith snes->setfromoptions = SNESSetFromOptions_LS; 564a935fc98SLois Curfman McInnes snes->view = SNESView_LS; 5655e42470aSBarry Smith 56678b31e54SBarry Smith neP = PETSCNEW(SNES_LS); CHKPTRQ(neP); 5675e42470aSBarry Smith snes->data = (void *) neP; 5685e42470aSBarry Smith neP->alpha = 1.e-4; 5695e42470aSBarry Smith neP->maxstep = 1.e8; 5705e42470aSBarry Smith neP->steptol = 1.e-12; 5715e42470aSBarry Smith neP->LineSearch = SNESCubicLineSearch; 5725e42470aSBarry Smith return 0; 5735e42470aSBarry Smith } 5745e42470aSBarry Smith 5755e42470aSBarry Smith 5765e42470aSBarry Smith 5775e42470aSBarry Smith 578