1 #include <../src/snes/impls/ncg/snesncgimpl.h> /*I "petscsnes.h" I*/ 2 const char *const SNESNCGTypes[] = {"FR","PRP","HS","DY","CD","SNESNCGType","SNES_NCG_",NULL}; 3 4 static PetscErrorCode SNESReset_NCG(SNES snes) 5 { 6 PetscFunctionBegin; 7 PetscFunctionReturn(0); 8 } 9 10 /* 11 SNESDestroy_NCG - Destroys the private SNES_NCG context that was created with SNESCreate_NCG(). 12 13 Input Parameter: 14 . snes - the SNES context 15 16 Application Interface Routine: SNESDestroy() 17 */ 18 static PetscErrorCode SNESDestroy_NCG(SNES snes) 19 { 20 PetscFunctionBegin; 21 CHKERRQ(PetscFree(snes->data)); 22 PetscFunctionReturn(0); 23 } 24 25 /* 26 SNESSetUp_NCG - Sets up the internal data structures for the later use 27 of the SNESNCG nonlinear solver. 28 29 Input Parameters: 30 + snes - the SNES context 31 - x - the solution vector 32 33 Application Interface Routine: SNESSetUp() 34 */ 35 36 static PetscErrorCode SNESSetUp_NCG(SNES snes) 37 { 38 PetscFunctionBegin; 39 CHKERRQ(SNESSetWorkVecs(snes,2)); 40 PetscCheckFalse(snes->npcside== PC_RIGHT,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE, "SNESNCG only supports left preconditioning"); 41 if (snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_UNPRECONDITIONED; 42 PetscFunctionReturn(0); 43 } 44 45 static PetscErrorCode SNESLineSearchApply_NCGLinear(SNESLineSearch linesearch) 46 { 47 PetscScalar alpha, ptAp; 48 Vec X, Y, F, W; 49 SNES snes; 50 PetscReal *fnorm, *xnorm, *ynorm; 51 52 PetscFunctionBegin; 53 CHKERRQ(SNESLineSearchGetSNES(linesearch, &snes)); 54 X = linesearch->vec_sol; 55 W = linesearch->vec_sol_new; 56 F = linesearch->vec_func; 57 Y = linesearch->vec_update; 58 fnorm = &linesearch->fnorm; 59 xnorm = &linesearch->xnorm; 60 ynorm = &linesearch->ynorm; 61 62 if (!snes->jacobian) { 63 CHKERRQ(SNESSetUpMatrices(snes)); 64 } 65 66 /* 67 68 The exact step size for unpreconditioned linear CG is just: 69 alpha = (r, r) / (p, Ap) = (f, f) / (y, Jy) 70 */ 71 CHKERRQ(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre)); 72 CHKERRQ(VecDot(F, F, &alpha)); 73 CHKERRQ(MatMult(snes->jacobian, Y, W)); 74 CHKERRQ(VecDot(Y, W, &ptAp)); 75 alpha = alpha / ptAp; 76 CHKERRQ(VecAXPY(X,-alpha,Y)); 77 CHKERRQ(SNESComputeFunction(snes,X,F)); 78 79 CHKERRQ(VecNorm(F,NORM_2,fnorm)); 80 CHKERRQ(VecNorm(X,NORM_2,xnorm)); 81 CHKERRQ(VecNorm(Y,NORM_2,ynorm)); 82 PetscFunctionReturn(0); 83 } 84 85 /*MC 86 SNESLINESEARCHNCGLINEAR - Special line search only for SNESNCG 87 88 This line search uses the length "as if" the problem is linear (that is what is computed by the linear CG method) using the Jacobian of the function. 89 alpha = (r, r) / (p, Ap) = (f, f) / (y, Jy) where r (f) is the current residual (function value), p (y) is the current search direction. 90 91 Notes: This requires a Jacobian-vector product but does not require the solution of a linear system with the Jacobian 92 93 This is a "odd-ball" line search, we don't know if it is in the literature or used in practice by anyone. 94 95 Level: advanced 96 97 .seealso: SNESLineSearchCreate(), SNESLineSearchSetType() 98 M*/ 99 100 PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_NCGLinear(SNESLineSearch linesearch) 101 { 102 PetscFunctionBegin; 103 linesearch->ops->apply = SNESLineSearchApply_NCGLinear; 104 linesearch->ops->destroy = NULL; 105 linesearch->ops->setfromoptions = NULL; 106 linesearch->ops->reset = NULL; 107 linesearch->ops->view = NULL; 108 linesearch->ops->setup = NULL; 109 PetscFunctionReturn(0); 110 } 111 112 /* 113 SNESSetFromOptions_NCG - Sets various parameters for the SNESNCG method. 114 115 Input Parameter: 116 . snes - the SNES context 117 118 Application Interface Routine: SNESSetFromOptions() 119 */ 120 static PetscErrorCode SNESSetFromOptions_NCG(PetscOptionItems *PetscOptionsObject,SNES snes) 121 { 122 SNES_NCG *ncg = (SNES_NCG*)snes->data; 123 PetscBool debug = PETSC_FALSE; 124 SNESNCGType ncgtype=ncg->type; 125 SNESLineSearch linesearch; 126 127 PetscFunctionBegin; 128 CHKERRQ(PetscOptionsHead(PetscOptionsObject,"SNES NCG options")); 129 CHKERRQ(PetscOptionsBool("-snes_ncg_monitor","Monitor the beta values used in the NCG iterations","SNES",ncg->monitor ? PETSC_TRUE : PETSC_FALSE, &debug, NULL)); 130 if (debug) { 131 ncg->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes)); 132 } 133 CHKERRQ(PetscOptionsEnum("-snes_ncg_type","NCG Beta type used","SNESNCGSetType",SNESNCGTypes,(PetscEnum)ncg->type,(PetscEnum*)&ncgtype,NULL)); 134 CHKERRQ(SNESNCGSetType(snes, ncgtype)); 135 CHKERRQ(PetscOptionsTail()); 136 if (!snes->linesearch) { 137 CHKERRQ(SNESGetLineSearch(snes, &linesearch)); 138 if (!((PetscObject)linesearch)->type_name) { 139 if (!snes->npc) { 140 CHKERRQ(SNESLineSearchSetType(linesearch, SNESLINESEARCHCP)); 141 } else { 142 CHKERRQ(SNESLineSearchSetType(linesearch, SNESLINESEARCHL2)); 143 } 144 } 145 } 146 PetscFunctionReturn(0); 147 } 148 149 /* 150 SNESView_NCG - Prints info from the SNESNCG data structure. 151 152 Input Parameters: 153 + SNES - the SNES context 154 - viewer - visualization context 155 156 Application Interface Routine: SNESView() 157 */ 158 static PetscErrorCode SNESView_NCG(SNES snes, PetscViewer viewer) 159 { 160 SNES_NCG *ncg = (SNES_NCG *) snes->data; 161 PetscBool iascii; 162 163 PetscFunctionBegin; 164 CHKERRQ(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii)); 165 if (iascii) { 166 CHKERRQ(PetscViewerASCIIPrintf(viewer, " type: %s\n", SNESNCGTypes[ncg->type])); 167 } 168 PetscFunctionReturn(0); 169 } 170 171 /* 172 173 Assuming F = SNESComputeFunction(X) compute Y^tJ^tF using a simple secant approximation of the jacobian. 174 175 This routine is not currently used. I don't know what its intended purpose is. 176 177 Note it has a hardwired differencing parameter of 1e-5 178 179 */ 180 PetscErrorCode SNESNCGComputeYtJtF_Private(SNES snes, Vec X, Vec F, Vec Y, Vec W, Vec G, PetscScalar * ytJtf) 181 { 182 PetscScalar ftf, ftg, fty, h; 183 184 PetscFunctionBegin; 185 CHKERRQ(VecDot(F, F, &ftf)); 186 CHKERRQ(VecDot(F, Y, &fty)); 187 h = 1e-5*fty / fty; 188 CHKERRQ(VecCopy(X, W)); 189 CHKERRQ(VecAXPY(W, -h, Y)); /* this is arbitrary */ 190 CHKERRQ(SNESComputeFunction(snes, W, G)); 191 CHKERRQ(VecDot(G, F, &ftg)); 192 *ytJtf = (ftg - ftf) / h; 193 PetscFunctionReturn(0); 194 } 195 196 /*@ 197 SNESNCGSetType - Sets the conjugate update type for SNESNCG. 198 199 Logically Collective on SNES 200 201 Input Parameters: 202 + snes - the iterative context 203 - btype - update type 204 205 Options Database: 206 . -snes_ncg_type <prp,fr,hs,dy,cd> -strategy for selecting algorithm for computing beta 207 208 Level: intermediate 209 210 SNESNCGSelectTypes: 211 + SNES_NCG_FR - Fletcher-Reeves update 212 . SNES_NCG_PRP - Polak-Ribiere-Polyak update 213 . SNES_NCG_HS - Hestenes-Steifel update 214 . SNES_NCG_DY - Dai-Yuan update 215 - SNES_NCG_CD - Conjugate Descent update 216 217 Notes: 218 SNES_NCG_PRP is the default, and the only one that tolerates generalized search directions. 219 220 It is not clear what "generalized search directions" means, does it mean use with a nonlinear preconditioner, 221 that is using -npc_snes_type <type>, SNESSetNPC(), or SNESGetNPC()? 222 223 @*/ 224 PetscErrorCode SNESNCGSetType(SNES snes, SNESNCGType btype) 225 { 226 PetscFunctionBegin; 227 PetscValidHeaderSpecific(snes,SNES_CLASSID,1); 228 CHKERRQ(PetscTryMethod(snes,"SNESNCGSetType_C",(SNES,SNESNCGType),(snes,btype))); 229 PetscFunctionReturn(0); 230 } 231 232 static PetscErrorCode SNESNCGSetType_NCG(SNES snes, SNESNCGType btype) 233 { 234 SNES_NCG *ncg = (SNES_NCG*)snes->data; 235 236 PetscFunctionBegin; 237 ncg->type = btype; 238 PetscFunctionReturn(0); 239 } 240 241 /* 242 SNESSolve_NCG - Solves a nonlinear system with the Nonlinear Conjugate Gradient method. 243 244 Input Parameters: 245 . snes - the SNES context 246 247 Output Parameter: 248 . outits - number of iterations until termination 249 250 Application Interface Routine: SNESSolve() 251 */ 252 static PetscErrorCode SNESSolve_NCG(SNES snes) 253 { 254 SNES_NCG *ncg = (SNES_NCG*)snes->data; 255 Vec X,dX,lX,F,dXold; 256 PetscReal fnorm, ynorm, xnorm, beta = 0.0; 257 PetscScalar dXdotdX, dXolddotdXold, dXdotdXold, lXdotdX, lXdotdXold; 258 PetscInt maxits, i; 259 SNESLineSearchReason lsresult = SNES_LINESEARCH_SUCCEEDED; 260 SNESLineSearch linesearch; 261 SNESConvergedReason reason; 262 263 PetscFunctionBegin; 264 PetscCheckFalse(snes->xl || snes->xu || snes->ops->computevariablebounds,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE, "SNES solver %s does not support bounds", ((PetscObject)snes)->type_name); 265 266 CHKERRQ(PetscCitationsRegister(SNESCitation,&SNEScite)); 267 snes->reason = SNES_CONVERGED_ITERATING; 268 269 maxits = snes->max_its; /* maximum number of iterations */ 270 X = snes->vec_sol; /* X^n */ 271 dXold = snes->work[0]; /* The previous iterate of X */ 272 dX = snes->work[1]; /* the preconditioned direction */ 273 lX = snes->vec_sol_update; /* the conjugate direction */ 274 F = snes->vec_func; /* residual vector */ 275 276 CHKERRQ(SNESGetLineSearch(snes, &linesearch)); 277 278 CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes)); 279 snes->iter = 0; 280 snes->norm = 0.; 281 CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes)); 282 283 /* compute the initial function and preconditioned update dX */ 284 285 if (snes->npc && snes->functype == SNES_FUNCTION_PRECONDITIONED) { 286 CHKERRQ(SNESApplyNPC(snes,X,NULL,dX)); 287 CHKERRQ(SNESGetConvergedReason(snes->npc,&reason)); 288 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 289 snes->reason = SNES_DIVERGED_INNER; 290 PetscFunctionReturn(0); 291 } 292 CHKERRQ(VecCopy(dX,F)); 293 CHKERRQ(VecNorm(F,NORM_2,&fnorm)); 294 } else { 295 if (!snes->vec_func_init_set) { 296 CHKERRQ(SNESComputeFunction(snes,X,F)); 297 } else snes->vec_func_init_set = PETSC_FALSE; 298 299 /* convergence test */ 300 CHKERRQ(VecNorm(F,NORM_2,&fnorm)); 301 SNESCheckFunctionNorm(snes,fnorm); 302 CHKERRQ(VecCopy(F,dX)); 303 } 304 if (snes->npc) { 305 if (snes->functype == SNES_FUNCTION_UNPRECONDITIONED) { 306 CHKERRQ(SNESApplyNPC(snes,X,F,dX)); 307 CHKERRQ(SNESGetConvergedReason(snes->npc,&reason)); 308 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 309 snes->reason = SNES_DIVERGED_INNER; 310 PetscFunctionReturn(0); 311 } 312 } 313 } 314 CHKERRQ(VecCopy(dX,lX)); 315 CHKERRQ(VecDot(dX, dX, &dXdotdX)); 316 317 CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes)); 318 snes->norm = fnorm; 319 CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes)); 320 CHKERRQ(SNESLogConvergenceHistory(snes,fnorm,0)); 321 CHKERRQ(SNESMonitor(snes,0,fnorm)); 322 323 /* test convergence */ 324 CHKERRQ((*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP)); 325 if (snes->reason) PetscFunctionReturn(0); 326 327 /* Call general purpose update function */ 328 if (snes->ops->update) { 329 CHKERRQ((*snes->ops->update)(snes, snes->iter)); 330 } 331 332 /* first update -- just use the (preconditioned) residual direction for the initial conjugate direction */ 333 334 for (i = 1; i < maxits + 1; i++) { 335 /* some update types require the old update direction or conjugate direction */ 336 if (ncg->type != SNES_NCG_FR) { 337 CHKERRQ(VecCopy(dX, dXold)); 338 } 339 CHKERRQ(SNESLineSearchApply(linesearch,X,F,&fnorm,lX)); 340 CHKERRQ(SNESLineSearchGetReason(linesearch, &lsresult)); 341 CHKERRQ(SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm)); 342 if (lsresult) { 343 if (++snes->numFailures >= snes->maxFailures) { 344 snes->reason = SNES_DIVERGED_LINE_SEARCH; 345 PetscFunctionReturn(0); 346 } 347 } 348 if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) { 349 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 350 PetscFunctionReturn(0); 351 } 352 /* Monitor convergence */ 353 CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes)); 354 snes->iter = i; 355 snes->norm = fnorm; 356 snes->xnorm = xnorm; 357 snes->ynorm = ynorm; 358 CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes)); 359 CHKERRQ(SNESLogConvergenceHistory(snes,snes->norm,0)); 360 CHKERRQ(SNESMonitor(snes,snes->iter,snes->norm)); 361 362 /* Test for convergence */ 363 CHKERRQ((*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP)); 364 if (snes->reason) PetscFunctionReturn(0); 365 366 /* Call general purpose update function */ 367 if (snes->ops->update) { 368 CHKERRQ((*snes->ops->update)(snes, snes->iter)); 369 } 370 if (snes->npc) { 371 if (snes->functype == SNES_FUNCTION_PRECONDITIONED) { 372 CHKERRQ(SNESApplyNPC(snes,X,NULL,dX)); 373 CHKERRQ(SNESGetConvergedReason(snes->npc,&reason)); 374 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 375 snes->reason = SNES_DIVERGED_INNER; 376 PetscFunctionReturn(0); 377 } 378 CHKERRQ(VecCopy(dX,F)); 379 } else { 380 CHKERRQ(SNESApplyNPC(snes,X,F,dX)); 381 CHKERRQ(SNESGetConvergedReason(snes->npc,&reason)); 382 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 383 snes->reason = SNES_DIVERGED_INNER; 384 PetscFunctionReturn(0); 385 } 386 } 387 } else { 388 CHKERRQ(VecCopy(F,dX)); 389 } 390 391 /* compute the conjugate direction lX = dX + beta*lX with beta = ((dX, dX) / (dX_old, dX_old) (Fletcher-Reeves update)*/ 392 switch (ncg->type) { 393 case SNES_NCG_FR: /* Fletcher-Reeves */ 394 dXolddotdXold= dXdotdX; 395 CHKERRQ(VecDot(dX, dX, &dXdotdX)); 396 beta = PetscRealPart(dXdotdX / dXolddotdXold); 397 break; 398 case SNES_NCG_PRP: /* Polak-Ribiere-Poylak */ 399 dXolddotdXold= dXdotdX; 400 CHKERRQ(VecDotBegin(dX, dX, &dXdotdXold)); 401 CHKERRQ(VecDotBegin(dXold, dX, &dXdotdXold)); 402 CHKERRQ(VecDotEnd(dX, dX, &dXdotdX)); 403 CHKERRQ(VecDotEnd(dXold, dX, &dXdotdXold)); 404 beta = PetscRealPart(((dXdotdX - dXdotdXold) / dXolddotdXold)); 405 if (beta < 0.0) beta = 0.0; /* restart */ 406 break; 407 case SNES_NCG_HS: /* Hestenes-Stiefel */ 408 CHKERRQ(VecDotBegin(dX, dX, &dXdotdX)); 409 CHKERRQ(VecDotBegin(dX, dXold, &dXdotdXold)); 410 CHKERRQ(VecDotBegin(lX, dX, &lXdotdX)); 411 CHKERRQ(VecDotBegin(lX, dXold, &lXdotdXold)); 412 CHKERRQ(VecDotEnd(dX, dX, &dXdotdX)); 413 CHKERRQ(VecDotEnd(dX, dXold, &dXdotdXold)); 414 CHKERRQ(VecDotEnd(lX, dX, &lXdotdX)); 415 CHKERRQ(VecDotEnd(lX, dXold, &lXdotdXold)); 416 beta = PetscRealPart((dXdotdX - dXdotdXold) / (lXdotdX - lXdotdXold)); 417 break; 418 case SNES_NCG_DY: /* Dai-Yuan */ 419 CHKERRQ(VecDotBegin(dX, dX, &dXdotdX)); 420 CHKERRQ(VecDotBegin(lX, dX, &lXdotdX)); 421 CHKERRQ(VecDotBegin(lX, dXold, &lXdotdXold)); 422 CHKERRQ(VecDotEnd(dX, dX, &dXdotdX)); 423 CHKERRQ(VecDotEnd(lX, dX, &lXdotdX)); 424 CHKERRQ(VecDotEnd(lX, dXold, &lXdotdXold)); 425 beta = PetscRealPart(dXdotdX / (lXdotdXold - lXdotdX)); 426 break; 427 case SNES_NCG_CD: /* Conjugate Descent */ 428 CHKERRQ(VecDotBegin(dX, dX, &dXdotdX)); 429 CHKERRQ(VecDotBegin(lX, dXold, &lXdotdXold)); 430 CHKERRQ(VecDotEnd(dX, dX, &dXdotdX)); 431 CHKERRQ(VecDotEnd(lX, dXold, &lXdotdXold)); 432 beta = PetscRealPart(dXdotdX / lXdotdXold); 433 break; 434 } 435 if (ncg->monitor) { 436 CHKERRQ(PetscViewerASCIIPrintf(ncg->monitor, "beta = %e\n", (double)beta)); 437 } 438 CHKERRQ(VecAYPX(lX, beta, dX)); 439 } 440 CHKERRQ(PetscInfo(snes, "Maximum number of iterations has been reached: %D\n", maxits)); 441 if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT; 442 PetscFunctionReturn(0); 443 } 444 445 /*MC 446 SNESNCG - Nonlinear Conjugate-Gradient method for the solution of general nonlinear systems. 447 448 Level: beginner 449 450 Options Database: 451 + -snes_ncg_type <fr, prp, dy, hs, cd> - Choice of conjugate-gradient update parameter, default is prp. 452 . -snes_linesearch_type <cp,l2,basic> - Line search type. 453 - -snes_ncg_monitor - Print the beta values used in the ncg iteration, . 454 455 Notes: 456 This solves the nonlinear system of equations F(x) = 0 using the nonlinear generalization of the conjugate 457 gradient method. This may be used with a nonlinear preconditioner used to pick the new search directions, but otherwise 458 chooses the initial search direction as F(x) for the initial guess x. 459 460 Only supports left non-linear preconditioning. 461 462 Default line search is SNESLINESEARCHCP, unless a nonlinear preconditioner is used with -npc_snes_type <type>, SNESSetNPC(), or SNESGetNPC() then 463 SNESLINESEARCHL2 is used. Also supports the special purpose line search SNESLINESEARCHNCGLINEAR 464 465 References: 466 . * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu,"Composing Scalable Nonlinear Algebraic Solvers", 467 SIAM Review, 57(4), 2015 468 469 .seealso: SNESCreate(), SNES, SNESSetType(), SNESNEWTONLS, SNESNEWTONTR, SNESNGMRES, SNESQN, SNESLINESEARCHNCGLINEAR, SNESNCGSetType(), SNESNCGGetType(), SNESLineSearchSetType() 470 M*/ 471 PETSC_EXTERN PetscErrorCode SNESCreate_NCG(SNES snes) 472 { 473 SNES_NCG * neP; 474 475 PetscFunctionBegin; 476 snes->ops->destroy = SNESDestroy_NCG; 477 snes->ops->setup = SNESSetUp_NCG; 478 snes->ops->setfromoptions = SNESSetFromOptions_NCG; 479 snes->ops->view = SNESView_NCG; 480 snes->ops->solve = SNESSolve_NCG; 481 snes->ops->reset = SNESReset_NCG; 482 483 snes->usesksp = PETSC_FALSE; 484 snes->usesnpc = PETSC_TRUE; 485 snes->npcside = PC_LEFT; 486 487 snes->alwayscomputesfinalresidual = PETSC_TRUE; 488 489 if (!snes->tolerancesset) { 490 snes->max_funcs = 30000; 491 snes->max_its = 10000; 492 snes->stol = 1e-20; 493 } 494 495 CHKERRQ(PetscNewLog(snes,&neP)); 496 snes->data = (void*) neP; 497 neP->monitor = NULL; 498 neP->type = SNES_NCG_PRP; 499 CHKERRQ(PetscObjectComposeFunction((PetscObject)snes,"SNESNCGSetType_C", SNESNCGSetType_NCG)); 500 PetscFunctionReturn(0); 501 } 502