15e76c431SBarry Smith #ifndef lint 2*336446d4SLois Curfman McInnes static char vcid[] = "$Id: ls.c,v 1.35 1995/07/27 03:01:17 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 275e42470aSBarry Smith and Schnabel. See the examples in src/snes/examples. 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; 37*336446d4SLois Curfman McInnes SLES sles; 38*336446d4SLois 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 */ 505e42470aSBarry Smith VecNorm(X,&xnorm); /* xnorm = || X || */ 5178b31e54SBarry Smith ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr); /* (+/-) F(X) */ 525e42470aSBarry Smith VecNorm(F,&fnorm); /* 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*336446d4SLois Curfman McInnes /* Set the KSP stopping criteria to use the Eisenstat-Walker method */ 59*336446d4SLois Curfman McInnes if (snes->ksp_ewconv) { 60*336446d4SLois Curfman McInnes ierr = SNESGetSLES(snes,&sles); CHKERRQ(ierr); 61*336446d4SLois Curfman McInnes ierr = SLESGetKSP(sles,&ksp); CHKERRQ(ierr); 62*336446d4SLois Curfman McInnes ierr = KSPSetConvergenceTest(ksp,SNES_KSP_EW_Converged_Private, 63*336446d4SLois Curfman McInnes (void *)snes); CHKERRQ(ierr); 64*336446d4SLois Curfman McInnes } 65*336446d4SLois 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; 865e42470aSBarry Smith VecNorm(X,&xnorm); /* xnorm = || X || */ 87bbb6d6a8SBarry Smith if (snes->monitor) 88bbb6d6a8SBarry Smith {(*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) { 9339e2f89bSBarry Smith VecCopy(X,snes->vec_sol); 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; 1225e42470aSBarry Smith VecFreeVecs(snes->work,snes->nwork); 12378b31e54SBarry Smith PETSCFREE(snes->data); 1245e42470aSBarry Smith return 0; 1255e76c431SBarry Smith } 1265e76c431SBarry Smith /* ------------------------------------------------------------ */ 1275e76c431SBarry Smith /*ARGSUSED*/ 1285e76c431SBarry Smith /*@ 1295e42470aSBarry Smith SNESNoLineSearch - This routine is not a line search at all; 1305e76c431SBarry Smith it simply uses the full Newton step. Thus, this routine is intended 1315e76c431SBarry Smith to serve as a template and is not recommended for general use. 1325e76c431SBarry Smith 1335e76c431SBarry Smith Input Parameters: 1345e42470aSBarry Smith . snes - nonlinear context 1355e76c431SBarry Smith . x - current iterate 1365e76c431SBarry Smith . f - residual evaluated at x 1375e76c431SBarry Smith . y - search direction (contains new iterate on output) 1385e76c431SBarry Smith . w - work vector 1395e76c431SBarry Smith . fnorm - 2-norm of f 1405e76c431SBarry Smith 141c4a48953SLois Curfman McInnes Output Parameters: 1425e76c431SBarry Smith . g - residual evaluated at new iterate y 1435e76c431SBarry Smith . y - new iterate (contains search direction on input) 1445e76c431SBarry Smith . gnorm - 2-norm of g 1455e76c431SBarry Smith . ynorm - 2-norm of search length 146761aaf1bSLois Curfman McInnes . flag - set to 0, indicating a successful line search 1475e76c431SBarry Smith 148c4a48953SLois Curfman McInnes Options Database Key: 149c4a48953SLois Curfman McInnes $ -snes_line_search basic 150c4a48953SLois Curfman McInnes 15128ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 15228ae5a21SLois Curfman McInnes 153f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 154a935fc98SLois Curfman McInnes SNESSetLineSearchRoutine() 1555e76c431SBarry Smith @*/ 1565e42470aSBarry Smith int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 157761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag ) 1585e76c431SBarry Smith { 1595e42470aSBarry Smith int ierr; 1605e42470aSBarry Smith Scalar one = 1.0; 161761aaf1bSLois Curfman McInnes *flag = 0; 1627857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 1635e42470aSBarry Smith VecNorm(y,ynorm); /* ynorm = || y || */ 1645e42470aSBarry Smith VecAXPY(&one,x,y); /* y <- x + y */ 16578b31e54SBarry Smith ierr = SNESComputeFunction(snes,y,g); CHKERRQ(ierr); 1665e42470aSBarry Smith VecNorm(g,gnorm); /* gnorm = || g || */ 1677857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 168761aaf1bSLois Curfman McInnes return 0; 1695e76c431SBarry Smith } 1705e76c431SBarry Smith /* ------------------------------------------------------------------ */ 1715e76c431SBarry Smith /*@ 172f59ffdedSLois Curfman McInnes SNESCubicLineSearch - This routine performs a cubic line search and 173f59ffdedSLois Curfman McInnes is the default line search method. 1745e76c431SBarry Smith 1755e76c431SBarry Smith Input Parameters: 1765e42470aSBarry Smith . snes - nonlinear context 1775e76c431SBarry Smith . x - current iterate 1785e76c431SBarry Smith . f - residual evaluated at x 1795e76c431SBarry Smith . y - search direction (contains new iterate on output) 1805e76c431SBarry Smith . w - work vector 1815e76c431SBarry Smith . fnorm - 2-norm of f 1825e76c431SBarry Smith 1835e76c431SBarry Smith Output parameters: 1845e76c431SBarry Smith . g - residual evaluated at new iterate y 1855e76c431SBarry Smith . y - new iterate (contains search direction on input) 1865e76c431SBarry Smith . gnorm - 2-norm of g 1875e76c431SBarry Smith . ynorm - 2-norm of search length 188761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 1895e76c431SBarry Smith 190c4a48953SLois Curfman McInnes Options Database Key: 191c4a48953SLois Curfman McInnes $ -snes_line_search cubic 192c4a48953SLois Curfman McInnes 1935e76c431SBarry Smith Notes: 1945e76c431SBarry Smith This line search is taken from "Numerical Methods for Unconstrained 1955e76c431SBarry Smith Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 1965e76c431SBarry Smith 19728ae5a21SLois Curfman McInnes .keywords: SNES, nonlinear, line search, cubic 19828ae5a21SLois Curfman McInnes 199f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 2005e76c431SBarry Smith @*/ 2015e42470aSBarry Smith int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 202761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag) 2035e76c431SBarry Smith { 2045e42470aSBarry Smith double steptol, initslope; 2055e42470aSBarry Smith double lambdaprev, gnormprev; 2065e76c431SBarry Smith double a, b, d, t1, t2; 2076b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2085e42470aSBarry Smith Scalar cinitslope,clambda; 2096b5873e3SBarry Smith #endif 2105e42470aSBarry Smith int ierr,count; 2115e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 2125e42470aSBarry Smith Scalar one = 1.0,scale; 2135e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 2145e76c431SBarry Smith 2157857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 216761aaf1bSLois Curfman McInnes *flag = 0; 2175e76c431SBarry Smith alpha = neP->alpha; 2185e76c431SBarry Smith maxstep = neP->maxstep; 2195e76c431SBarry Smith steptol = neP->steptol; 2205e76c431SBarry Smith 2215e42470aSBarry Smith VecNorm(y, ynorm ); 2225e76c431SBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 2235e42470aSBarry Smith scale = maxstep/(*ynorm); 2246b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 2256b5873e3SBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",real(scale)); 2266b5873e3SBarry Smith #else 2275e42470aSBarry Smith PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale); 2286b5873e3SBarry Smith #endif 2295e42470aSBarry Smith VecScale(&scale, y ); 2305e76c431SBarry Smith *ynorm = maxstep; 2315e76c431SBarry Smith } 2325e76c431SBarry Smith minlambda = steptol/(*ynorm); 2335e42470aSBarry Smith #if defined(PETSC_COMPLEX) 2345e42470aSBarry Smith VecDot(f, y, &cinitslope ); 2355e42470aSBarry Smith initslope = real(cinitslope); 2365e42470aSBarry Smith #else 2375e42470aSBarry Smith VecDot(f, y, &initslope ); 2385e42470aSBarry Smith #endif 2395e76c431SBarry Smith if (initslope > 0.0) initslope = -initslope; 2405e76c431SBarry Smith if (initslope == 0.0) initslope = -1.0; 2415e76c431SBarry Smith 2425e42470aSBarry Smith VecCopy(y, w ); 2435e42470aSBarry Smith VecAXPY(&one, x, w ); 24478b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 2455e42470aSBarry Smith VecNorm(g, gnorm ); 2465e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 2475e42470aSBarry Smith VecCopy(w, y ); 2485e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 249d93a2b8dSBarry Smith goto theend; 2505e76c431SBarry Smith } 2515e76c431SBarry Smith 2525e76c431SBarry Smith /* Fit points with quadratic */ 2535e76c431SBarry Smith lambda = 1.0; count = 0; 2545e76c431SBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 2555e76c431SBarry Smith lambdaprev = lambda; 2565e76c431SBarry Smith gnormprev = *gnorm; 2575e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 2585e76c431SBarry Smith lambda = .1*lambda; 2595e76c431SBarry Smith } else lambda = lambdatemp; 2605e42470aSBarry Smith VecCopy(x, w ); 2615e42470aSBarry Smith #if defined(PETSC_COMPLEX) 2625e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 2635e42470aSBarry Smith #else 2645e42470aSBarry Smith VecAXPY(&lambda, y, w ); 2655e42470aSBarry Smith #endif 26678b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 2675e42470aSBarry Smith VecNorm(g, gnorm ); 2685e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 2695e42470aSBarry Smith VecCopy(w, y ); 2705e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 271d93a2b8dSBarry Smith goto theend; 2725e76c431SBarry Smith } 2735e76c431SBarry Smith 2745e76c431SBarry Smith /* Fit points with cubic */ 2755e76c431SBarry Smith count = 1; 2765e76c431SBarry Smith while (1) { 2775e76c431SBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 2785e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 2795e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 2805e76c431SBarry Smith fnorm,*gnorm, *ynorm,lambda); 2815e42470aSBarry Smith VecCopy(w, y ); 282761aaf1bSLois Curfman McInnes *flag = -1; break; 2835e76c431SBarry Smith } 2845e76c431SBarry Smith t1 = *gnorm - fnorm - lambda*initslope; 2855e76c431SBarry Smith t2 = gnormprev - fnorm - lambdaprev*initslope; 2865e76c431SBarry Smith a = (t1/(lambda*lambda) - 2875e76c431SBarry Smith t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 2885e76c431SBarry Smith b = (-lambdaprev*t1/(lambda*lambda) + 2895e76c431SBarry Smith lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 2905e76c431SBarry Smith d = b*b - 3*a*initslope; 2915e76c431SBarry Smith if (d < 0.0) d = 0.0; 2925e76c431SBarry Smith if (a == 0.0) { 2935e76c431SBarry Smith lambdatemp = -initslope/(2.0*b); 2945e76c431SBarry Smith } else { 2955e76c431SBarry Smith lambdatemp = (-b + sqrt(d))/(3.0*a); 2965e76c431SBarry Smith } 2975e76c431SBarry Smith if (lambdatemp > .5*lambda) { 2985e76c431SBarry Smith lambdatemp = .5*lambda; 2995e76c431SBarry Smith } 3005e76c431SBarry Smith lambdaprev = lambda; 3015e76c431SBarry Smith gnormprev = *gnorm; 3025e76c431SBarry Smith if (lambdatemp <= .1*lambda) { 3035e76c431SBarry Smith lambda = .1*lambda; 3045e76c431SBarry Smith } 3055e76c431SBarry Smith else lambda = lambdatemp; 3065e42470aSBarry Smith VecCopy( x, w ); 3075e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3085e42470aSBarry Smith VecAXPY(&clambda, y, w ); 3095e42470aSBarry Smith #else 3105e42470aSBarry Smith VecAXPY(&lambda, y, w ); 3115e42470aSBarry Smith #endif 31278b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 3135e42470aSBarry Smith VecNorm(g, gnorm ); 3145e76c431SBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* is reduction enough */ 3155e42470aSBarry Smith VecCopy(w, y ); 3165e42470aSBarry Smith PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda); 317761aaf1bSLois Curfman McInnes *flag = -1; break; 3185e76c431SBarry Smith } 3195e76c431SBarry Smith count++; 3205e76c431SBarry Smith } 321d93a2b8dSBarry Smith theend: 3227857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 3235e42470aSBarry Smith return 0; 3245e76c431SBarry Smith } 32552392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 3265e42470aSBarry Smith /*@ 3275e42470aSBarry Smith SNESQuadraticLineSearch - This routine performs a cubic line search. 3285e76c431SBarry Smith 3295e42470aSBarry Smith Input Parameters: 330f59ffdedSLois Curfman McInnes . snes - the SNES context 3315e42470aSBarry Smith . x - current iterate 3325e42470aSBarry Smith . f - residual evaluated at x 3335e42470aSBarry Smith . y - search direction (contains new iterate on output) 3345e42470aSBarry Smith . w - work vector 3355e42470aSBarry Smith . fnorm - 2-norm of f 3365e42470aSBarry Smith 337c4a48953SLois Curfman McInnes Output Parameters: 3385e42470aSBarry Smith . g - residual evaluated at new iterate y 3395e42470aSBarry Smith . y - new iterate (contains search direction on input) 3405e42470aSBarry Smith . gnorm - 2-norm of g 3415e42470aSBarry Smith . ynorm - 2-norm of search length 342761aaf1bSLois Curfman McInnes . flag - 0 if line search succeeds; -1 on failure. 3435e42470aSBarry Smith 344c4a48953SLois Curfman McInnes Options Database Key: 345c4a48953SLois Curfman McInnes $ -snes_line_search quadratic 346c4a48953SLois Curfman McInnes 3475e42470aSBarry Smith Notes: 348f59ffdedSLois Curfman McInnes Use SNESSetLineSearchRoutine() 349f59ffdedSLois Curfman McInnes to set this routine within the SNES_NLS method. 3505e42470aSBarry Smith 351f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, quadratic, line search 352f59ffdedSLois Curfman McInnes 353f59ffdedSLois Curfman McInnes .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearchRoutine() 3545e42470aSBarry Smith @*/ 3555e42470aSBarry Smith int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 356761aaf1bSLois Curfman McInnes double fnorm, double *ynorm, double *gnorm,int *flag) 3575e76c431SBarry Smith { 3585e42470aSBarry Smith double steptol, initslope; 3595e42470aSBarry Smith double lambdaprev, gnormprev; 3606b5873e3SBarry Smith #if defined(PETSC_COMPLEX) 3615e42470aSBarry Smith Scalar cinitslope,clambda; 3626b5873e3SBarry Smith #endif 3635e42470aSBarry Smith int ierr,count; 3645e42470aSBarry Smith SNES_LS *neP = (SNES_LS *) snes->data; 3655e42470aSBarry Smith Scalar one = 1.0,scale; 3665e42470aSBarry Smith double maxstep,minlambda,alpha,lambda,lambdatemp; 3675e76c431SBarry Smith 3687857610eSBarry Smith PLogEventBegin(SNES_LineSearch,snes,x,f,g); 369761aaf1bSLois Curfman McInnes *flag = 0; 3705e42470aSBarry Smith alpha = neP->alpha; 3715e42470aSBarry Smith maxstep = neP->maxstep; 3725e42470aSBarry Smith steptol = neP->steptol; 3735e76c431SBarry Smith 3745e42470aSBarry Smith VecNorm(y, ynorm ); 3755e42470aSBarry Smith if (*ynorm > maxstep) { /* Step too big, so scale back */ 3765e42470aSBarry Smith scale = maxstep/(*ynorm); 3775e42470aSBarry Smith VecScale(&scale, y ); 3785e42470aSBarry Smith *ynorm = maxstep; 3795e76c431SBarry Smith } 3805e42470aSBarry Smith minlambda = steptol/(*ynorm); 3815e42470aSBarry Smith #if defined(PETSC_COMPLEX) 3825e42470aSBarry Smith VecDot(f, y, &cinitslope ); 3835e42470aSBarry Smith initslope = real(cinitslope); 3845e42470aSBarry Smith #else 3855e42470aSBarry Smith VecDot(f, y, &initslope ); 3865e42470aSBarry Smith #endif 3875e42470aSBarry Smith if (initslope > 0.0) initslope = -initslope; 3885e42470aSBarry Smith if (initslope == 0.0) initslope = -1.0; 3895e42470aSBarry Smith 3905e42470aSBarry Smith VecCopy(y, w ); 3915e42470aSBarry Smith VecAXPY(&one, x, w ); 39278b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 3935e42470aSBarry Smith VecNorm(g, gnorm ); 3945e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 3955e42470aSBarry Smith VecCopy(w, y ); 3965e42470aSBarry Smith PLogInfo((PetscObject)snes,"Using full step\n"); 397d93a2b8dSBarry Smith goto theend; 3985e42470aSBarry Smith } 3995e42470aSBarry Smith 4005e42470aSBarry Smith /* Fit points with quadratic */ 4015e42470aSBarry Smith lambda = 1.0; count = 0; 4025e42470aSBarry Smith count = 1; 4035e42470aSBarry Smith while (1) { 4045e42470aSBarry Smith if (lambda <= minlambda) { /* bad luck; use full step */ 4055e42470aSBarry Smith PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 4065e42470aSBarry Smith PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 4075e42470aSBarry Smith fnorm,*gnorm, *ynorm,lambda); 4085e42470aSBarry Smith VecCopy(w, y ); 409761aaf1bSLois Curfman McInnes *flag = -1; break; 4105e42470aSBarry Smith } 4115e42470aSBarry Smith lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 4125e42470aSBarry Smith lambdaprev = lambda; 4135e42470aSBarry Smith gnormprev = *gnorm; 4145e42470aSBarry Smith if (lambdatemp <= .1*lambda) { 4155e42470aSBarry Smith lambda = .1*lambda; 4165e42470aSBarry Smith } else lambda = lambdatemp; 4175e42470aSBarry Smith VecCopy(x, w ); 4185e42470aSBarry Smith #if defined(PETSC_COMPLEX) 4195e42470aSBarry Smith clambda = lambda; VecAXPY(&clambda, y, w ); 4205e42470aSBarry Smith #else 4215e42470aSBarry Smith VecAXPY(&lambda, y, w ); 4225e42470aSBarry Smith #endif 42378b31e54SBarry Smith ierr = SNESComputeFunction(snes,w,g); CHKERRQ(ierr); 4245e42470aSBarry Smith VecNorm(g, gnorm ); 4255e42470aSBarry Smith if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 4265e42470aSBarry Smith VecCopy(w, y ); 4275e42470aSBarry Smith PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 42806259719SBarry Smith break; 4295e42470aSBarry Smith } 4305e42470aSBarry Smith count++; 4315e42470aSBarry Smith } 432d93a2b8dSBarry Smith theend: 4337857610eSBarry Smith PLogEventEnd(SNES_LineSearch,snes,x,f,g); 4345e42470aSBarry Smith return 0; 4355e76c431SBarry Smith } 4365e76c431SBarry Smith /* ------------------------------------------------------------ */ 437b1f0a012SBarry Smith /*@ 4385e42470aSBarry Smith SNESSetLineSearchRoutine - Sets the line search routine to be used 439fbe28522SBarry Smith by the method SNES_LS. 4405e76c431SBarry Smith 4415e76c431SBarry Smith Input Parameters: 442eafb4bcbSBarry Smith . snes - nonlinear context obtained from SNESCreate() 4435e76c431SBarry Smith . func - pointer to int function 4445e76c431SBarry Smith 445c4a48953SLois Curfman McInnes Available Routines: 446f59ffdedSLois Curfman McInnes . SNESCubicLineSearch() - default line search 447f59ffdedSLois Curfman McInnes . SNESQuadraticLineSearch() - quadratic line search 448f59ffdedSLois Curfman McInnes . SNESNoLineSearch() - the full Newton step (actually not a line search) 4495e76c431SBarry Smith 450c4a48953SLois Curfman McInnes Options Database Keys: 451c4a48953SLois Curfman McInnes $ -snes_line_search [basic,quadratic,cubic] 452c4a48953SLois Curfman McInnes 4535e76c431SBarry Smith Calling sequence of func: 454f59ffdedSLois Curfman McInnes func (SNES snes, Vec x, Vec f, Vec g, Vec y, 455761aaf1bSLois Curfman McInnes Vec w, double fnorm, double *ynorm, 456761aaf1bSLois Curfman McInnes double *gnorm, *flag) 4575e76c431SBarry Smith 4585e76c431SBarry Smith Input parameters for func: 4595e42470aSBarry Smith . snes - nonlinear context 4605e76c431SBarry Smith . x - current iterate 4615e76c431SBarry Smith . f - residual evaluated at x 4625e76c431SBarry Smith . y - search direction (contains new iterate on output) 4635e76c431SBarry Smith . w - work vector 4645e76c431SBarry Smith . fnorm - 2-norm of f 4655e76c431SBarry Smith 4665e76c431SBarry Smith Output parameters for func: 4675e76c431SBarry Smith . g - residual evaluated at new iterate y 4685e76c431SBarry Smith . y - new iterate (contains search direction on input) 4695e76c431SBarry Smith . gnorm - 2-norm of g 4705e76c431SBarry Smith . ynorm - 2-norm of search length 471761aaf1bSLois Curfman McInnes . flag - set to 0 if the line search succeeds; a nonzero integer 472761aaf1bSLois Curfman McInnes on failure. 473f59ffdedSLois Curfman McInnes 474f59ffdedSLois Curfman McInnes .keywords: SNES, nonlinear, set, line search, routine 475f59ffdedSLois Curfman McInnes 476f59ffdedSLois Curfman McInnes .seealso: SNESNoLineSearch(), SNESQuadraticLineSearch(), SNESCubicLineSearch() 4775e76c431SBarry Smith @*/ 4785e42470aSBarry Smith int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 479761aaf1bSLois Curfman McInnes double,double *,double*,int*) ) 4805e76c431SBarry Smith { 481fbe28522SBarry Smith if ((snes)->type == SNES_NLS) 4825e42470aSBarry Smith ((SNES_LS *)(snes->data))->LineSearch = func; 4835e42470aSBarry Smith return 0; 4845e76c431SBarry Smith } 48552392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 4865e42470aSBarry Smith static int SNESPrintHelp_LS(SNES snes) 4875e42470aSBarry Smith { 4885e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 48952392280SLois Curfman McInnes char *p; 49052392280SLois Curfman McInnes if (snes->prefix) p = snes->prefix; else p = "-"; 49152392280SLois Curfman McInnes MPIU_printf(snes->comm," method ls (system of nonlinear equations):\n"); 49252392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search [basic,quadratic,cubic]\n",p); 49352392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search_alpha alpha (default %g)\n",p,ls->alpha); 49452392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search_maxstep max (default %g)\n",p,ls->maxstep); 49552392280SLois Curfman McInnes MPIU_printf(snes->comm," %ssnes_line_search_steptol tol (default %g)\n",p,ls->steptol); 4966b5873e3SBarry Smith return 0; 4975e42470aSBarry Smith } 49852392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 499a935fc98SLois Curfman McInnes static int SNESView_LS(PetscObject obj,Viewer viewer) 500a935fc98SLois Curfman McInnes { 501a935fc98SLois Curfman McInnes SNES snes = (SNES)obj; 502a935fc98SLois Curfman McInnes SNES_LS *ls = (SNES_LS *)snes->data; 503a935fc98SLois Curfman McInnes FILE *fd = ViewerFileGetPointer_Private(viewer); 504a935fc98SLois Curfman McInnes char *cstring; 505a935fc98SLois Curfman McInnes 506a935fc98SLois Curfman McInnes if (ls->LineSearch == SNESNoLineSearch) 507a935fc98SLois Curfman McInnes cstring = "SNESNoLineSearch"; 508a935fc98SLois Curfman McInnes else if (ls->LineSearch == SNESQuadraticLineSearch) 509a935fc98SLois Curfman McInnes cstring = "SNESQuadraticLineSearch"; 510a935fc98SLois Curfman McInnes else if (ls->LineSearch == SNESCubicLineSearch) 511a935fc98SLois Curfman McInnes cstring = "SNESCubicLineSearch"; 512a935fc98SLois Curfman McInnes else 513a935fc98SLois Curfman McInnes cstring = "unknown"; 514a935fc98SLois Curfman McInnes MPIU_fprintf(snes->comm,fd," line search variant: %s\n",cstring); 515a935fc98SLois Curfman McInnes MPIU_fprintf(snes->comm,fd," alpha=%g, maxstep=%g, steptol=%g\n", 516a935fc98SLois Curfman McInnes ls->alpha,ls->maxstep,ls->steptol); 517a935fc98SLois Curfman McInnes return 0; 518a935fc98SLois Curfman McInnes } 51952392280SLois Curfman McInnes /* ------------------------------------------------------------------ */ 5205e42470aSBarry Smith static int SNESSetFromOptions_LS(SNES snes) 5215e42470aSBarry Smith { 5225e42470aSBarry Smith SNES_LS *ls = (SNES_LS *)snes->data; 5235e42470aSBarry Smith char ver[16]; 5245e42470aSBarry Smith double tmp; 5255e42470aSBarry Smith 526df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_alpa",&tmp)) { 5275e42470aSBarry Smith ls->alpha = tmp; 5285e42470aSBarry Smith } 529df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_maxstep",&tmp)) { 5305e42470aSBarry Smith ls->maxstep = tmp; 5315e42470aSBarry Smith } 532df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-snes_line_search_steptol",&tmp)) { 5335e42470aSBarry Smith ls->steptol = tmp; 5345e42470aSBarry Smith } 535df60cc22SBarry Smith if (OptionsGetString(snes->prefix,"-snes_line_search",ver,16)) { 5365e42470aSBarry Smith if (!strcmp(ver,"basic")) { 5375e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESNoLineSearch); 5385e42470aSBarry Smith } 5395e42470aSBarry Smith else if (!strcmp(ver,"quadratic")) { 5405e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch); 5415e42470aSBarry Smith } 5425e42470aSBarry Smith else if (!strcmp(ver,"cubic")) { 5435e42470aSBarry Smith SNESSetLineSearchRoutine(snes,SNESCubicLineSearch); 5445e42470aSBarry Smith } 5458c48e012SBarry Smith else {SETERRQ(1,"SNESSetFromOptions_LS: Unknown line search");} 5465e42470aSBarry Smith } 5475e42470aSBarry Smith return 0; 5485e42470aSBarry Smith } 5495e42470aSBarry Smith /* ------------------------------------------------------------ */ 5505e42470aSBarry Smith int SNESCreate_LS(SNES snes ) 5515e42470aSBarry Smith { 5525e42470aSBarry Smith SNES_LS *neP; 5535e42470aSBarry Smith 5541a3481a4SLois Curfman McInnes if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1, 5551a3481a4SLois Curfman McInnes "SNESCreate_LS: Valid for SNES_NONLINEAR_EQUATIONS problems only"); 55625c7317fSLois Curfman McInnes snes->type = SNES_EQ_NLS; 55749e3953dSBarry Smith snes->setup = SNESSetUp_LS; 55849e3953dSBarry Smith snes->solve = SNESSolve_LS; 5595e42470aSBarry Smith snes->destroy = SNESDestroy_LS; 560bbb6d6a8SBarry Smith snes->converged = SNESDefaultConverged; 56149e3953dSBarry Smith snes->printhelp = SNESPrintHelp_LS; 56249e3953dSBarry Smith snes->setfromoptions = SNESSetFromOptions_LS; 563a935fc98SLois Curfman McInnes snes->view = SNESView_LS; 5645e42470aSBarry Smith 56578b31e54SBarry Smith neP = PETSCNEW(SNES_LS); CHKPTRQ(neP); 5665e42470aSBarry Smith snes->data = (void *) neP; 5675e42470aSBarry Smith neP->alpha = 1.e-4; 5685e42470aSBarry Smith neP->maxstep = 1.e8; 5695e42470aSBarry Smith neP->steptol = 1.e-12; 5705e42470aSBarry Smith neP->LineSearch = SNESCubicLineSearch; 5715e42470aSBarry Smith return 0; 5725e42470aSBarry Smith } 5735e42470aSBarry Smith 5745e42470aSBarry Smith 5755e42470aSBarry Smith 5765e42470aSBarry Smith 577