1 #ifdef PETSC_RCS_HEADER 2 static char vcid[] = "$Id: ls.c,v 1.141 1999/09/02 14:54:04 bsmith Exp bsmith $"; 3 #endif 4 5 #include "src/snes/impls/ls/ls.h" 6 7 /* -------------------------------------------------------------------- 8 9 This file implements a truncated Newton method with a line search, 10 for solving a system of nonlinear equations, using the SLES, Vec, 11 and Mat interfaces for linear solvers, vectors, and matrices, 12 respectively. 13 14 The following basic routines are required for each nonlinear solver: 15 SNESCreate_XXX() - Creates a nonlinear solver context 16 SNESSetFromOptions_XXX() - Sets runtime options 17 SNESSolve_XXX() - Solves the nonlinear system 18 SNESDestroy_XXX() - Destroys the nonlinear solver context 19 The suffix "_XXX" denotes a particular implementation, in this case 20 we use _EQ_LS (e.g., SNESCreate_EQ_LS, SNESSolve_EQ_LS) for solving 21 systems of nonlinear equations (EQ) with a line search (LS) method. 22 These routines are actually called via the common user interface 23 routines SNESCreate(), SNESSetFromOptions(), SNESSolve(), and 24 SNESDestroy(), so the application code interface remains identical 25 for all nonlinear solvers. 26 27 Another key routine is: 28 SNESSetUp_XXX() - Prepares for the use of a nonlinear solver 29 by setting data structures and options. The interface routine SNESSetUp() 30 is not usually called directly by the user, but instead is called by 31 SNESSolve() if necessary. 32 33 Additional basic routines are: 34 SNESPrintHelp_XXX() - Prints nonlinear solver runtime options 35 SNESView_XXX() - Prints details of runtime options that 36 have actually been used. 37 These are called by application codes via the interface routines 38 SNESPrintHelp() and SNESView(). 39 40 The various types of solvers (preconditioners, Krylov subspace methods, 41 nonlinear solvers, timesteppers) are all organized similarly, so the 42 above description applies to these categories also. 43 44 -------------------------------------------------------------------- */ 45 /* 46 SNESSolve_EQ_LS - Solves a nonlinear system with a truncated Newton 47 method with a line search. 48 49 Input Parameters: 50 . snes - the SNES context 51 52 Output Parameter: 53 . outits - number of iterations until termination 54 55 Application Interface Routine: SNESSolve() 56 57 Notes: 58 This implements essentially a truncated Newton method with a 59 line search. By default a cubic backtracking line search 60 is employed, as described in the text "Numerical Methods for 61 Unconstrained Optimization and Nonlinear Equations" by Dennis 62 and Schnabel. 63 */ 64 #undef __FUNC__ 65 #define __FUNC__ "SNESSolve_EQ_LS" 66 int SNESSolve_EQ_LS(SNES snes,int *outits) 67 { 68 SNES_LS *neP = (SNES_LS *) snes->data; 69 int maxits, i, ierr, lits, lsfail; 70 MatStructure flg = DIFFERENT_NONZERO_PATTERN; 71 double fnorm, gnorm, xnorm, ynorm; 72 Vec Y, X, F, G, W, TMP; 73 SNESConvergedReason reason; 74 75 PetscFunctionBegin; 76 snes->reason = SNES_CONVERGED_ITERATING; 77 78 maxits = snes->max_its; /* maximum number of iterations */ 79 X = snes->vec_sol; /* solution vector */ 80 F = snes->vec_func; /* residual vector */ 81 Y = snes->work[0]; /* work vectors */ 82 G = snes->work[1]; 83 W = snes->work[2]; 84 85 ierr = SNESComputeFunction(snes,X,F);CHKERRQ(ierr); /* F(X) */ 86 ierr = VecNorm(F,NORM_2,&fnorm);CHKERRQ(ierr); /* fnorm <- ||F|| */ 87 ierr = PetscAMSTakeAccess(snes);CHKERRQ(ierr); 88 snes->iter = 0; 89 snes->norm = fnorm; 90 ierr = PetscAMSGrantAccess(snes);CHKERRQ(ierr); 91 SNESLogConvHistory(snes,fnorm,0); 92 SNESMonitor(snes,0,fnorm); 93 94 if (fnorm < snes->atol) {*outits = 0; snes->reason = SNES_CONVERGED_FNORM_ABS; PetscFunctionReturn(0);} 95 96 /* set parameter for default relative tolerance convergence test */ 97 snes->ttol = fnorm*snes->rtol; 98 99 for ( i=0; i<maxits; i++ ) { 100 101 /* Solve J Y = F, where J is Jacobian matrix */ 102 ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr); 103 ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr); 104 ierr = SLESSolve(snes->sles,F,Y,&lits);CHKERRQ(ierr); 105 snes->linear_its += PetscAbsInt(lits); 106 PLogInfo(snes,"SNESSolve_EQ_LS: iter=%d, linear solve iterations=%d\n",snes->iter,lits); 107 108 /* Compute a (scaled) negative update in the line search routine: 109 Y <- X - lambda*Y 110 and evaluate G(Y) = function(Y)) 111 */ 112 ierr = VecCopy(Y,snes->vec_sol_update_always);CHKERRQ(ierr); 113 ierr = (*neP->LineSearch)(snes,neP->lsP,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lsfail);CHKERRQ(ierr); 114 PLogInfo(snes,"SNESSolve_EQ_LS: fnorm=%g, gnorm=%g, ynorm=%g, lsfail=%d\n",fnorm,gnorm,ynorm,lsfail); 115 if (lsfail) snes->nfailures++; 116 117 TMP = F; F = G; snes->vec_func_always = F; G = TMP; 118 TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP; 119 fnorm = gnorm; 120 121 ierr = PetscAMSTakeAccess(snes);CHKERRQ(ierr); 122 snes->iter = i+1; 123 snes->norm = fnorm; 124 ierr = PetscAMSGrantAccess(snes);CHKERRQ(ierr); 125 SNESLogConvHistory(snes,fnorm,lits); 126 SNESMonitor(snes,i+1,fnorm); 127 128 /* Test for convergence */ 129 if (snes->converged) { 130 ierr = VecNorm(X,NORM_2,&xnorm);CHKERRQ(ierr); /* xnorm = || X || */ 131 ierr = (*snes->converged)(snes,xnorm,ynorm,fnorm,&reason,snes->cnvP);CHKERRQ(ierr); 132 if (reason) { 133 break; 134 } 135 } 136 } 137 if (X != snes->vec_sol) { 138 ierr = VecCopy(X,snes->vec_sol);CHKERRQ(ierr); 139 snes->vec_sol_always = snes->vec_sol; 140 snes->vec_func_always = snes->vec_func; 141 } 142 if (i == maxits) { 143 PLogInfo(snes,"SNESSolve_EQ_LS: Maximum number of iterations has been reached: %d\n",maxits); 144 i--; 145 reason = SNES_DIVERGED_MAX_IT; 146 } 147 snes->reason = reason; 148 *outits = i+1; 149 PetscFunctionReturn(0); 150 } 151 /* -------------------------------------------------------------------------- */ 152 /* 153 SNESSetUp_EQ_LS - Sets up the internal data structures for the later use 154 of the SNES_EQ_LS nonlinear solver. 155 156 Input Parameter: 157 . snes - the SNES context 158 . x - the solution vector 159 160 Application Interface Routine: SNESSetUp() 161 162 Notes: 163 For basic use of the SNES solvers the user need not explicitly call 164 SNESSetUp(), since these actions will automatically occur during 165 the call to SNESSolve(). 166 */ 167 #undef __FUNC__ 168 #define __FUNC__ "SNESSetUp_EQ_LS" 169 int SNESSetUp_EQ_LS(SNES snes) 170 { 171 int ierr; 172 173 PetscFunctionBegin; 174 snes->nwork = 4; 175 ierr = VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work);CHKERRQ(ierr); 176 PLogObjectParents(snes,snes->nwork,snes->work); 177 snes->vec_sol_update_always = snes->work[3]; 178 PetscFunctionReturn(0); 179 } 180 /* -------------------------------------------------------------------------- */ 181 /* 182 SNESDestroy_EQ_LS - Destroys the private SNES_LS context that was created 183 with SNESCreate_EQ_LS(). 184 185 Input Parameter: 186 . snes - the SNES context 187 188 Application Interface Routine: SNESDestroy() 189 */ 190 #undef __FUNC__ 191 #define __FUNC__ "SNESDestroy_EQ_LS" 192 int SNESDestroy_EQ_LS(SNES snes) 193 { 194 int ierr; 195 196 PetscFunctionBegin; 197 if (snes->nwork) { 198 ierr = VecDestroyVecs(snes->work,snes->nwork);CHKERRQ(ierr); 199 } 200 ierr = PetscFree(snes->data);CHKERRQ(ierr); 201 PetscFunctionReturn(0); 202 } 203 /* -------------------------------------------------------------------------- */ 204 #undef __FUNC__ 205 #define __FUNC__ "SNESNoLineSearch" 206 207 /*@C 208 SNESNoLineSearch - This routine is not a line search at all; 209 it simply uses the full Newton step. Thus, this routine is intended 210 to serve as a template and is not recommended for general use. 211 212 Collective on SNES and Vec 213 214 Input Parameters: 215 + snes - nonlinear context 216 . lsctx - optional context for line search (not used here) 217 . x - current iterate 218 . f - residual evaluated at x 219 . y - search direction (contains new iterate on output) 220 . w - work vector 221 - fnorm - 2-norm of f 222 223 Output Parameters: 224 + g - residual evaluated at new iterate y 225 . y - new iterate (contains search direction on input) 226 . gnorm - 2-norm of g 227 . ynorm - 2-norm of search length 228 - flag - set to 0, indicating a successful line search 229 230 Options Database Key: 231 . -snes_eq_ls basic - Activates SNESNoLineSearch() 232 233 Level: advanced 234 235 .keywords: SNES, nonlinear, line search, cubic 236 237 .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 238 SNESSetLineSearch(), SNESNoLineSearchNoNorms() 239 @*/ 240 int SNESNoLineSearch(SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 241 double fnorm, double *ynorm, double *gnorm,int *flag ) 242 { 243 int ierr; 244 Scalar mone = -1.0; 245 SNES_LS *neP = (SNES_LS *) snes->data; 246 PetscTruth change_y = PETSC_FALSE; 247 248 PetscFunctionBegin; 249 *flag = 0; 250 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 251 ierr = VecNorm(y,NORM_2,ynorm);CHKERRQ(ierr); /* ynorm = || y || */ 252 ierr = VecAYPX(&mone,x,y);CHKERRQ(ierr); /* y <- y - x */ 253 if (neP->CheckStep) { 254 ierr = (*neP->CheckStep)(snes,neP->checkP,y,&change_y);CHKERRQ(ierr); 255 } 256 ierr = SNESComputeFunction(snes,y,g);CHKERRQ(ierr); /* Compute F(y) */ 257 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); /* gnorm = || g || */ 258 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 259 PetscFunctionReturn(0); 260 } 261 /* -------------------------------------------------------------------------- */ 262 #undef __FUNC__ 263 #define __FUNC__ "SNESNoLineSearchNoNorms" 264 265 /*@C 266 SNESNoLineSearchNoNorms - This routine is not a line search at 267 all; it simply uses the full Newton step. This version does not 268 even compute the norm of the function or search direction; this 269 is intended only when you know the full step is fine and are 270 not checking for convergence of the nonlinear iteration (for 271 example, you are running always for a fixed number of Newton steps). 272 273 Collective on SNES and Vec 274 275 Input Parameters: 276 + snes - nonlinear context 277 . lsctx - optional context for line search (not used here) 278 . x - current iterate 279 . f - residual evaluated at x 280 . y - search direction (contains new iterate on output) 281 . w - work vector 282 - fnorm - 2-norm of f 283 284 Output Parameters: 285 + g - residual evaluated at new iterate y 286 . gnorm - not changed 287 . ynorm - not changed 288 - flag - set to 0, indicating a successful line search 289 290 Options Database Key: 291 . -snes_eq_ls basicnonorms - Activates SNESNoLineSearchNoNorms() 292 293 Notes: 294 SNESNoLineSearchNoNorms() must be used in conjunction with 295 either the options 296 $ -snes_no_convergence_test -snes_max_it <its> 297 or alternatively a user-defined custom test set via 298 SNESSetConvergenceTest(); otherwise, the SNES solver will 299 generate an error. 300 301 The residual norms printed by monitoring routines such as 302 SNESDefaultMonitor() (as activated via -snes_monitor) will not be 303 correct, since they are not computed. 304 305 Level: advanced 306 307 .keywords: SNES, nonlinear, line search, cubic 308 309 .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), 310 SNESSetLineSearch(), SNESNoLineSearch() 311 @*/ 312 int SNESNoLineSearchNoNorms(SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 313 double fnorm, double *ynorm, double *gnorm,int *flag ) 314 { 315 int ierr; 316 Scalar mone = -1.0; 317 SNES_LS *neP = (SNES_LS *) snes->data; 318 PetscTruth change_y = PETSC_FALSE; 319 320 PetscFunctionBegin; 321 *flag = 0; 322 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 323 ierr = VecAYPX(&mone,x,y);CHKERRQ(ierr); /* y <- y - x */ 324 if (neP->CheckStep) { 325 ierr = (*neP->CheckStep)(snes,neP->checkP,y,&change_y);CHKERRQ(ierr); 326 } 327 ierr = SNESComputeFunction(snes,y,g);CHKERRQ(ierr); /* Compute F(y) */ 328 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 329 PetscFunctionReturn(0); 330 } 331 /* -------------------------------------------------------------------------- */ 332 #undef __FUNC__ 333 #define __FUNC__ "SNESCubicLineSearch" 334 /*@C 335 SNESCubicLineSearch - Performs a cubic line search (default line search method). 336 337 Collective on SNES 338 339 Input Parameters: 340 + snes - nonlinear context 341 . lsctx - optional context for line search (not used here) 342 . x - current iterate 343 . f - residual evaluated at x 344 . y - search direction (contains new iterate on output) 345 . w - work vector 346 - fnorm - 2-norm of f 347 348 Output Parameters: 349 + g - residual evaluated at new iterate y 350 . y - new iterate (contains search direction on input) 351 . gnorm - 2-norm of g 352 . ynorm - 2-norm of search length 353 - flag - 0 if line search succeeds; -1 on failure. 354 355 Options Database Key: 356 $ -snes_eq_ls cubic - Activates SNESCubicLineSearch() 357 358 Notes: 359 This line search is taken from "Numerical Methods for Unconstrained 360 Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 361 362 Level: advanced 363 364 .keywords: SNES, nonlinear, line search, cubic 365 366 .seealso: SNESQuadraticLineSearch(), SNESNoLineSearch(), SNESSetLineSearch(), SNESNoLineSearchNoNorms() 367 @*/ 368 int SNESCubicLineSearch(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w, 369 double fnorm,double *ynorm,double *gnorm,int *flag) 370 { 371 /* 372 Note that for line search purposes we work with with the related 373 minimization problem: 374 min z(x): R^n -> R, 375 where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2. 376 */ 377 378 double steptol, initslope, lambdaprev, gnormprev, a, b, d, t1, t2; 379 double maxstep, minlambda, alpha, lambda, lambdatemp, lambdaneg; 380 #if defined(PETSC_USE_COMPLEX) 381 Scalar cinitslope, clambda; 382 #endif 383 int ierr, count; 384 SNES_LS *neP = (SNES_LS *) snes->data; 385 Scalar mone = -1.0, scale; 386 PetscTruth change_y = PETSC_FALSE; 387 388 PetscFunctionBegin; 389 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 390 *flag = 0; 391 alpha = neP->alpha; 392 maxstep = neP->maxstep; 393 steptol = neP->steptol; 394 395 ierr = VecNorm(y,NORM_2,ynorm);CHKERRQ(ierr); 396 if (*ynorm < snes->atol) { 397 PLogInfo(snes,"SNESCubicLineSearch: Search direction and size are nearly 0\n"); 398 *gnorm = fnorm; 399 ierr = VecCopy(x,y);CHKERRQ(ierr); 400 ierr = VecCopy(f,g);CHKERRQ(ierr); 401 goto theend1; 402 } 403 if (*ynorm > maxstep) { /* Step too big, so scale back */ 404 scale = maxstep/(*ynorm); 405 #if defined(PETSC_USE_COMPLEX) 406 PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",PetscReal(scale)); 407 #else 408 PLogInfo(snes,"SNESCubicLineSearch: Scaling step by %g\n",scale); 409 #endif 410 ierr = VecScale(&scale,y);CHKERRQ(ierr); 411 *ynorm = maxstep; 412 } 413 minlambda = steptol/(*ynorm); 414 ierr = MatMult(snes->jacobian,y,w);CHKERRQ(ierr); 415 #if defined(PETSC_USE_COMPLEX) 416 ierr = VecDot(f,w,&cinitslope);CHKERRQ(ierr); 417 initslope = PetscReal(cinitslope); 418 #else 419 ierr = VecDot(f,w,&initslope);CHKERRQ(ierr); 420 #endif 421 if (initslope > 0.0) initslope = -initslope; 422 if (initslope == 0.0) initslope = -1.0; 423 424 ierr = VecCopy(y,w);CHKERRQ(ierr); 425 ierr = VecAYPX(&mone,x,w);CHKERRQ(ierr); 426 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 427 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 428 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* Sufficient reduction */ 429 ierr = VecCopy(w,y);CHKERRQ(ierr); 430 PLogInfo(snes,"SNESCubicLineSearch: Using full step\n"); 431 goto theend1; 432 } 433 434 /* Fit points with quadratic */ 435 lambda = 1.0; 436 lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 437 lambdaprev = lambda; 438 gnormprev = *gnorm; 439 if (lambdatemp <= .1*lambda) lambda = .1*lambda; 440 else lambda = lambdatemp; 441 ierr = VecCopy(x,w);CHKERRQ(ierr); 442 lambdaneg = -lambda; 443 #if defined(PETSC_USE_COMPLEX) 444 clambda = lambdaneg; ierr = VecAXPY(&clambda,y,w);CHKERRQ(ierr); 445 #else 446 ierr = VecAXPY(&lambdaneg,y,w);CHKERRQ(ierr); 447 #endif 448 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 449 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 450 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* sufficient reduction */ 451 ierr = VecCopy(w,y);CHKERRQ(ierr); 452 PLogInfo(snes,"SNESCubicLineSearch: Quadratically determined step, lambda=%g\n",lambda); 453 goto theend1; 454 } 455 456 /* Fit points with cubic */ 457 count = 1; 458 while (1) { 459 if (lambda <= minlambda) { /* bad luck; use full step */ 460 PLogInfo(snes,"SNESCubicLineSearch:Unable to find good step length! %d \n",count); 461 PLogInfo(snes,"SNESCubicLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n", 462 fnorm,*gnorm,*ynorm,lambda,initslope); 463 ierr = VecCopy(w,y);CHKERRQ(ierr); 464 *flag = -1; break; 465 } 466 t1 = ((*gnorm)*(*gnorm) - fnorm*fnorm)*0.5 - lambda*initslope; 467 t2 = (gnormprev*gnormprev - fnorm*fnorm)*0.5 - lambdaprev*initslope; 468 a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 469 b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 470 d = b*b - 3*a*initslope; 471 if (d < 0.0) d = 0.0; 472 if (a == 0.0) { 473 lambdatemp = -initslope/(2.0*b); 474 } else { 475 lambdatemp = (-b + sqrt(d))/(3.0*a); 476 } 477 if (lambdatemp > .5*lambda) { 478 lambdatemp = .5*lambda; 479 } 480 lambdaprev = lambda; 481 gnormprev = *gnorm; 482 if (lambdatemp <= .1*lambda) { 483 lambda = .1*lambda; 484 } 485 else lambda = lambdatemp; 486 ierr = VecCopy( x, w );CHKERRQ(ierr); 487 lambdaneg = -lambda; 488 #if defined(PETSC_USE_COMPLEX) 489 clambda = lambdaneg; 490 ierr = VecAXPY(&clambda,y,w);CHKERRQ(ierr); 491 #else 492 ierr = VecAXPY(&lambdaneg,y,w);CHKERRQ(ierr); 493 #endif 494 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 495 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 496 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* is reduction enough? */ 497 ierr = VecCopy(w,y);CHKERRQ(ierr); 498 PLogInfo(snes,"SNESCubicLineSearch: Cubically determined step, lambda=%g\n",lambda); 499 goto theend1; 500 } else { 501 PLogInfo(snes,"SNESCubicLineSearch: Cubic step no good, shrinking lambda, lambda=%g\n",lambda); 502 } 503 count++; 504 } 505 theend1: 506 /* Optional user-defined check for line search step validity */ 507 if (neP->CheckStep) { 508 ierr = (*neP->CheckStep)(snes,neP->checkP,y,&change_y);CHKERRQ(ierr); 509 if (change_y == PETSC_TRUE) { /* recompute the function if the step has changed */ 510 ierr = SNESComputeFunction(snes,y,g);CHKERRQ(ierr); 511 ierr = VecNormBegin(y,NORM_2,ynorm);CHKERRQ(ierr); 512 ierr = VecNormBegin(g,NORM_2,gnorm);CHKERRQ(ierr); 513 ierr = VecNormEnd(y,NORM_2,ynorm);CHKERRQ(ierr); 514 ierr = VecNormEnd(g,NORM_2,gnorm);CHKERRQ(ierr); 515 } 516 } 517 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 518 PetscFunctionReturn(0); 519 } 520 /* -------------------------------------------------------------------------- */ 521 #undef __FUNC__ 522 #define __FUNC__ "SNESQuadraticLineSearch" 523 /*@C 524 SNESQuadraticLineSearch - Performs a quadratic line search. 525 526 Collective on SNES and Vec 527 528 Input Parameters: 529 + snes - the SNES context 530 . lsctx - optional context for line search (not used here) 531 . x - current iterate 532 . f - residual evaluated at x 533 . y - search direction (contains new iterate on output) 534 . w - work vector 535 - fnorm - 2-norm of f 536 537 Output Parameters: 538 + g - residual evaluated at new iterate y 539 . y - new iterate (contains search direction on input) 540 . gnorm - 2-norm of g 541 . ynorm - 2-norm of search length 542 - flag - 0 if line search succeeds; -1 on failure. 543 544 Options Database Key: 545 . -snes_eq_ls quadratic - Activates SNESQuadraticLineSearch() 546 547 Notes: 548 Use SNESSetLineSearch() to set this routine within the SNES_EQ_LS method. 549 550 Level: advanced 551 552 .keywords: SNES, nonlinear, quadratic, line search 553 554 .seealso: SNESCubicLineSearch(), SNESNoLineSearch(), SNESSetLineSearch(), SNESNoLineSearchNoNorms() 555 @*/ 556 int SNESQuadraticLineSearch(SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 557 double fnorm, double *ynorm, double *gnorm,int *flag) 558 { 559 /* 560 Note that for line search purposes we work with with the related 561 minimization problem: 562 min z(x): R^n -> R, 563 where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2. 564 */ 565 double steptol,initslope,maxstep,minlambda,alpha,lambda,lambdatemp; 566 #if defined(PETSC_USE_COMPLEX) 567 Scalar cinitslope,clambda; 568 #endif 569 int ierr, count; 570 SNES_LS *neP = (SNES_LS *) snes->data; 571 Scalar mone = -1.0,scale; 572 PetscTruth change_y = PETSC_FALSE; 573 574 PetscFunctionBegin; 575 PLogEventBegin(SNES_LineSearch,snes,x,f,g); 576 *flag = 0; 577 alpha = neP->alpha; 578 maxstep = neP->maxstep; 579 steptol = neP->steptol; 580 581 VecNorm(y, NORM_2,ynorm ); 582 if (*ynorm < snes->atol) { 583 PLogInfo(snes,"SNESQuadraticLineSearch: Search direction and size is 0\n"); 584 *gnorm = fnorm; 585 ierr = VecCopy(x,y);CHKERRQ(ierr); 586 ierr = VecCopy(f,g);CHKERRQ(ierr); 587 goto theend2; 588 } 589 if (*ynorm > maxstep) { /* Step too big, so scale back */ 590 scale = maxstep/(*ynorm); 591 ierr = VecScale(&scale,y);CHKERRQ(ierr); 592 *ynorm = maxstep; 593 } 594 minlambda = steptol/(*ynorm); 595 ierr = MatMult(snes->jacobian,y,w);CHKERRQ(ierr); 596 #if defined(PETSC_USE_COMPLEX) 597 ierr = VecDot(f,w,&cinitslope);CHKERRQ(ierr); 598 initslope = PetscReal(cinitslope); 599 #else 600 ierr = VecDot(f,w,&initslope);CHKERRQ(ierr); 601 #endif 602 if (initslope > 0.0) initslope = -initslope; 603 if (initslope == 0.0) initslope = -1.0; 604 605 ierr = VecCopy(y,w);CHKERRQ(ierr); 606 ierr = VecAYPX(&mone,x,w);CHKERRQ(ierr); 607 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 608 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 609 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* Sufficient reduction */ 610 ierr = VecCopy(w,y);CHKERRQ(ierr); 611 PLogInfo(snes,"SNESQuadraticLineSearch: Using full step\n"); 612 goto theend2; 613 } 614 615 /* Fit points with quadratic */ 616 lambda = 1.0; 617 count = 1; 618 while (1) { 619 if (lambda <= minlambda) { /* bad luck; use full step */ 620 PLogInfo(snes,"SNESQuadraticLineSearch:Unable to find good step length! %d \n",count); 621 PLogInfo(snes,"SNESQuadraticLineSearch:fnorm=%g, gnorm=%g, ynorm=%g, lambda=%g, initial slope=%g\n", 622 fnorm,*gnorm,*ynorm,lambda,initslope); 623 ierr = VecCopy(w,y);CHKERRQ(ierr); 624 *flag = -1; break; 625 } 626 lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 627 if (lambdatemp <= .1*lambda) { 628 lambda = .1*lambda; 629 } else lambda = lambdatemp; 630 ierr = VecCopy(x,w);CHKERRQ(ierr); 631 lambda = -lambda; 632 #if defined(PETSC_USE_COMPLEX) 633 clambda = lambda; ierr = VecAXPY(&clambda,y,w);CHKERRQ(ierr); 634 #else 635 ierr = VecAXPY(&lambda,y,w);CHKERRQ(ierr); 636 #endif 637 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 638 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 639 if ((*gnorm)*(*gnorm)*0.5 <= fnorm*fnorm*0.5 + alpha*initslope) { /* sufficient reduction */ 640 ierr = VecCopy(w,y);CHKERRQ(ierr); 641 PLogInfo(snes,"SNESQuadraticLineSearch:Quadratically determined step, lambda=%g\n",lambda); 642 break; 643 } 644 count++; 645 } 646 theend2: 647 /* Optional user-defined check for line search step validity */ 648 if (neP->CheckStep) { 649 ierr = (*neP->CheckStep)(snes,neP->checkP,y,&change_y);CHKERRQ(ierr); 650 if (change_y == PETSC_TRUE) { /* recompute the function if the step has changed */ 651 ierr = SNESComputeFunction(snes,y,g);CHKERRQ(ierr); 652 ierr = VecNormBegin(y,NORM_2,ynorm);CHKERRQ(ierr); 653 ierr = VecNormBegin(g,NORM_2,gnorm);CHKERRQ(ierr); 654 ierr = VecNormEnd(y,NORM_2,ynorm);CHKERRQ(ierr); 655 ierr = VecNormEnd(g,NORM_2,gnorm);CHKERRQ(ierr); 656 } 657 } 658 PLogEventEnd(SNES_LineSearch,snes,x,f,g); 659 PetscFunctionReturn(0); 660 } 661 /* -------------------------------------------------------------------------- */ 662 #undef __FUNC__ 663 #define __FUNC__ "SNESSetLineSearch" 664 /*@C 665 SNESSetLineSearch - Sets the line search routine to be used 666 by the method SNES_EQ_LS. 667 668 Input Parameters: 669 + snes - nonlinear context obtained from SNESCreate() 670 . lsctx - optional user-defined context for use by line search 671 - func - pointer to int function 672 673 Collective on SNES 674 675 Available Routines: 676 + SNESCubicLineSearch() - default line search 677 . SNESQuadraticLineSearch() - quadratic line search 678 . SNESNoLineSearch() - the full Newton step (actually not a line search) 679 - SNESNoLineSearchNoNorms() - the full Newton step (calculating no norms; faster in parallel) 680 681 Options Database Keys: 682 + -snes_eq_ls [cubic,quadratic,basic,basicnonorms] - Selects line search 683 . -snes_eq_ls_alpha <alpha> - Sets alpha 684 . -snes_eq_ls_maxstep <max> - Sets maxstep 685 - -snes_eq_ls_steptol <steptol> - Sets steptol 686 687 Calling sequence of func: 688 .vb 689 func (SNES snes, void *lsctx, Vec x, Vec f, Vec g, Vec y, Vec w, 690 double fnorm, double *ynorm, double *gnorm, *flag) 691 .ve 692 693 Input parameters for func: 694 + snes - nonlinear context 695 . lsctx - optional user-defined context for line search 696 . x - current iterate 697 . f - residual evaluated at x 698 . y - search direction (contains new iterate on output) 699 . w - work vector 700 - fnorm - 2-norm of f 701 702 Output parameters for func: 703 + g - residual evaluated at new iterate y 704 . y - new iterate (contains search direction on input) 705 . gnorm - 2-norm of g 706 . ynorm - 2-norm of search length 707 - flag - set to 0 if the line search succeeds; a nonzero integer 708 on failure. 709 710 Level: advanced 711 712 .keywords: SNES, nonlinear, set, line search, routine 713 714 .seealso: SNESCubicLineSearch(), SNESQuadraticLineSearch(), SNESNoLineSearch(), SNESNoLineSearchNoNorms(), SNESSetLineSearchCheck(), SNESSetLineSearchParams(), SNESGetLineSearchParams() 715 @*/ 716 int SNESSetLineSearch(SNES snes,int (*func)(SNES,void*,Vec,Vec,Vec,Vec,Vec,double,double*,double*,int*),void *lsctx) 717 { 718 int ierr, (*f)(SNES,int (*)(SNES,void*,Vec,Vec,Vec,Vec,Vec,double,double*,double*,int*),void*); 719 720 PetscFunctionBegin; 721 ierr = PetscObjectQueryFunction((PetscObject)snes,"SNESSetLineSearch_C",(void **)&f);CHKERRQ(ierr); 722 if (f) { 723 ierr = (*f)(snes,func,lsctx);CHKERRQ(ierr); 724 } 725 PetscFunctionReturn(0); 726 } 727 /* -------------------------------------------------------------------------- */ 728 EXTERN_C_BEGIN 729 #undef __FUNC__ 730 #define __FUNC__ "SNESSetLineSearch_LS" 731 int SNESSetLineSearch_LS(SNES snes,int (*func)(SNES,void*,Vec,Vec,Vec,Vec,Vec, 732 double,double*,double*,int*),void *lsctx) 733 { 734 PetscFunctionBegin; 735 ((SNES_LS *)(snes->data))->LineSearch = func; 736 ((SNES_LS *)(snes->data))->lsP = lsctx; 737 PetscFunctionReturn(0); 738 } 739 EXTERN_C_END 740 /* -------------------------------------------------------------------------- */ 741 #undef __FUNC__ 742 #define __FUNC__ "SNESSetLineSearchCheck" 743 /*@C 744 SNESSetLineSearchCheck - Sets a routine to check the validity of new iterate computed 745 by the line search routine in the Newton-based method SNES_EQ_LS. 746 747 Input Parameters: 748 + snes - nonlinear context obtained from SNESCreate() 749 . func - pointer to int function 750 - checkctx - optional user-defined context for use by step checking routine 751 752 Collective on SNES 753 754 Calling sequence of func: 755 .vb 756 int func (SNES snes, void *checkctx, Vec x, PetscTruth *flag) 757 .ve 758 where func returns an error code of 0 on success and a nonzero 759 on failure. 760 761 Input parameters for func: 762 + snes - nonlinear context 763 . checkctx - optional user-defined context for use by step checking routine 764 - x - current candidate iterate 765 766 Output parameters for func: 767 + x - current iterate (possibly modified) 768 - flag - flag indicating whether x has been modified (either 769 PETSC_TRUE of PETSC_FALSE) 770 771 Level: advanced 772 773 Notes: 774 SNESNoLineSearch() and SNESNoLineSearchNoNorms() accept the new 775 iterate computed by the line search checking routine. In particular, 776 these routines (1) compute a candidate iterate u_{i+1}, (2) pass control 777 to the checking routine, and then (3) compute the corresponding nonlinear 778 function f(u_{i+1}) with the (possibly altered) iterate u_{i+1}. 779 780 SNESQuadraticLineSearch() and SNESCubicLineSearch() also accept the 781 new iterate computed by the line search checking routine. In particular, 782 these routines (1) compute a candidate iterate u_{i+1} as well as a 783 candidate nonlinear function f(u_{i+1}), (2) pass control to the checking 784 routine, and then (3) force a re-evaluation of f(u_{i+1}) if any changes 785 were made to the candidate iterate in the checking routine (as indicated 786 by flag=PETSC_TRUE). The overhead of this function re-evaluation can be 787 very costly, so use this feature with caution! 788 789 .keywords: SNES, nonlinear, set, line search check, step check, routine 790 791 .seealso: SNESSetLineSearch() 792 @*/ 793 int SNESSetLineSearchCheck(SNES snes,int (*func)(SNES,void*,Vec,PetscTruth*),void *checkctx) 794 { 795 int ierr, (*f)(SNES,int (*)(SNES,void*,Vec,PetscTruth*),void*); 796 797 PetscFunctionBegin; 798 ierr = PetscObjectQueryFunction((PetscObject)snes,"SNESSetLineSearchCheck_C",(void **)&f);CHKERRQ(ierr); 799 if (f) { 800 ierr = (*f)(snes,func,checkctx);CHKERRQ(ierr); 801 } 802 PetscFunctionReturn(0); 803 } 804 /* -------------------------------------------------------------------------- */ 805 EXTERN_C_BEGIN 806 #undef __FUNC__ 807 #define __FUNC__ "SNESSetLineSearchCheck_LS" 808 int SNESSetLineSearchCheck_LS(SNES snes,int (*func)(SNES,void*,Vec,PetscTruth*),void *checkctx) 809 { 810 PetscFunctionBegin; 811 ((SNES_LS *)(snes->data))->CheckStep = func; 812 ((SNES_LS *)(snes->data))->checkP = checkctx; 813 PetscFunctionReturn(0); 814 } 815 EXTERN_C_END 816 /* -------------------------------------------------------------------------- */ 817 /* 818 SNESPrintHelp_EQ_LS - Prints all options for the SNES_EQ_LS method. 819 820 Input Parameter: 821 . snes - the SNES context 822 823 Application Interface Routine: SNESPrintHelp() 824 */ 825 #undef __FUNC__ 826 #define __FUNC__ "SNESPrintHelp_EQ_LS" 827 static int SNESPrintHelp_EQ_LS(SNES snes,char *p) 828 { 829 SNES_LS *ls = (SNES_LS *)snes->data; 830 int ierr; 831 832 PetscFunctionBegin; 833 ierr = (*PetscHelpPrintf)(snes->comm," method SNES_EQ_LS (ls) for systems of nonlinear equations:\n");CHKERRQ(ierr); 834 ierr = (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls [cubic,quadratic,basic,basicnonorms,...]\n",p);CHKERRQ(ierr); 835 ierr = (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls_alpha <alpha> (default %g)\n",p,ls->alpha);CHKERRQ(ierr); 836 ierr = (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls_maxstep <max> (default %g)\n",p,ls->maxstep);CHKERRQ(ierr); 837 ierr = (*PetscHelpPrintf)(snes->comm," %ssnes_eq_ls_steptol <tol> (default %g)\n",p,ls->steptol);CHKERRQ(ierr); 838 PetscFunctionReturn(0); 839 } 840 /* -------------------------------------------------------------------------- */ 841 /* 842 SNESView_EQ_LS - Prints info from the SNES_EQ_LS data structure. 843 844 Input Parameters: 845 . SNES - the SNES context 846 . viewer - visualization context 847 848 Application Interface Routine: SNESView() 849 */ 850 #undef __FUNC__ 851 #define __FUNC__ "SNESView_EQ_LS" 852 static int SNESView_EQ_LS(SNES snes,Viewer viewer) 853 { 854 SNES_LS *ls = (SNES_LS *)snes->data; 855 char *cstr; 856 int ierr; 857 ViewerType vtype; 858 859 PetscFunctionBegin; 860 ierr = ViewerGetType(viewer,&vtype);CHKERRQ(ierr); 861 if (PetscTypeCompare(vtype,ASCII_VIEWER)) { 862 if (ls->LineSearch == SNESNoLineSearch) cstr = "SNESNoLineSearch"; 863 else if (ls->LineSearch == SNESQuadraticLineSearch) cstr = "SNESQuadraticLineSearch"; 864 else if (ls->LineSearch == SNESCubicLineSearch) cstr = "SNESCubicLineSearch"; 865 else cstr = "unknown"; 866 ierr = ViewerASCIIPrintf(viewer," line search variant: %s\n",cstr);CHKERRQ(ierr); 867 ierr = ViewerASCIIPrintf(viewer," alpha=%g, maxstep=%g, steptol=%g\n",ls->alpha,ls->maxstep,ls->steptol);CHKERRQ(ierr); 868 } else { 869 SETERRQ(1,1,"Viewer type not supported for this object"); 870 } 871 PetscFunctionReturn(0); 872 } 873 /* -------------------------------------------------------------------------- */ 874 /* 875 SNESSetFromOptions_EQ_LS - Sets various parameters for the SNES_EQ_LS method. 876 877 Input Parameter: 878 . snes - the SNES context 879 880 Application Interface Routine: SNESSetFromOptions() 881 */ 882 #undef __FUNC__ 883 #define __FUNC__ "SNESSetFromOptions_EQ_LS" 884 static int SNESSetFromOptions_EQ_LS(SNES snes) 885 { 886 SNES_LS *ls = (SNES_LS *)snes->data; 887 char ver[16]; 888 double tmp; 889 int ierr,flg; 890 891 PetscFunctionBegin; 892 ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_alpha",&tmp, &flg);CHKERRQ(ierr); 893 if (flg) { 894 ls->alpha = tmp; 895 } 896 ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_maxstep",&tmp, &flg);CHKERRQ(ierr); 897 if (flg) { 898 ls->maxstep = tmp; 899 } 900 ierr = OptionsGetDouble(snes->prefix,"-snes_eq_ls_steptol",&tmp, &flg);CHKERRQ(ierr); 901 if (flg) { 902 ls->steptol = tmp; 903 } 904 ierr = OptionsGetString(snes->prefix,"-snes_eq_ls",ver,16, &flg);CHKERRQ(ierr); 905 if (flg) { 906 if (!PetscStrcmp(ver,"basic")) { 907 ierr = SNESSetLineSearch(snes,SNESNoLineSearch,PETSC_NULL);CHKERRQ(ierr); 908 } else if (!PetscStrcmp(ver,"basicnonorms")) { 909 ierr = SNESSetLineSearch(snes,SNESNoLineSearchNoNorms,PETSC_NULL);CHKERRQ(ierr); 910 } else if (!PetscStrcmp(ver,"quadratic")) { 911 ierr = SNESSetLineSearch(snes,SNESQuadraticLineSearch,PETSC_NULL);CHKERRQ(ierr); 912 } else if (!PetscStrcmp(ver,"cubic")) { 913 ierr = SNESSetLineSearch(snes,SNESCubicLineSearch,PETSC_NULL);CHKERRQ(ierr); 914 } 915 else {SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,0,"Unknown line search");} 916 } 917 PetscFunctionReturn(0); 918 } 919 /* -------------------------------------------------------------------------- */ 920 /* 921 SNESCreate_EQ_LS - Creates a nonlinear solver context for the SNES_EQ_LS method, 922 SNES_LS, and sets this as the private data within the generic nonlinear solver 923 context, SNES, that was created within SNESCreate(). 924 925 926 Input Parameter: 927 . snes - the SNES context 928 929 Application Interface Routine: SNESCreate() 930 */ 931 EXTERN_C_BEGIN 932 #undef __FUNC__ 933 #define __FUNC__ "SNESCreate_EQ_LS" 934 int SNESCreate_EQ_LS(SNES snes) 935 { 936 int ierr; 937 SNES_LS *neP; 938 939 PetscFunctionBegin; 940 if (snes->method_class != SNES_NONLINEAR_EQUATIONS) { 941 SETERRQ(PETSC_ERR_ARG_WRONG,0,"For SNES_NONLINEAR_EQUATIONS only"); 942 } 943 944 snes->setup = SNESSetUp_EQ_LS; 945 snes->solve = SNESSolve_EQ_LS; 946 snes->destroy = SNESDestroy_EQ_LS; 947 snes->converged = SNESConverged_EQ_LS; 948 snes->printhelp = SNESPrintHelp_EQ_LS; 949 snes->setfromoptions = SNESSetFromOptions_EQ_LS; 950 snes->view = SNESView_EQ_LS; 951 snes->nwork = 0; 952 953 neP = PetscNew(SNES_LS);CHKPTRQ(neP); 954 PLogObjectMemory(snes,sizeof(SNES_LS)); 955 snes->data = (void *) neP; 956 neP->alpha = 1.e-4; 957 neP->maxstep = 1.e8; 958 neP->steptol = 1.e-12; 959 neP->LineSearch = SNESCubicLineSearch; 960 neP->lsP = PETSC_NULL; 961 neP->CheckStep = PETSC_NULL; 962 neP->checkP = PETSC_NULL; 963 964 ierr = PetscObjectComposeFunction((PetscObject)snes,"SNESSetLineSearch_C","SNESSetLineSearch_LS", 965 (void*)SNESSetLineSearch_LS);CHKERRQ(ierr); 966 ierr = PetscObjectComposeFunction((PetscObject)snes,"SNESSetLineSearchCheck_C","SNESSetLineSearchCheck_LS", 967 (void*)SNESSetLineSearchCheck_LS);CHKERRQ(ierr); 968 969 PetscFunctionReturn(0); 970 } 971 EXTERN_C_END 972 973 974 975