1 #include <petsc/private/snesimpl.h> /*I "petscsnes.h" I*/ 2 #include <petscdm.h> 3 4 #define H(i, j) qn->dXdFmat[i * qn->m + j] 5 6 const char *const SNESQNScaleTypes[] = {"DEFAULT", "NONE", "SCALAR", "DIAGONAL", "JACOBIAN", "SNESQNScaleType", "SNES_QN_SCALING_", NULL}; 7 const char *const SNESQNRestartTypes[] = {"DEFAULT", "NONE", "POWELL", "PERIODIC", "SNESQNRestartType", "SNES_QN_RESTART_", NULL}; 8 const char *const SNESQNTypes[] = {"LBFGS", "BROYDEN", "BADBROYDEN", "SNESQNType", "SNES_QN_", NULL}; 9 10 typedef struct { 11 Mat B; /* Quasi-Newton approximation Matrix (MATLMVM) */ 12 PetscInt m; /* The number of kept previous steps */ 13 PetscReal *lambda; /* The line search history of the method */ 14 PetscBool monflg; 15 PetscViewer monitor; 16 PetscReal powell_gamma; /* Powell angle restart condition */ 17 PetscReal scaling; /* scaling of H0 */ 18 SNESQNType type; /* the type of quasi-newton method used */ 19 SNESQNScaleType scale_type; /* the type of scaling used */ 20 SNESQNRestartType restart_type; /* determine the frequency and type of restart conditions */ 21 } SNES_QN; 22 23 static PetscErrorCode SNESSolve_QN(SNES snes) 24 { 25 SNES_QN *qn = (SNES_QN *)snes->data; 26 Vec X, Xold; 27 Vec F, W; 28 Vec Y, D, Dold; 29 PetscInt i, i_r; 30 PetscReal fnorm, xnorm, ynorm; 31 SNESLineSearchReason lssucceed; 32 PetscBool badstep, powell, periodic; 33 PetscScalar DolddotD, DolddotDold; 34 SNESConvergedReason reason; 35 #if defined(PETSC_USE_INFO) 36 PetscReal gnorm; 37 #endif 38 39 /* basically just a regular newton's method except for the application of the Jacobian */ 40 41 PetscFunctionBegin; 42 PetscCheck(!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); 43 44 PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite)); 45 F = snes->vec_func; /* residual vector */ 46 Y = snes->vec_sol_update; /* search direction generated by J^-1D*/ 47 W = snes->work[3]; 48 X = snes->vec_sol; /* solution vector */ 49 Xold = snes->work[0]; 50 51 /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */ 52 D = snes->work[1]; 53 Dold = snes->work[2]; 54 55 snes->reason = SNES_CONVERGED_ITERATING; 56 57 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 58 snes->iter = 0; 59 snes->norm = 0.; 60 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 61 62 if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_PRECONDITIONED) { 63 PetscCall(SNESApplyNPC(snes, X, NULL, F)); 64 PetscCall(SNESGetConvergedReason(snes->npc, &reason)); 65 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 66 snes->reason = SNES_DIVERGED_INNER; 67 PetscFunctionReturn(PETSC_SUCCESS); 68 } 69 PetscCall(VecNorm(F, NORM_2, &fnorm)); 70 } else { 71 if (!snes->vec_func_init_set) { 72 PetscCall(SNESComputeFunction(snes, X, F)); 73 } else snes->vec_func_init_set = PETSC_FALSE; 74 75 PetscCall(VecNorm(F, NORM_2, &fnorm)); 76 SNESCheckFunctionNorm(snes, fnorm); 77 } 78 if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) { 79 PetscCall(SNESApplyNPC(snes, X, F, D)); 80 PetscCall(SNESGetConvergedReason(snes->npc, &reason)); 81 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 82 snes->reason = SNES_DIVERGED_INNER; 83 PetscFunctionReturn(PETSC_SUCCESS); 84 } 85 } else { 86 PetscCall(VecCopy(F, D)); 87 } 88 89 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 90 snes->norm = fnorm; 91 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 92 PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0)); 93 PetscCall(SNESMonitor(snes, 0, fnorm)); 94 95 /* test convergence */ 96 PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm)); 97 if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS); 98 99 if (snes->npc && snes->npcside == PC_RIGHT) { 100 PetscCall(PetscLogEventBegin(SNES_NPCSolve, snes->npc, X, 0, 0)); 101 PetscCall(SNESSolve(snes->npc, snes->vec_rhs, X)); 102 PetscCall(PetscLogEventEnd(SNES_NPCSolve, snes->npc, X, 0, 0)); 103 PetscCall(SNESGetConvergedReason(snes->npc, &reason)); 104 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 105 snes->reason = SNES_DIVERGED_INNER; 106 PetscFunctionReturn(PETSC_SUCCESS); 107 } 108 PetscCall(SNESGetNPCFunction(snes, F, &fnorm)); 109 PetscCall(VecCopy(F, D)); 110 } 111 112 /* general purpose update */ 113 PetscTryTypeMethod(snes, update, snes->iter); 114 115 /* scale the initial update */ 116 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 117 PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre)); 118 SNESCheckJacobianDomainerror(snes); 119 PetscCall(KSPSetOperators(snes->ksp, snes->jacobian, snes->jacobian_pre)); 120 PetscCall(MatLMVMSetJ0KSP(qn->B, snes->ksp)); 121 } 122 123 for (i = 0, i_r = 0; i < snes->max_its; i++, i_r++) { 124 /* update QN approx and calculate step */ 125 PetscCall(MatLMVMUpdate(qn->B, X, D)); 126 PetscCall(MatSolve(qn->B, D, Y)); 127 128 /* line search for lambda */ 129 ynorm = 1; 130 #if defined(PETSC_USE_INFO) 131 gnorm = fnorm; 132 #endif 133 PetscCall(VecCopy(D, Dold)); 134 PetscCall(VecCopy(X, Xold)); 135 PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y)); 136 if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break; 137 PetscCall(SNESLineSearchGetReason(snes->linesearch, &lssucceed)); 138 PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm)); 139 badstep = PETSC_FALSE; 140 if (lssucceed) { 141 if (++snes->numFailures >= snes->maxFailures) { 142 snes->reason = SNES_DIVERGED_LINE_SEARCH; 143 break; 144 } 145 badstep = PETSC_TRUE; 146 } 147 148 /* convergence monitoring */ 149 PetscCall(PetscInfo(snes, "fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n", (double)fnorm, (double)gnorm, (double)ynorm, (int)lssucceed)); 150 151 if (snes->npc && snes->npcside == PC_RIGHT) { 152 PetscCall(PetscLogEventBegin(SNES_NPCSolve, snes->npc, X, 0, 0)); 153 PetscCall(SNESSolve(snes->npc, snes->vec_rhs, X)); 154 PetscCall(PetscLogEventEnd(SNES_NPCSolve, snes->npc, X, 0, 0)); 155 PetscCall(SNESGetConvergedReason(snes->npc, &reason)); 156 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 157 snes->reason = SNES_DIVERGED_INNER; 158 PetscFunctionReturn(PETSC_SUCCESS); 159 } 160 PetscCall(SNESGetNPCFunction(snes, F, &fnorm)); 161 } 162 163 PetscCall(SNESSetIterationNumber(snes, i + 1)); 164 snes->norm = fnorm; 165 snes->xnorm = xnorm; 166 snes->ynorm = ynorm; 167 168 PetscCall(SNESLogConvergenceHistory(snes, snes->norm, snes->iter)); 169 170 /* set parameter for default relative tolerance convergence test */ 171 PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm)); 172 PetscCall(SNESMonitor(snes, snes->iter, snes->norm)); 173 if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS); 174 if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) { 175 PetscCall(SNESApplyNPC(snes, X, F, D)); 176 PetscCall(SNESGetConvergedReason(snes->npc, &reason)); 177 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 178 snes->reason = SNES_DIVERGED_INNER; 179 PetscFunctionReturn(PETSC_SUCCESS); 180 } 181 } else { 182 PetscCall(VecCopy(F, D)); 183 } 184 185 /* general purpose update */ 186 PetscTryTypeMethod(snes, update, snes->iter); 187 188 /* restart conditions */ 189 powell = PETSC_FALSE; 190 if (qn->restart_type == SNES_QN_RESTART_POWELL && i_r > 1) { 191 /* check restart by Powell's Criterion: |F^T H_0 Fold| > powell_gamma * |Fold^T H_0 Fold| */ 192 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 193 PetscCall(MatMult(snes->jacobian, Dold, W)); 194 } else { 195 PetscCall(VecCopy(Dold, W)); 196 } 197 PetscCall(VecDotBegin(W, Dold, &DolddotDold)); 198 PetscCall(VecDotBegin(W, D, &DolddotD)); 199 PetscCall(VecDotEnd(W, Dold, &DolddotDold)); 200 PetscCall(VecDotEnd(W, D, &DolddotD)); 201 if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma * PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE; 202 } 203 periodic = PETSC_FALSE; 204 if (qn->restart_type == SNES_QN_RESTART_PERIODIC) { 205 if (i_r > qn->m - 1) periodic = PETSC_TRUE; 206 } 207 /* restart if either powell or periodic restart is satisfied. */ 208 if (badstep || powell || periodic) { 209 if (qn->monflg) { 210 PetscCall(PetscViewerASCIIAddTab(qn->monitor, ((PetscObject)snes)->tablevel + 2)); 211 if (powell) { 212 PetscCall(PetscViewerASCIIPrintf(qn->monitor, "Powell restart! |%14.12e| > %6.4f*|%14.12e| i_r = %" PetscInt_FMT "\n", (double)PetscRealPart(DolddotD), (double)qn->powell_gamma, (double)PetscRealPart(DolddotDold), i_r)); 213 } else { 214 PetscCall(PetscViewerASCIIPrintf(qn->monitor, "Periodic restart! i_r = %" PetscInt_FMT "\n", i_r)); 215 } 216 PetscCall(PetscViewerASCIISubtractTab(qn->monitor, ((PetscObject)snes)->tablevel + 2)); 217 } 218 i_r = -1; 219 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) { 220 PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre)); 221 SNESCheckJacobianDomainerror(snes); 222 } 223 PetscCall(MatLMVMReset(qn->B, PETSC_FALSE)); 224 } 225 } 226 PetscFunctionReturn(PETSC_SUCCESS); 227 } 228 229 static PetscErrorCode SNESSetUp_QN(SNES snes) 230 { 231 SNES_QN *qn = (SNES_QN *)snes->data; 232 DM dm; 233 PetscInt n, N; 234 235 PetscFunctionBegin; 236 237 if (!snes->vec_sol) { 238 PetscCall(SNESGetDM(snes, &dm)); 239 PetscCall(DMCreateGlobalVector(dm, &snes->vec_sol)); 240 } 241 PetscCall(SNESSetWorkVecs(snes, 4)); 242 243 if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) PetscCall(SNESSetUpMatrices(snes)); 244 if (snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_UNPRECONDITIONED; 245 246 /* set method defaults */ 247 if (qn->scale_type == SNES_QN_SCALE_DEFAULT) { 248 if (qn->type == SNES_QN_BADBROYDEN) { 249 qn->scale_type = SNES_QN_SCALE_NONE; 250 } else { 251 qn->scale_type = SNES_QN_SCALE_SCALAR; 252 } 253 } 254 if (qn->restart_type == SNES_QN_RESTART_DEFAULT) { 255 if (qn->type == SNES_QN_LBFGS) { 256 qn->restart_type = SNES_QN_RESTART_POWELL; 257 } else { 258 qn->restart_type = SNES_QN_RESTART_PERIODIC; 259 } 260 } 261 262 /* Set up the LMVM matrix */ 263 switch (qn->type) { 264 case SNES_QN_BROYDEN: 265 PetscCall(MatSetType(qn->B, MATLMVMBROYDEN)); 266 qn->scale_type = SNES_QN_SCALE_NONE; 267 break; 268 case SNES_QN_BADBROYDEN: 269 PetscCall(MatSetType(qn->B, MATLMVMBADBROYDEN)); 270 qn->scale_type = SNES_QN_SCALE_NONE; 271 break; 272 default: 273 PetscCall(MatSetType(qn->B, MATLMVMBFGS)); 274 switch (qn->scale_type) { 275 case SNES_QN_SCALE_NONE: 276 PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_NONE)); 277 break; 278 case SNES_QN_SCALE_SCALAR: 279 PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_SCALAR)); 280 break; 281 case SNES_QN_SCALE_JACOBIAN: 282 PetscCall(MatLMVMSymBroydenSetScaleType(qn->B, MAT_LMVM_SYMBROYDEN_SCALE_USER)); 283 break; 284 case SNES_QN_SCALE_DIAGONAL: 285 case SNES_QN_SCALE_DEFAULT: 286 default: 287 break; 288 } 289 break; 290 } 291 PetscCall(VecGetLocalSize(snes->vec_sol, &n)); 292 PetscCall(VecGetSize(snes->vec_sol, &N)); 293 PetscCall(MatSetSizes(qn->B, n, n, N, N)); 294 PetscCall(MatSetUp(qn->B)); 295 PetscCall(MatLMVMReset(qn->B, PETSC_TRUE)); 296 PetscCall(MatLMVMSetHistorySize(qn->B, qn->m)); 297 PetscCall(MatLMVMAllocate(qn->B, snes->vec_sol, snes->vec_func)); 298 PetscFunctionReturn(PETSC_SUCCESS); 299 } 300 301 static PetscErrorCode SNESReset_QN(SNES snes) 302 { 303 SNES_QN *qn; 304 305 PetscFunctionBegin; 306 if (snes->data) { 307 qn = (SNES_QN *)snes->data; 308 PetscCall(MatDestroy(&qn->B)); 309 } 310 PetscFunctionReturn(PETSC_SUCCESS); 311 } 312 313 static PetscErrorCode SNESDestroy_QN(SNES snes) 314 { 315 PetscFunctionBegin; 316 PetscCall(SNESReset_QN(snes)); 317 PetscCall(PetscFree(snes->data)); 318 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetScaleType_C", NULL)); 319 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetRestartType_C", NULL)); 320 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetType_C", NULL)); 321 PetscFunctionReturn(PETSC_SUCCESS); 322 } 323 324 static PetscErrorCode SNESSetFromOptions_QN(SNES snes, PetscOptionItems *PetscOptionsObject) 325 { 326 SNES_QN *qn = (SNES_QN *)snes->data; 327 PetscBool flg; 328 SNESLineSearch linesearch; 329 SNESQNRestartType rtype = qn->restart_type; 330 SNESQNScaleType stype = qn->scale_type; 331 SNESQNType qtype = qn->type; 332 333 PetscFunctionBegin; 334 PetscOptionsHeadBegin(PetscOptionsObject, "SNES QN options"); 335 PetscCall(PetscOptionsInt("-snes_qn_m", "Number of past states saved for L-BFGS methods", "SNESQN", qn->m, &qn->m, NULL)); 336 PetscCall(PetscOptionsReal("-snes_qn_powell_gamma", "Powell angle tolerance", "SNESQN", qn->powell_gamma, &qn->powell_gamma, NULL)); 337 PetscCall(PetscOptionsBool("-snes_qn_monitor", "Monitor for the QN methods", "SNESQN", qn->monflg, &qn->monflg, NULL)); 338 PetscCall(PetscOptionsEnum("-snes_qn_scale_type", "Scaling type", "SNESQNSetScaleType", SNESQNScaleTypes, (PetscEnum)stype, (PetscEnum *)&stype, &flg)); 339 if (flg) PetscCall(SNESQNSetScaleType(snes, stype)); 340 341 PetscCall(PetscOptionsEnum("-snes_qn_restart_type", "Restart type", "SNESQNSetRestartType", SNESQNRestartTypes, (PetscEnum)rtype, (PetscEnum *)&rtype, &flg)); 342 if (flg) PetscCall(SNESQNSetRestartType(snes, rtype)); 343 344 PetscCall(PetscOptionsEnum("-snes_qn_type", "Quasi-Newton update type", "", SNESQNTypes, (PetscEnum)qtype, (PetscEnum *)&qtype, &flg)); 345 if (flg) PetscCall(SNESQNSetType(snes, qtype)); 346 PetscCall(MatSetFromOptions(qn->B)); 347 PetscOptionsHeadEnd(); 348 if (!snes->linesearch) { 349 PetscCall(SNESGetLineSearch(snes, &linesearch)); 350 if (!((PetscObject)linesearch)->type_name) { 351 if (qn->type == SNES_QN_LBFGS) { 352 PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHCP)); 353 } else if (qn->type == SNES_QN_BROYDEN) { 354 PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBASIC)); 355 } else { 356 PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHL2)); 357 } 358 } 359 } 360 if (qn->monflg) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)snes), &qn->monitor)); 361 PetscFunctionReturn(PETSC_SUCCESS); 362 } 363 364 static PetscErrorCode SNESView_QN(SNES snes, PetscViewer viewer) 365 { 366 SNES_QN *qn = (SNES_QN *)snes->data; 367 PetscBool iascii; 368 369 PetscFunctionBegin; 370 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 371 if (iascii) { 372 PetscCall(PetscViewerASCIIPrintf(viewer, " type is %s, restart type is %s, scale type is %s\n", SNESQNTypes[qn->type], SNESQNRestartTypes[qn->restart_type], SNESQNScaleTypes[qn->scale_type])); 373 PetscCall(PetscViewerASCIIPrintf(viewer, " Stored subspace size: %" PetscInt_FMT "\n", qn->m)); 374 } 375 PetscFunctionReturn(PETSC_SUCCESS); 376 } 377 378 /*@ 379 SNESQNSetRestartType - Sets the restart type for `SNESQN`. 380 381 Logically Collective 382 383 Input Parameters: 384 + snes - the iterative context 385 - rtype - restart type 386 387 Options Database Keys: 388 + -snes_qn_restart_type <powell,periodic,none> - set the restart type 389 - -snes_qn_m <m> - sets the number of stored updates and the restart period for periodic 390 391 Level: intermediate 392 393 `SNESQNRestartType`s: 394 + `SNES_QN_RESTART_NONE` - never restart 395 . `SNES_QN_RESTART_POWELL` - restart based upon descent criteria 396 - `SNES_QN_RESTART_PERIODIC` - restart after a fixed number of iterations 397 398 .seealso: `SNESQN`, `SNESQNRestartType`, `SNES_QN_RESTART_NONE`, `SNES_QN_RESTART_POWELL`, `SNES_QN_RESTART_PERIODIC` 399 @*/ 400 PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype) 401 { 402 PetscFunctionBegin; 403 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 404 PetscTryMethod(snes, "SNESQNSetRestartType_C", (SNES, SNESQNRestartType), (snes, rtype)); 405 PetscFunctionReturn(PETSC_SUCCESS); 406 } 407 408 /*@ 409 SNESQNSetScaleType - Sets the scaling type for the inner inverse Jacobian in `SNESQN`. 410 411 Logically Collective 412 413 Input Parameters: 414 + snes - the nonlinear solver context 415 - stype - scale type 416 417 Options Database Key: 418 . -snes_qn_scale_type <diagonal,none,scalar,jacobian> - Scaling type 419 420 Level: intermediate 421 422 `SNESQNScaleType`s: 423 + `SNES_QN_SCALE_NONE` - don't scale the problem 424 . `SNES_QN_SCALE_SCALAR` - use Shanno scaling 425 . `SNES_QN_SCALE_DIAGONAL` - scale with a diagonalized BFGS formula (see Gilbert and Lemarechal 1989), available 426 - `SNES_QN_SCALE_JACOBIAN` - scale by solving a linear system coming from the Jacobian you provided with SNESSetJacobian() computed at the first iteration 427 of QN and at ever restart. 428 429 .seealso: `SNES`, `SNESQN`, `SNESLineSearch`, `SNESQNScaleType`, `SNESSetJacobian()` 430 @*/ 431 432 PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype) 433 { 434 PetscFunctionBegin; 435 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 436 PetscTryMethod(snes, "SNESQNSetScaleType_C", (SNES, SNESQNScaleType), (snes, stype)); 437 PetscFunctionReturn(PETSC_SUCCESS); 438 } 439 440 PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype) 441 { 442 SNES_QN *qn = (SNES_QN *)snes->data; 443 444 PetscFunctionBegin; 445 qn->scale_type = stype; 446 if (stype == SNES_QN_SCALE_JACOBIAN) snes->usesksp = PETSC_TRUE; 447 PetscFunctionReturn(PETSC_SUCCESS); 448 } 449 450 PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype) 451 { 452 SNES_QN *qn = (SNES_QN *)snes->data; 453 454 PetscFunctionBegin; 455 qn->restart_type = rtype; 456 PetscFunctionReturn(PETSC_SUCCESS); 457 } 458 459 /*@ 460 SNESQNSetType - Sets the quasi-Newton variant to be used in `SNESQN`. 461 462 Logically Collective 463 464 Input Parameters: 465 + snes - the iterative context 466 - qtype - variant type 467 468 Options Database Key: 469 . -snes_qn_type <lbfgs,broyden,badbroyden> - quasi-Newton type 470 471 Level: beginner 472 473 `SNESQNType`s: 474 + `SNES_QN_LBFGS` - LBFGS variant 475 . `SNES_QN_BROYDEN` - Broyden variant 476 - `SNES_QN_BADBROYDEN` - Bad Broyden variant 477 478 .seealso: `SNESQN`, `SNES_QN_LBFGS`, `SNES_QN_BROYDEN`, `SNES_QN_BADBROYDEN`, `SNESQNType`, `TAOLMVM`, `TAOBLMVM` 479 @*/ 480 481 PetscErrorCode SNESQNSetType(SNES snes, SNESQNType qtype) 482 { 483 PetscFunctionBegin; 484 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 485 PetscTryMethod(snes, "SNESQNSetType_C", (SNES, SNESQNType), (snes, qtype)); 486 PetscFunctionReturn(PETSC_SUCCESS); 487 } 488 489 PetscErrorCode SNESQNSetType_QN(SNES snes, SNESQNType qtype) 490 { 491 SNES_QN *qn = (SNES_QN *)snes->data; 492 493 PetscFunctionBegin; 494 qn->type = qtype; 495 PetscFunctionReturn(PETSC_SUCCESS); 496 } 497 498 /*MC 499 SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems. 500 501 Options Database Keys: 502 + -snes_qn_m <m> - Number of past states saved for the L-Broyden methods. 503 . -snes_qn_restart_type <powell,periodic,none> - set the restart type 504 . -snes_qn_powell_gamma - Angle condition for restart. 505 . -snes_qn_powell_descent - Descent condition for restart. 506 . -snes_qn_type <lbfgs,broyden,badbroyden> - QN type 507 . -snes_qn_scale_type <diagonal,none,scalar,jacobian> - scaling performed on inner Jacobian 508 . -snes_linesearch_type <cp, l2, basic> - Type of line search. 509 - -snes_qn_monitor - Monitors the quasi-newton Jacobian. 510 511 References: 512 + * - Kelley, C.T., Iterative Methods for Linear and Nonlinear Equations, Chapter 8, SIAM, 1995. 513 . * - R. Byrd, J. Nocedal, R. Schnabel, Representations of Quasi Newton Matrices and their use in Limited Memory Methods, 514 Technical Report, Northwestern University, June 1992. 515 . * - Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker, Experiments with Quasi-Newton Methods in Solving Stiff ODE 516 Systems, SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985. 517 . * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers", 518 SIAM Review, 57(4), 2015 519 . * - Griewank, Andreas. "Broyden updating, the good and the bad!." Doc. Math (2012): 301-315. 520 . * - Gilbert, Jean Charles, and Claude Lemar{\'e}chal. "Some numerical experiments with variable-storage quasi-Newton algorithms." 521 Mathematical programming 45.1-3 (1989): 407-435. 522 - * - Dener A., Munson T. "Accelerating Limited-Memory Quasi-Newton Convergence for Large-Scale Optimization" 523 Computational Science - ICCS 2019. ICCS 2019. Lecture Notes in Computer Science, vol 11538. Springer, Cham 524 525 Level: beginner 526 527 Notes: 528 This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using 529 previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one 530 updates. 531 532 When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied. The first of 533 these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this 534 iteration as the current iteration's values when constructing the approximate Jacobian. The second, composed, 535 perturbs the problem the Jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner. 536 537 Uses left nonlinear preconditioning by default. 538 539 .seealso: `SNESQNRestartType`, `SNESQNSetRestartType()`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESNEWTONTR`, 540 `SNESQNScaleType`, `SNESQNSetScaleType()`, `SNESQNSetType`, `SNESQNSetType()` 541 M*/ 542 PETSC_EXTERN PetscErrorCode SNESCreate_QN(SNES snes) 543 { 544 SNES_QN *qn; 545 const char *optionsprefix; 546 547 PetscFunctionBegin; 548 snes->ops->setup = SNESSetUp_QN; 549 snes->ops->solve = SNESSolve_QN; 550 snes->ops->destroy = SNESDestroy_QN; 551 snes->ops->setfromoptions = SNESSetFromOptions_QN; 552 snes->ops->view = SNESView_QN; 553 snes->ops->reset = SNESReset_QN; 554 555 snes->npcside = PC_LEFT; 556 557 snes->usesnpc = PETSC_TRUE; 558 snes->usesksp = PETSC_FALSE; 559 560 snes->alwayscomputesfinalresidual = PETSC_TRUE; 561 562 if (!snes->tolerancesset) { 563 snes->max_funcs = 30000; 564 snes->max_its = 10000; 565 } 566 567 PetscCall(PetscNew(&qn)); 568 snes->data = (void *)qn; 569 qn->m = 10; 570 qn->scaling = 1.0; 571 qn->monitor = NULL; 572 qn->monflg = PETSC_FALSE; 573 qn->powell_gamma = 0.9999; 574 qn->scale_type = SNES_QN_SCALE_DEFAULT; 575 qn->restart_type = SNES_QN_RESTART_DEFAULT; 576 qn->type = SNES_QN_LBFGS; 577 578 PetscCall(MatCreate(PetscObjectComm((PetscObject)snes), &qn->B)); 579 PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix)); 580 PetscCall(MatSetOptionsPrefix(qn->B, optionsprefix)); 581 582 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetScaleType_C", SNESQNSetScaleType_QN)); 583 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetRestartType_C", SNESQNSetRestartType_QN)); 584 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESQNSetType_C", SNESQNSetType_QN)); 585 PetscFunctionReturn(PETSC_SUCCESS); 586 } 587