1 #ifndef lint 2 static char vcid[] = "$Id: newls1.c,v 1.1 1995/03/20 22:59:54 bsmith Exp bsmith $"; 3 #endif 4 5 #include <math.h> 6 #include "newls1.h" 7 8 int SNESNewtonDefaultMonitor(SNES,int,Vec,Vec, double ,void*); 9 int SNESNewtonDefaultConverged(SNES, double ,double ,double,void * ); 10 11 /* 12 Implements a line search variant of Newton's Method 13 for solving systems of nonlinear equations. 14 15 Input parameters: 16 . snes - nonlinear context obtained from SNESCreate() 17 18 Output Parameters: 19 . its - Number of global iterations until termination. 20 21 Notes: 22 See SNESCreate() and SNESSetUp() for information on the definition and 23 initialization of the nonlinear solver context. 24 25 This implements essentially a truncated Newton method with a 26 line search. By default a cubic backtracking line search 27 is employed, as described in the text "Numerical Methods for 28 Unconstrained Optimization and Nonlinear Equations" by Dennis 29 and Schnabel. See the examples in src/snes/examples. 30 */ 31 32 int SNESSolve_LS( SNES snes, int *outits ) 33 { 34 SNES_LS *neP = (SNES_LS *) snes->data; 35 int maxits, i, iters, line, nlconv, history_len,ierr,lits; 36 double fnorm, gnorm, gpnorm, xnorm, ynorm, *history; 37 Vec Y, X, F, G, W, TMP; 38 39 history = snes->conv_hist; /* convergence history */ 40 history_len = snes->conv_hist_len; /* convergence history length */ 41 maxits = snes->max_its; /* maximum number of iterations */ 42 X = snes->vec_sol; /* solution vector */ 43 F = snes->vec_res; /* residual vector */ 44 Y = snes->work[0]; /* work vectors */ 45 G = snes->work[1]; 46 W = snes->work[2]; 47 48 ierr = SNESComputeInitialGuess(snes,X); CHKERR(ierr); /* X <- X_0 */ 49 VecNorm( X, &xnorm ); /* xnorm = || X || */ 50 ierr = SNESComputeResidual(snes,X,F); CHKERR(ierr); /* (+/-) F(X) */ 51 VecNorm(F, &fnorm ); /* fnorm <- || F || */ 52 snes->norm = fnorm; 53 if (history && history_len > 0) history[0] = fnorm; 54 (*snes->Monitor)(snes,0,X,F,fnorm,snes->monP); /* Monitor progress */ 55 56 for ( i=0; i<maxits; i++ ) { 57 snes->iter = i+1; 58 59 /* Solve J Y = -F, where J is Jacobian matrix */ 60 (*snes->ComputeJacobian)(X,&snes->jacobian,snes->jacP); 61 ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian,0); 62 ierr = SLESSolve(snes->sles,F,Y,&lits); CHKERR(ierr); 63 ierr = (*neP->LineSearch)(snes, X, F, G, Y, W, fnorm, &ynorm, &gnorm ); 64 65 TMP = F; F = G; G = TMP; 66 TMP = X; X = Y; Y = TMP; 67 fnorm = gnorm; 68 69 snes->norm = fnorm; 70 if (history && history_len > i+1) history[i+1] = fnorm; 71 VecNorm( X, &xnorm ); /* xnorm = || X || */ 72 (*snes->Monitor)(snes,i+1,X,F,fnorm,snes->monP); /* Monitor progress */ 73 74 /* Test for convergence */ 75 if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) { 76 if (X != snes->vec_sol) VecCopy( X, snes->vec_sol ); 77 break; 78 } 79 } 80 if (i == maxits) i--; 81 *outits = i+1; 82 return 0; 83 } 84 /* ------------------------------------------------------------ */ 85 /*ARGSUSED*/ 86 int SNESSetUp_LS(SNES snes ) 87 { 88 SNES_LS *ctx = (SNES_LS *)snes->data; 89 int ierr; 90 snes->nwork = 3; 91 ierr = VecGetVecs( snes->vec_sol, snes->nwork,&snes->work ); CHKERR(ierr); 92 PLogObjectParents(snes,snes->nwork,snes->work ); 93 return 0; 94 } 95 /* ------------------------------------------------------------ */ 96 /*ARGSUSED*/ 97 int SNESDestroy_LS(PetscObject obj) 98 { 99 SNES snes = (SNES) obj; 100 SLESDestroy(snes->sles); 101 VecFreeVecs(snes->work, snes->nwork ); 102 PLogObjectDestroy(obj); 103 PETSCHEADERDESTROY(obj); 104 return 0; 105 } 106 /*@ 107 SNESDefaultMonitor - Default monitor for NLE solvers. Prints the 108 residual norm at each iteration. 109 110 Input Parameters: 111 . nlP - nonlinear context 112 . x - current iterate 113 . f - current residual (+/-) 114 . fnorm - 2-norm residual value (may be estimated). 115 116 Notes: 117 f is either the residual or its negative, depending on the user's 118 preference, as set with NLSetResidualRoutine(). 119 120 121 @*/ 122 int SNESDefaultMonitor(SNES snes,int its, Vec x,Vec f,double fnorm,void *dummy) 123 { 124 fprintf( stdout, "iter = %d, residual norm %g \n",its,fnorm); 125 return 0; 126 } 127 128 /*@ 129 SNESDefaultConverged - Default test for monitoring the convergence 130 of the NLE solvers. 131 132 Input Parameters: 133 . nlP - nonlinear context 134 . xnorm - 2-norm of current iterate 135 . pnorm - 2-norm of current step 136 . fnorm - 2-norm of residual 137 138 Returns: 139 $ 2 if ( fnorm < atol ), 140 $ 3 if ( pnorm < xtol*xnorm ), 141 $ -2 if ( nres > max_res ), 142 $ 0 otherwise, 143 144 where 145 $ atol - absolute residual norm tolerance, 146 $ set with NLSetAbsConvergenceTol() 147 $ max_res - maximum number of residual evaluations, 148 $ set with NLSetMaxResidualEvaluations() 149 $ nres - number of residual evaluations 150 $ xtol - relative residual norm tolerance, 151 152 $ set with NLSetMaxResidualEvaluations() 153 $ nres - number of residual evaluations 154 $ xtol - relative residual norm tolerance, 155 $ set with NLSetRelConvergenceTol() 156 157 @*/ 158 int SNESDefaultConverged(SNES snes,double xnorm,double pnorm,double fnorm, 159 void *dummy) 160 { 161 if (fnorm < snes->atol) { 162 PLogInfo((PetscObject)snes,"Converged due to absolute residual norm %g < %g\n", 163 fnorm,snes->atol); 164 return 2; 165 } 166 if (pnorm < snes->xtol*(xnorm)) { 167 PLogInfo((PetscObject)snes,"Converged due to small update length %g < %g*%g\n", 168 pnorm,snes->xtol,xnorm); 169 return 3; 170 } 171 return 0; 172 } 173 174 /* ------------------------------------------------------------ */ 175 /*ARGSUSED*/ 176 /*@ 177 SNESNoLineSearch - This routine is not a line search at all; 178 it simply uses the full Newton step. Thus, this routine is intended 179 to serve as a template and is not recommended for general use. 180 181 Input Parameters: 182 . snes - nonlinear context 183 . x - current iterate 184 . f - residual evaluated at x 185 . y - search direction (contains new iterate on output) 186 . w - work vector 187 . fnorm - 2-norm of f 188 189 Output parameters: 190 . g - residual evaluated at new iterate y 191 . y - new iterate (contains search direction on input) 192 . gnorm - 2-norm of g 193 . ynorm - 2-norm of search length 194 195 Returns: 196 1, indicating success of the step. 197 198 @*/ 199 int SNESNoLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 200 double fnorm, double *ynorm, double *gnorm ) 201 { 202 int ierr; 203 Scalar one = 1.0; 204 VecNorm(y, ynorm ); /* ynorm = || y || */ 205 VecAXPY(&one, x, y ); /* y <- x + y */ 206 ierr = SNESComputeResidual(snes,y,g); CHKERR(ierr); 207 VecNorm( g, gnorm ); /* gnorm = || g || */ 208 return 1; 209 } 210 /* ------------------------------------------------------------------ */ 211 /*@ 212 SNESCubicLineSearch - This routine performs a cubic line search. 213 214 Input Parameters: 215 . snes - nonlinear context 216 . x - current iterate 217 . f - residual evaluated at x 218 . y - search direction (contains new iterate on output) 219 . w - work vector 220 . fnorm - 2-norm of f 221 222 Output parameters: 223 . g - residual evaluated at new iterate y 224 . y - new iterate (contains search direction on input) 225 . gnorm - 2-norm of g 226 . ynorm - 2-norm of search length 227 228 Returns: 229 1 if the line search succeeds; 0 if the line search fails. 230 231 Notes: 232 Use either NLSetStepLineSearchRoutines() or NLSetLineSearchRoutine() 233 to set this routine within the SNES_NLS1 method. 234 235 This line search is taken from "Numerical Methods for Unconstrained 236 Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 237 238 @*/ 239 int SNESCubicLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 240 double fnorm, double *ynorm, double *gnorm ) 241 { 242 double steptol, initslope; 243 double lambdaprev, gnormprev; 244 double a, b, d, t1, t2; 245 Scalar cinitslope,clambda; 246 int ierr,count; 247 SNES_LS *neP = (SNES_LS *) snes->data; 248 Scalar one = 1.0,scale; 249 double maxstep,minlambda,alpha,lambda,lambdatemp; 250 251 alpha = neP->alpha; 252 maxstep = neP->maxstep; 253 steptol = neP->steptol; 254 255 VecNorm(y, ynorm ); 256 if (*ynorm > maxstep) { /* Step too big, so scale back */ 257 scale = maxstep/(*ynorm); 258 PLogInfo((PetscObject)snes,"Scaling step by %g\n",scale); 259 VecScale(&scale, y ); 260 *ynorm = maxstep; 261 } 262 minlambda = steptol/(*ynorm); 263 #if defined(PETSC_COMPLEX) 264 VecDot(f, y, &cinitslope ); 265 initslope = real(cinitslope); 266 #else 267 VecDot(f, y, &initslope ); 268 #endif 269 if (initslope > 0.0) initslope = -initslope; 270 if (initslope == 0.0) initslope = -1.0; 271 272 VecCopy(y, w ); 273 VecAXPY(&one, x, w ); 274 ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr); 275 VecNorm(g, gnorm ); 276 if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 277 VecCopy(w, y ); 278 PLogInfo((PetscObject)snes,"Using full step\n"); 279 return 0; 280 } 281 282 /* Fit points with quadratic */ 283 lambda = 1.0; count = 0; 284 lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 285 lambdaprev = lambda; 286 gnormprev = *gnorm; 287 if (lambdatemp <= .1*lambda) { 288 lambda = .1*lambda; 289 } else lambda = lambdatemp; 290 VecCopy(x, w ); 291 #if defined(PETSC_COMPLEX) 292 clambda = lambda; VecAXPY(&clambda, y, w ); 293 #else 294 VecAXPY(&lambda, y, w ); 295 #endif 296 ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr); 297 VecNorm(g, gnorm ); 298 if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 299 VecCopy(w, y ); 300 PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 301 return 0; 302 } 303 304 /* Fit points with cubic */ 305 count = 1; 306 while (1) { 307 if (lambda <= minlambda) { /* bad luck; use full step */ 308 PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 309 PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 310 fnorm,*gnorm, *ynorm,lambda); 311 VecCopy(w, y ); 312 return 0; 313 } 314 t1 = *gnorm - fnorm - lambda*initslope; 315 t2 = gnormprev - fnorm - lambdaprev*initslope; 316 a = (t1/(lambda*lambda) - 317 t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 318 b = (-lambdaprev*t1/(lambda*lambda) + 319 lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 320 d = b*b - 3*a*initslope; 321 if (d < 0.0) d = 0.0; 322 if (a == 0.0) { 323 lambdatemp = -initslope/(2.0*b); 324 } else { 325 lambdatemp = (-b + sqrt(d))/(3.0*a); 326 } 327 if (lambdatemp > .5*lambda) { 328 lambdatemp = .5*lambda; 329 } 330 lambdaprev = lambda; 331 gnormprev = *gnorm; 332 if (lambdatemp <= .1*lambda) { 333 lambda = .1*lambda; 334 } 335 else lambda = lambdatemp; 336 VecCopy( x, w ); 337 #if defined(PETSC_COMPLEX) 338 VecAXPY(&clambda, y, w ); 339 #else 340 VecAXPY(&lambda, y, w ); 341 #endif 342 ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr); 343 VecNorm(g, gnorm ); 344 if (*gnorm <= fnorm + alpha*initslope) { /* is reduction enough */ 345 VecCopy(w, y ); 346 PLogInfo((PetscObject)snes,"Cubically determined step, lambda %g\n",lambda); 347 return 0; 348 } 349 count++; 350 } 351 return 0; 352 } 353 /*@ 354 SNESQuadraticLineSearch - This routine performs a cubic line search. 355 356 Input Parameters: 357 . snes - nonlinear context 358 . x - current iterate 359 . f - residual evaluated at x 360 . y - search direction (contains new iterate on output) 361 . w - work vector 362 . fnorm - 2-norm of f 363 364 Output parameters: 365 . g - residual evaluated at new iterate y 366 . y - new iterate (contains search direction on input) 367 . gnorm - 2-norm of g 368 . ynorm - 2-norm of search length 369 370 Returns: 371 1 if the line search succeeds; 0 if the line search fails. 372 373 Notes: 374 Use SNESSetLineSearchRoutines() 375 to set this routine within the SNES_NLS1 method. 376 377 @*/ 378 int SNESQuadraticLineSearch(SNES snes, Vec x, Vec f, Vec g, Vec y, Vec w, 379 double fnorm, double *ynorm, double *gnorm ) 380 { 381 double steptol, initslope; 382 double lambdaprev, gnormprev; 383 double a, b, d, t1, t2; 384 Scalar cinitslope,clambda; 385 int ierr,count; 386 SNES_LS *neP = (SNES_LS *) snes->data; 387 Scalar one = 1.0,scale; 388 double maxstep,minlambda,alpha,lambda,lambdatemp; 389 390 alpha = neP->alpha; 391 maxstep = neP->maxstep; 392 steptol = neP->steptol; 393 394 VecNorm(y, ynorm ); 395 if (*ynorm > maxstep) { /* Step too big, so scale back */ 396 scale = maxstep/(*ynorm); 397 VecScale(&scale, y ); 398 *ynorm = maxstep; 399 } 400 minlambda = steptol/(*ynorm); 401 #if defined(PETSC_COMPLEX) 402 VecDot(f, y, &cinitslope ); 403 initslope = real(cinitslope); 404 #else 405 VecDot(f, y, &initslope ); 406 #endif 407 if (initslope > 0.0) initslope = -initslope; 408 if (initslope == 0.0) initslope = -1.0; 409 410 VecCopy(y, w ); 411 VecAXPY(&one, x, w ); 412 ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr); 413 VecNorm(g, gnorm ); 414 if (*gnorm <= fnorm + alpha*initslope) { /* Sufficient reduction */ 415 VecCopy(w, y ); 416 PLogInfo((PetscObject)snes,"Using full step\n"); 417 return 0; 418 } 419 420 /* Fit points with quadratic */ 421 lambda = 1.0; count = 0; 422 count = 1; 423 while (1) { 424 if (lambda <= minlambda) { /* bad luck; use full step */ 425 PLogInfo((PetscObject)snes,"Unable to find good step length! %d \n",count); 426 PLogInfo((PetscObject)snes, "f %g fnew %g ynorm %g lambda %g \n", 427 fnorm,*gnorm, *ynorm,lambda); 428 VecCopy(w, y ); 429 return 0; 430 } 431 lambdatemp = -initslope/(2.0*(*gnorm - fnorm - initslope)); 432 lambdaprev = lambda; 433 gnormprev = *gnorm; 434 if (lambdatemp <= .1*lambda) { 435 lambda = .1*lambda; 436 } else lambda = lambdatemp; 437 VecCopy(x, w ); 438 #if defined(PETSC_COMPLEX) 439 clambda = lambda; VecAXPY(&clambda, y, w ); 440 #else 441 VecAXPY(&lambda, y, w ); 442 #endif 443 ierr = SNESComputeResidual(snes,w,g); CHKERR(ierr); 444 VecNorm(g, gnorm ); 445 if (*gnorm <= fnorm + alpha*initslope) { /* sufficient reduction */ 446 VecCopy(w, y ); 447 PLogInfo((PetscObject)snes,"Quadratically determined step, lambda %g\n",lambda); 448 return 0; 449 } 450 count++; 451 } 452 453 return 0; 454 } 455 /* ------------------------------------------------------------ */ 456 /*@C 457 SNESSetLineSearchRoutine - Sets the line search routine to be used 458 by the method SNES_NLS1. 459 460 Input Parameters: 461 . snes - nonlinear context obtained from NLCreate() 462 . func - pointer to int function 463 464 Possible routines: 465 SNESCubicLineSearch() - default line search 466 SNESNoLineSearch() - the full Newton step (actually not a line search) 467 468 Calling sequence of func: 469 . func (SNES snes, Vec x, Vec f, Vec g, Vec y, 470 Vec w, double fnorm, double *ynorm, double *gnorm ) 471 472 Input parameters for func: 473 . snes - nonlinear context 474 . x - current iterate 475 . f - residual evaluated at x 476 . y - search direction (contains new iterate on output) 477 . w - work vector 478 . fnorm - 2-norm of f 479 480 Output parameters for func: 481 . g - residual evaluated at new iterate y 482 . y - new iterate (contains search direction on input) 483 . gnorm - 2-norm of g 484 . ynorm - 2-norm of search length 485 486 Returned by func: 487 1 if the line search succeeds; 0 if the line search fails. 488 @*/ 489 int SNESSetLineSearchRoutine(SNES snes,int (*func)(SNES,Vec,Vec,Vec,Vec,Vec, 490 double,double *,double*) ) 491 { 492 if ((snes)->type == SNES_NLS1) 493 ((SNES_LS *)(snes->data))->LineSearch = func; 494 return 0; 495 } 496 497 static int SNESPrintHelp_LS(SNES snes) 498 { 499 SNES_LS *ls = (SNES_LS *)snes->data; 500 fprintf(stderr,"-snes_line_search [basic,quadratic,cubic]\n"); 501 fprintf(stderr,"-snes_line_search_alpha alpha (default %g)\n",ls->alpha); 502 fprintf(stderr,"-snes_line_search_maxstep max (default %g)\n",ls->maxstep); 503 fprintf(stderr,"-snes_line_search_steptol tol (default %g)\n",ls->steptol); 504 } 505 506 static int SNESSetFromOptions_LS(SNES snes) 507 { 508 SNES_LS *ls = (SNES_LS *)snes->data; 509 char ver[16]; 510 double tmp; 511 512 if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_alpa",&tmp)) { 513 ls->alpha = tmp; 514 } 515 if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_maxstep",&tmp)) { 516 ls->maxstep = tmp; 517 } 518 if (OptionsGetDouble(0,snes->prefix,"-snes_line_search_steptol",&tmp)) { 519 ls->steptol = tmp; 520 } 521 if (OptionsGetString(0,snes->prefix,"-snes_line_search",ver,16)) { 522 if (!strcmp(ver,"basic")) { 523 SNESSetLineSearchRoutine(snes,SNESNoLineSearch); 524 } 525 else if (!strcmp(ver,"quadratic")) { 526 SNESSetLineSearchRoutine(snes,SNESQuadraticLineSearch); 527 } 528 else if (!strcmp(ver,"cubic")) { 529 SNESSetLineSearchRoutine(snes,SNESCubicLineSearch); 530 } 531 else {SETERR(1,"Unknown line search?");} 532 } 533 return 0; 534 } 535 536 /* ------------------------------------------------------------ */ 537 int SNESCreate_LS(SNES snes ) 538 { 539 SNES_LS *neP; 540 541 snes->type = SNES_NLS1; 542 snes->Setup = SNESSetUp_LS; 543 snes->Solver = SNESSolve_LS; 544 snes->destroy = SNESDestroy_LS; 545 snes->Monitor = SNESDefaultMonitor; 546 snes->Converged = SNESDefaultConverged; 547 snes->PrintHelp = SNESPrintHelp_LS; 548 snes->SetFromOptions = SNESSetFromOptions_LS; 549 550 neP = NEW(SNES_LS); CHKPTR(neP); 551 snes->data = (void *) neP; 552 neP->alpha = 1.e-4; 553 neP->maxstep = 1.e8; 554 neP->steptol = 1.e-12; 555 neP->LineSearch = SNESCubicLineSearch; 556 return 0; 557 } 558 559 560 561 562